Home Blog Certs Knowledge Base About

LPIC-1 105.1 โ€” Shell Variables and Environment (Part 2)

Exam weight: 4 โ€” LPIC-1 v5, Exam 102 | Part 2 of 105.1

Shell Types โ€” Quick Reference

Bash reads different initialization files depending on two attributes: interactive vs non-interactive, and login vs non-login.

Check interactivity via $- โ€” if the output contains i, the shell is interactive:

$ echo $-
himBHs

Check login status:

$ echo $0          # -bash means login shell
$ shopt login_shell  # on = login, off = non-login
Launch methodLoginInteractive
ssh user@hostyesyes
Ctrl+Alt+F2 (tty login)yesyes
su - / sudo -iyesyes
gnome-terminal, sakuranoyes
bash inside a shellnoyes
./script.shnono
bash -c 'cmd'nono

Initialization Files

Global Files (All Users)

/etc/profile โ€” read by any login shell. Usually sources scripts from /etc/profile.d/*.sh.

/etc/bash.bashrc โ€” Debian/Ubuntu. Read by interactive shells.

/etc/bashrc โ€” RHEL/CentOS equivalent of /etc/bash.bashrc.

User Files

~/.bash_profile โ€” personal login file, highest priority.

~/.bash_login โ€” read only if ~/.bash_profile is absent.

~/.profile โ€” fallback, compatible with other POSIX shells. Read if neither of the above exists.

~/.bashrc โ€” for interactive non-login shells. Often sourced from ~/.bash_profile:

if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

~/.bash_logout โ€” executed when a login shell exits.

Reading Order

Shell typeFiles read
Login + interactive/etc/profile โ†’ /etc/profile.d/*.sh โ†’ first of ~/.bash_profile, ~/.bash_login, ~/.profile (which sources ~/.bashrc)
Non-login + interactive/etc/bash.bashrc (if present), ~/.bashrc
Non-login + non-interactive (script)Only the file pointed to by BASH_ENV, if set
On exit from login shell~/.bash_logout

source and the Dot Command

Apply changes to ~/.bashrc without restarting the shell:

source ~/.bashrc
. ~/.bashrc          # equivalent

Both execute the file’s commands in the current shell. Running bash file spawns a child โ€” variables set there disappear on exit.

To fully reload the current shell process: exec bash or exec $SHELL.


Shell Variables

Assignment and Reference

$ distro=zorinos     # assignment โ€” no spaces around =
$ echo $distro       # reference โ€” dollar sign required
zorinos

Spaces around = break the command:

$ distro =zorinos
-bash: distro: command not found
$ distro= zorinos
-bash: zorinos: command not found

Variable Naming Rules

  • May contain letters (a-z, A-Z), digits (0-9), underscore (_)
  • Must start with a letter or underscore โ€” not a digit
  • No spaces in names (use _ instead)
$ distro_1=zorinos    # OK
$ _distro=zorinos     # OK
$ 1distro=zorinos     # error

Value Rules

Values may contain letters, digits, and most special characters (?, !, *, ., /).

Values with spaces must be quoted:

$ distro="zorin 12.4"

Values with redirect or pipe characters need quoting, otherwise the shell interprets them literally:

$ distro=>zorin      # creates an empty file named 'zorin'
$ distro=">zorin"    # now it is a variable value

Backslash must be escaped with another backslash:

$ win_path=C:\\path\\to\\dir\\
$ echo $win_path
C:\path\to\dir\

Quotes in Assignment Context

Single quotes โ€” literal, no substitution:

$ lizard=uromastyx
$ animal='My $lizard'
$ echo $animal
My $lizard

Double quotes โ€” allow variable substitution:

$ animal="My $lizard"
$ echo $animal
My uromastyx

When referencing a variable whose value contains spaces or wildcards, always use double quotes with echo:

$ lizard="    genus    |    uromastyx"
$ echo $lizard          # shell does field splitting
genus | uromastyx
$ echo "$lizard"        # spaces preserved
    genus    |    uromastyx

Local Shell Variables

By convention, local variable names are lowercase.

readonly โ€” Immutable Variable

$ readonly reptile=tortoise

Or in two steps:

$ reptile=tortoise
$ readonly reptile

Attempt to change it fails:

$ reptile=lizard
-bash: reptile: readonly variable

List all readonly variables:

$ readonly
$ readonly -p

set โ€” List All Variables

set without arguments prints all shell variables and functions (including unexported ones). Pipe through less or grep:

$ set | less
$ set | grep reptile
reptile=tortoise

Child Shells and Visibility

A local variable is not passed to child shells:

$ reptile=tortoise
$ bash
$ echo $reptile
                     # empty
$ exit

unset โ€” Remove a Variable

$ unset reptile
$ echo $reptile
                     # empty

No dollar sign before the name. Works on both local and global variables.


Global Environment Variables

Environment variables are visible in the current shell and all spawned processes. By convention, names are UPPERCASE.

$ echo $SHELL
/bin/bash

Passing Values Between Variables

$ my_shell=$SHELL
$ your_shell=$my_shell
$ echo $your_shell
/bin/bash

export โ€” Make a Variable Global

$ export reptile          # export existing local variable
$ export amphibian=frog   # create and export in one step

After export, the variable is visible in child shells:

$ bash
$ echo $amphibian
frog

export -n โ€” Revoke Export

$ export -n reptile       # back to local

env, printenv, declare -x

All three list environment variables.

export without arguments (same as declare -x):

$ export
declare -x HOME="/home/user2"
declare -x PATH="/usr/local/bin:/usr/bin:/bin"
declare -x SHELL="/bin/bash"

env and printenv give a simpler list:

$ env
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
HOME=/home/user2

printenv can fetch one variable without a dollar sign:

$ printenv PWD
/home/user2

Distinction for the exam: set shows everything including local variables and functions. env/printenv show only exported variables that child processes inherit.


Running a Program in a Modified Environment

env -i โ€” Empty Environment

Starts a program with almost no inherited variables:

$ env -i bash
$ echo $USER
                     # empty

Useful for testing that a script does not rely on accidental user variables.

env VAR=value command

Run one command with a temporarily changed variable โ€” the shell’s own environment is unchanged:

$ env LANG=es_ES.UTF-8 date

BASH_ENV for Scripts

A non-interactive script reads no startup files, but checks BASH_ENV. If it points to a file, Bash sources that file before executing the script:

# ~/.startup_script
CROCODILIAN=caiman
$ BASH_ENV=/home/user2/.startup_script ./test_env.sh
caiman

Common Environment Variables

DISPLAY

X server address โ€” [hostname]:display[:screen]. Empty hostname means localhost:

$ printenv DISPLAY
:0

reptilium:0:2 โ€” X server on host reptilium, display 0, screen 2.

History Variables

VariablePurpose
HISTCONTROLignorespace (skip commands starting with space), ignoredups (skip consecutive duplicates), ignoreboth (both)
HISTSIZECommands kept in memory during session (default 1000)
HISTFILESIZECommands stored in history file (default 2000)
HISTFILEPath to history file (default ~/.bash_history)

HOME and Tilde

HOME holds the absolute path to the user’s home directory. Tilde ~ is a synonym:

$ echo ~; echo $HOME
/home/carol
/home/carol

Standard check in ~/.profile:

if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
fi

Test equality:

$ if [ ~ == "$HOME" ]; then echo "true"; fi
true

HOSTNAME and HOSTTYPE

$ echo $HOSTNAME      # TCP/IP hostname
debian
$ echo $HOSTTYPE      # CPU architecture
x86_64

LANG

System locale:

$ echo $LANG
en_UK.UTF-8

LD_LIBRARY_PATH

Colon-separated list of shared library directories:

$ echo $LD_LIBRARY_PATH
/usr/local/lib

MAIL and MAILCHECK

$ echo $MAIL          # path to mailbox
/var/mail/carol
$ echo $MAILCHECK     # check interval in seconds
60

PATH

Colon-separated list of directories searched for executables. Typical /etc/profile block:

if [ "`id -u`" -eq 0 ]; then
    PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
else
    PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"
fi
export PATH

id -u returns the UID โ€” 0 for root.

Add a directory:

$ PATH=/new/dir:$PATH     # first (searched first)
$ PATH=$PATH:/new/dir     # last (searched last)

Prompt Variables PS1โ€“PS4

PS1 โ€” primary prompt (# for root, $ for regular users).

PS2 โ€” continuation prompt for multi-line commands (default > ).

PS3 โ€” prompt for select construct.

PS4 โ€” debug tracing prefix for set -x (default + ).

SHELL and USER

$ echo $SHELL        # absolute path to current shell
/bin/bash
$ echo $USER         # current username
carol

Aliases and Functions

Aliases

alias ll='ls -lah'
alias ..='cd ..'
alias grep='grep --color=auto'

alias             # list all
unalias ll        # remove one
unalias -a        # remove all

Bash Functions

Functions accept positional arguments via $1, $2, $@. Functions override aliases of the same name.

greet() {
    echo "Hello, $1!"
}

greet World
# Hello, World!

List all defined functions:

$ declare -f

Saving Aliases and Functions

Put them in ~/.bashrc for persistence. Many distros use a separate ~/.bash_aliases sourced from ~/.bashrc:

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

Skeleton Directory /etc/skel

/etc/skel is the template home directory. Its contents are copied into every new user’s home when the account is created with useradd -m or adduser.

$ ls -la /etc/skel
.bash_logout  .bashrc  .profile

Add a line to any file here โ€” all future users get it automatically. Existing users are unaffected.

Change the template path: edit SKEL= in /etc/default/useradd, or use useradd -k /alternate/skel.


Quick Reference

# Check shell type
echo $0              # -bash = login
shopt login_shell    # on = login, off = non-login
echo $-              # 'i' present = interactive

# Apply config without restart
source ~/.bashrc
. ~/.bashrc
exec bash            # replace current shell process

# Variables
var=value            # local variable
export var=value     # create and export
export var           # export existing
export -n var        # revoke export
readonly var=value   # immutable
readonly             # list readonly variables
unset var            # remove variable
echo $var            # show value
printenv var         # show value (no $ needed)

# List variables
set                  # all variables and functions
env                  # environment only
printenv             # same as env
export               # environment with declare -x
declare -x           # synonym for export

# Modified environment
env -i bash                      # empty environment
env LANG=es_ES.UTF-8 date        # one variable for one command
BASH_ENV=~/.startup ./script.sh  # startup file for script

# Aliases and functions
alias ll='ls -lah'
unalias ll
alias
declare -f

# PATH
PATH=/new/dir:$PATH   # prepend (higher priority)
PATH=$PATH:/new/dir   # append

# Skel
ls -la /etc/skel
useradd -m newuser    # home created from /etc/skel

Exam Questions

  1. What is the difference between ~/.bash_profile and ~/.bashrc? โ†’ bash_profile is read once by a login shell at login. bashrc is read by every interactive non-login shell.
  2. Both ~/.bash_profile and ~/.profile exist โ€” which is read? โ†’ ~/.bash_profile; ~/.profile is ignored.
  3. Where does the system-wide PATH live? โ†’ /etc/profile (plus fragments in /etc/profile.d/).
  4. Difference between source file and bash file? โ†’ source executes in the current shell; bash file spawns a child shell and changes are lost on exit.
  5. Which command turns a local variable into a global one? โ†’ export VAR.
  6. What does export -n VAR do? โ†’ Revokes export; the variable becomes local again.
  7. Single vs double quotes in assignment? โ†’ Double quotes allow $variable substitution; single quotes take everything literally.
  8. Which variable specifies a startup file for a non-interactive script? โ†’ BASH_ENV.
  9. Which command shows only environment variables? โ†’ env, printenv, or export -p.
  10. Which command shows all variables including local ones and functions? โ†’ set.
  11. How to make new users receive a particular alias by default? โ†’ Add the alias line to the appropriate file in /etc/skel.
  12. How to create an immutable variable? โ†’ readonly VAR=value.
  13. What is ~/.bash_logout and when does it run? โ†’ A script executed when a login shell exits.
  14. Difference between unset VAR and VAR=? โ†’ unset removes the variable entirely; VAR= leaves it with an empty value.
  15. Which characters are forbidden at the start of a variable name? โ†’ Digits. Names must begin with a letter or underscore.
  16. What is declare -x? โ†’ A synonym for export; lists or marks variables as exported.
  17. What does env -i do? โ†’ Starts a command with an almost empty environment, stripping inherited variables.
  18. What do HISTCONTROL=ignoreboth and HISTCONTROL=ignoredups do? โ†’ ignoreboth: skip commands starting with space AND consecutive duplicates. ignoredups: skip consecutive duplicates only.
  19. What does DISPLAY=reptilium:0:2 mean? โ†’ X server on host reptilium, display number 0, screen 2.

Exercises

Exercise 1 โ€” Shell type by launch method

For each launch method, state whether the shell is a login shell and whether it is interactive.

Launch methodLoginInteractive
ssh user@host??
Ctrl+Alt+F2 (login on tty2)??
su -??
gnome-terminal??
sakura from konsole??
Running ./script.sh??
Answer
Launch methodLoginInteractive
ssh user@hostyesyes
Ctrl+Alt+F2yesyes
su -yesyes
gnome-terminalnoyes
sakura from konsolenoyes
./script.shnono

SSH, local tty login, and su - create a login shell (explicit system entry). Terminal emulators in a graphical session and shells inside other shells are non-login. A script is non-interactive.


Exercise 2 โ€” su and sudo for four shell types

Write the command to switch to user bob’s shell for each of the four types.

Shell typeCommand
Login + interactive?
Non-login + interactive?
Login + non-interactive?
Non-login + non-interactive?
Answer
Shell typeCommand
Login + interactivesu - bob or sudo -i -u bob
Non-login + interactivesu bob or sudo -u bob bash
Login + non-interactivesu - bob -c 'cmd' or sudo -i -u bob 'cmd'
Non-login + non-interactivesu bob -c 'cmd' or sudo -u bob 'cmd'

The dash in su - or the -i flag in sudo marks a login shell. Without them the shell is non-login. Adding -c 'cmd' runs one command non-interactively.


Exercise 3 โ€” Startup files for each shell type

Which initialization files does each shell type read? Assume the user has ~/.bash_profile, ~/.bashrc, and ~/.bash_logout, and ~/.bash_profile contains the standard block that sources ~/.bashrc.

Shell typeFiles read
Login + interactive?
Non-login + interactive?
Login + non-interactive?
Non-login + non-interactive?
Answer
Shell typeFiles read
Login + interactive/etc/profile, /etc/profile.d/*.sh, ~/.bash_profile (sources ~/.bashrc); on exit: ~/.bash_logout
Non-login + interactive/etc/bash.bashrc (Debian) or /etc/bashrc (RHEL), ~/.bashrc
Login + non-interactiveRare (bash --login script.sh) โ€” reads the same set as login + interactive
Non-login + non-interactiveOnly the file pointed to by BASH_ENV, if set

Exercise 4 โ€” Local or global variable?

For each command sequence, decide whether the result is a local or global variable.

CommandsLocalGlobal
debian=mother??
ubuntu=deb-based??
mint=ubuntu-based; export mint??
export suse=rpm-based??
zorin=ubuntu-based??
Answer
CommandsLocalGlobal
debian=motheryesno
ubuntu=deb-basedyesno
mint=ubuntu-based; export mintnoyes
export suse=rpm-basednoyes
zorin=ubuntu-basedyesno

Plain assignment creates a local variable. export, whether combined with assignment or applied afterward, makes it a global environment variable.


Exercise 5 โ€” Decode variable values

Explain what each output means.

CommandOutput
echo $HISTCONTROLignoreboth
echo ~/home/carol
echo $DISPLAYreptilium:0:2
echo $MAILCHECK60
echo $HISTFILE/home/carol/.bash_history
Answer

HISTCONTROL=ignoreboth โ€” commands starting with a space are not saved to history, and consecutive duplicate commands are not saved either.

~ = /home/carol โ€” the tilde expands to $HOME, which is /home/carol for user carol.

DISPLAY=reptilium:0:2 โ€” the X server is running on host reptilium, display number 0, screen 2.

MAILCHECK=60 โ€” Bash checks for new mail every 60 seconds.

HISTFILE=/home/carol/.bash_history โ€” command history is saved to this file when the session ends.


Exercise 6 โ€” Fix invalid assignments

Each assignment is broken. Write the correct command and the echo call to produce the expected output.

Broken assignmentExpected output
lizard =chameleonchameleon
cool lizard=chameleonchameleon
lizard=cha|me|leoncha|me|leon
lizard=/** chameleon **//** chameleon **/
win_path=C:\path\to\dir\C:\path\to\dir\
Answer
Correct commandReference
lizard=chameleonecho $lizard
cool_lizard=chameleonecho $cool_lizard
lizard="cha|me|leon"echo $lizard
lizard="/** chameleon **/"echo "$lizard"
win_path=C:\\path\\to\\dir\\echo $win_path

Line 1: no spaces around =. Line 2: spaces in names are forbidden โ€” use underscore. Lines 3โ€“4: special characters | and * must be quoted. Line 4: double quotes required in echo to preserve internal spaces. Line 5: each backslash must be escaped with another backslash.


Exercise 7 โ€” Name the command for each task

TaskCommand
Set the current shell locale to Spanish UTF-8 (es_ES.UTF-8)?
Print the current working directory?
Access the variable holding SSH connection info?
Append /home/carol/scripts to PATH (lowest priority)?
Assign the literal string PATH to my_path?
Assign the value of $PATH to my_path?
Answer
TaskCommand
Set localeLANG=es_ES.UTF-8
Print working directoryecho $PWD or pwd
SSH connection infoecho $SSH_CONNECTION
Append scripts to PATHPATH=$PATH:/home/carol/scripts
Literal PATH into my_pathmy_path=PATH
Value of $PATH into my_pathmy_path=$PATH

Without the dollar sign PATH is just five characters. With $ the shell expands it to its value.


Exercise 8 โ€” Local variable mammal

Create a local variable mammal with value gnu.

Answer
mammal=gnu

Exercise 9 โ€” Variable substitution in a value

Using substitution, create a local variable var_sub so that echo $var_sub outputs The value of mammal is gnu.

Answer
var_sub="The value of mammal is $mammal"

Double quotes allow $mammal to be expanded. Single quotes would output The value of mammal is $mammal literally.


Exercise 10 โ€” Export mammal

Make mammal a global environment variable.

Answer
export mammal

Exercise 11 โ€” Search with set and grep

Find mammal using set and grep.

Answer
set | grep mammal

Exercise 12 โ€” Search with env and grep

Find mammal using env and grep.

Answer
env | grep mammal

After export mammal, the variable appears in both set and env. Before export, env did not show it; set did.


Exercise 13 โ€” BIRD with two commands

Create an environment variable BIRD with value penguin. Use two commands.

Answer
BIRD=penguin; export BIRD

The semicolon separates two commands on one line. They can also be written on separate lines.


Exercise 14 โ€” NEW_BIRD with one command

Create an environment variable NEW_BIRD with value yellow-eyed penguin. Use one command.

Answer
export NEW_BIRD="yellow-eyed penguin"

Quotes are required because of the spaces in the value. Single quotes work equally well here since there is no variable substitution needed.


Exercise 15 โ€” Create ~/bin

You are user2. Create a bin directory in your home directory.

Answer
mkdir ~/bin

Equivalents: mkdir /home/user2/bin or mkdir $HOME/bin.


Exercise 16 โ€” Prepend ~/bin to PATH

Add ~/bin to PATH so Bash searches it first.

Answer
PATH="$HOME/bin:$PATH"

Equivalents: PATH=~/bin:$PATH or PATH=/home/user2/bin:$PATH.

The colon separates directories. Placing your directory before $PATH makes Bash check it before system directories.


Exercise 17 โ€” Persist PATH via ~/.profile

Write the if block to add ~/bin to PATH persistently via ~/.profile.

Answer
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

The [ -d "$HOME/bin" ] check confirms the directory exists before adding it. Without the check, a non-existent path would pollute PATH.


Exercise 18 โ€” Hello world function and where to put it

Write a hello function that prints Hello, world!. Show how to load it with source. In which file should it go so user2 has it in every X Window terminal? In which file so root has it in any interactive shell?

Answer
hello() {
    echo "Hello, world!"
}

Save to a file (e.g., ~/myfuncs.sh) and load:

$ source ~/myfuncs.sh
$ hello
Hello, world!

For user2 in X Window terminals (non-login interactive): ~/.bashrc.

For root in any interactive shell: /root/.bashrc. If root also uses login shells (su -), add source ~/.bashrc to /root/.bash_profile as well, or put the function in /etc/bash.bashrc to cover all interactive shells system-wide.


Exercise 19 โ€” Is a simple echo script interactive?

Script simple.sh:

#!/bin/bash
echo "Hi"

Is it interactive? What makes a script interactive?

Answer

Not interactive. The script runs as a non-login non-interactive shell: commands come from a file, not a keyboard; stdin/stdout are not connected to a terminal by the user.

A script becomes interactive when it explicitly requests user input via read and its stdin is connected to a terminal:

#!/bin/bash
read -p "What is your name? " name
echo "Hello, $name!"

Exercise 20 โ€” Apply ~/.bashrc changes without restarting

You edited ~/.bashrc. Apply the changes in the current shell without restarting it. Show two ways.

Answer
source ~/.bashrc
. ~/.bashrc

Both execute the file’s commands in the current shell. Running bash ~/.bashrc would not work โ€” variables would be set in a child shell and lost on exit.

A third option for a full shell reload: exec bash or exec $SHELL replaces the current shell process with a new one, reading all startup files fresh.


Exercise 21 โ€” Switching to tty when GUI freezes

The GUI is frozen. You press Ctrl+Alt+F2 and log in on tty2. Which initialization files does Bash read?

Answer

Logging in on tty2 creates a login interactive shell. Files read:

  1. /etc/profile (sources /etc/profile.d/*.sh)
  2. First found of: ~/.bash_profile, ~/.bash_login, ~/.profile

If ~/.bash_profile contains the standard block sourcing ~/.bashrc, then ~/.bashrc is also read. On Debian, /etc/profile typically also sources /etc/bash.bashrc.

On exit, ~/.bash_logout is executed.


Exercise 22 โ€” Automating ~/.bash_login via /etc/skel

You want every new user to receive ~/.bash_login with a custom set of commands. How do you automate this?

Answer

Place the file in /etc/skel:

$ sudo nano /etc/skel/.bash_login

When a new user is created with useradd -m newuser or adduser newuser, the system copies /etc/skel/.bash_login to /home/newuser/.bash_login.

Note on priority: ~/.bash_login is used only if ~/.bash_profile is absent. If ~/.bash_profile exists, ~/.bash_login is silently skipped.

Existing users are not affected โ€” copy the file manually for them.


Exercise 23 โ€” let and arithmetic

Create a local variable my_val with the value 10 as the result of 5 + 5. Then create your_val with the value 5 as the result of dividing my_val by 2.

Answer
let "my_val = 5 + 5"
let "your_val = $my_val / 2"

let evaluates an arithmetic expression and assigns the result. Equivalents: ((my_val = 5 + 5)) or my_val=$((5 + 5)). All variants work with integers only.


Exercise 24 โ€” Command substitution syntax

Which is the correct modern equivalent of:

latest_music=`ls -l1t ~/Music | head -n 6`

A. latest_music=$(ls -l1t ~/Music | head -n 6)

B. latest_music="(ls -l1t ~/Music | head -n 6)"

C. latest_music=((ls -l1t ~/Music | head -n 6))

Answer

A is correct:

latest_music=$(ls -l1t ~/Music | head -n 6)

Command substitution has two equivalent syntaxes: backticks `cmd` and $(cmd). The modern style is $(cmd) because it is easier to nest.

B produces a literal string. C is arithmetic substitution โ€” it evaluates integers, not shell commands.


LPIC-1 Study Notes | Topic 105: Shells and Shell Scripting