1 Bash and Bash scripts #
Today, many people use computers with a graphical user interface (GUI) like GNOME. Although GUIs offer many features, they are limited when performing automated task execution. Shells complement GUIs well, and this chapter gives an overview of some aspects of shells, in this case the Bash shell.
1.1 What is “the shell”? #
Traditionally, the Linux shell is Bash (Bourne again Shell). When this chapter speaks about “the shell” it means Bash. There are more shells available (ash, csh, ksh, zsh, …), each employing different features and characteristics.
1.1.1 Bash configuration files #
A shell can be invoked as an:
Interactive login shell. This is used when logging in to a machine, invoking Bash with the
--login
option or when logging in to a remote machine with SSH.Interactive non-login shell. This is normally the case when starting xterm, konsole, gnome-terminal, or similar command line interface (CLI) tools.
Non-interactive non-login shell. This is invoked when invoking a shell script at the command line.
Depending on the type of shell you use, different configuration files will be read. The following tables show the login and non-login shell configuration files.
Bash looks for its configuration files in a specific order depending on
the type of shell where it is run. Find more details on the Bash man
page (man 1 bash
). Search for the headline
INVOCATION
.
File |
Description |
---|---|
|
Do not modify this file, otherwise your modifications may be destroyed during your next update! |
|
Use this file if you extend |
|
Contains system-wide configuration files for specific programs |
|
Insert user specific configuration for login shells here |
Note that the login shell also sources the configuration files listed under Table 1.2, “Bash configuration files for non-login shells”.
|
Do not modify this file, otherwise your modifications may be destroyed during your next update! |
|
Use this file to insert your system-wide modifications for Bash only |
|
Insert user specific configuration here |
Additionally, Bash uses some more files:
File |
Description |
---|---|
|
Contains a list of all commands you have typed |
|
Executed when logging out |
|
User defined aliases of frequently used commands. See
|
No-Login Shells#
There are special shells that block users from logging into
the system: /bin/false
and
/sbin/nologin
. Both fail silently
when the user attempts to log into the system. This was intended
as a security measure for system users, though modern
Linux operating systems have more effective tools for controlling system
access, such as PAM and AppArmor.
The default on SUSE Linux Enterprise Server is to assign /bin/bash
to human users, and /bin/false
or
/sbin/nologin
to system users.
The nobody
user has /bin/bash
for historical reasons, as
it is a minimally-privileged user that used to be the default for system users.
However, whatever little bit of security gained by using
nobody
is lost when
multiple system users use it. It should be possible to change it to
/sbin/nologin
; the fastest way to test it is change
it and see if it breaks any services or applications.
Use the following command to list which shells are assigned to all users,
system and human users, in /etc/passwd
. The output
varies according to the services and users on your system:
>
sort -t: -k 7 /etc/passwd | awk -F: '{print $1"\t" $7}' | column -t
tux /bin/bash
nobody /bin/bash
root /bin/bash
avahi /bin/false
chrony /bin/false
dhcpd /bin/false
dnsmasq /bin/false
ftpsecure /bin/false
lightdm /bin/false
mysql /bin/false
postfix /bin/false
rtkit /bin/false
sshd /bin/false
tftp /bin/false
unbound /bin/false
bin /sbin/nologin
daemon /sbin/nologin
ftp /sbin/nologin
lp /sbin/nologin
mail /sbin/nologin
man /sbin/nologin
nscd /sbin/nologin
polkitd /sbin/nologin
pulse /sbin/nologin
qemu /sbin/nologin
radvd /sbin/nologin
rpc /sbin/nologin
statd /sbin/nologin
svn /sbin/nologin
systemd-coredump /sbin/nologin
systemd-network /sbin/nologin
systemd-timesync /sbin/nologin
usbmux /sbin/nologin
vnc /sbin/nologin
wwwrun /sbin/nologin
messagebus /usr/bin/false
scard /usr/sbin/nologin
1.1.2 The directory structure #
The following table provides a short overview of the most important higher-level directories that you find on a Linux system. Find more detailed information about the directories and important subdirectories in the following list.
Directory |
Contents |
---|---|
|
Root directory—the starting point of the directory tree. |
|
Essential binary files, such as commands that are needed by both the system administrator and normal users. Usually also contains the shells, such as Bash. |
|
Static files of the boot loader. |
|
Files needed to access host-specific devices. |
|
Host-specific system configuration files. |
|
Holds the home directories of all users who have accounts on the system.
However, |
|
Essential shared libraries and kernel modules. |
|
Mount points for removable media. |
|
Mount point for temporarily mounting a file system. |
|
Add-on application software packages. |
|
Home directory for the superuser |
|
Essential system binaries. |
|
Data for services provided by the system. |
|
Temporary files. |
|
Secondary hierarchy with read-only data. |
|
Variable data such as log files. |
|
Only available if you have both Microsoft Windows* and Linux installed on your system. Contains the Windows data. |
The following list provides more detailed information and gives some examples of which files and subdirectories can be found in the directories:
/bin
Contains the basic shell commands that may be used both by
root
and by other users. These commands includels
,mkdir
,cp
,mv
,rm
andrmdir
./bin
also contains Bash, the default shell in SUSE Linux Enterprise Server./boot
Contains data required for booting, such as the boot loader, the kernel, and other data that is used before the kernel begins executing user-mode programs.
/dev
Holds device files that represent hardware components.
/etc
Contains local configuration files that control the operation of programs like the X Window System. The
/etc/init.d
subdirectory contains LSB init scripts that can be executed during the boot process./home/USERNAME
Holds the private data of every user who has an account on the system. The files located here can only be modified by their owner or by the system administrator. By default, your e-mail directory and personal desktop configuration are located here in the form of hidden files and directories, such as
.gconf/
and.config
.Note: Home directory in a network environmentIf you are working in a network environment, your home directory may be mapped to a directory in the file system other than
/home
./lib
Contains the essential shared libraries needed to boot the system and to run the commands in the root file system. The Windows equivalent for shared libraries are DLL files.
/media
Contains mount points for removable media, such as CD-ROMs, flash disks, and digital cameras (if they use USB).
/media
generally holds any type of drive except the hard disk of your system. When your removable medium has been inserted or connected to the system and has been mounted, you can access it from here./mnt
This directory provides a mount point for a temporarily mounted file system.
root
may mount file systems here./opt
Reserved for the installation of third-party software. Optional software and larger add-on program packages can be found here.
/root
Home directory for the
root
user. The personal data ofroot
is located here./run
A tmpfs directory used by
systemd
and various components./var/run
is a symbolic link to/run
./sbin
As the
s
indicates, this directory holds utilities for the superuser./sbin
contains the binaries essential for booting, restoring and recovering the system in addition to the binaries in/bin
./srv
Holds data for services provided by the system, such as FTP and HTTP.
/tmp
This directory is used by programs that require temporary storage of files.
Important: Cleaning up/tmp
at boot timeData stored in
/tmp
is not guaranteed to survive a system reboot. It depends, for example, on settings made in/etc/tmpfiles.d/tmp.conf
./usr
/usr
has nothing to do with users, but is the acronym for Unix system resources. The data in/usr
is static, read-only data that can be shared among various hosts compliant with theFilesystem Hierarchy Standard
(FHS). This directory contains all application programs including the graphical desktops such as GNOME and establishes a secondary hierarchy in the file system./usr
holds several subdirectories, such as/usr/bin
,/usr/sbin
,/usr/local
, and/usr/share/doc
./usr/bin
Contains generally accessible programs.
/usr/sbin
Contains programs reserved for the system administrator, such as repair functions.
/usr/local
In this directory the system administrator can install local, distribution-independent extensions.
/usr/share/doc
Holds various documentation files and the release notes for your system. In the
manual
subdirectory find an online version of this manual. If more than one language is installed, this directory may contain versions of the manuals for different languages.Under
packages
find the documentation included in the software packages installed on your system. For every package, a subdirectory/usr/share/doc/packages/PACKAGENAME
is created that often holds README files for the package and sometimes examples, configuration files or additional scripts.If HOWTOs are installed on your system
/usr/share/doc
also holds thehowto
subdirectory in which to find additional documentation on many tasks related to the setup and operation of Linux software./var
Whereas
/usr
holds static, read-only data,/var
is for data which is written during system operation and thus is variable data, such as log files or spooling data. For an overview of the most important log files you can find under/var/log/
, refer to Table 48.1, “Log files”.
1.2 Writing shell scripts #
Shell scripts provide a convenient way to perform a wide range of tasks: collecting data, searching for a word or phrase in a text and other useful things. The following example shows a small shell script that prints a text:
#!/bin/sh 1 # Output the following line: 2 echo "Hello World" 3
The first line begins with the Shebang
characters ( | |
The second line is a comment beginning with the hash sign. We recommend that you comment difficult lines. With proper commenting, you can remember the purpose and function of the line. Also, other readers will hopefully understand your script. Commenting is considered good practice in the development community. | |
The third line uses the built-in command |
Before you can run this script, there are a few prerequisites:
Every script should contain a Shebang line (as in the example above). If the line is missing, you need to call the interpreter manually.
You can save the script wherever you want. However, it is a good idea to save it in a directory where the shell can find it. The search path in a shell is determined by the environment variable
PATH
. Usually a normal user does not have write access to/usr/bin
. Therefore it is recommended to save your scripts in the users' directory~/bin/
. The above example gets the namehello.sh
.The script needs executable permissions. Set the permissions with the following command:
>
chmod +x ~/bin/hello.sh
If you have fulfilled all of the above prerequisites, you can execute the script in the following ways:
As absolute path. The script can be executed with an absolute path. In our case, it is
~/bin/hello.sh
.Everywhere. If the
PATH
environment variable contains the directory where the script is located, you can execute the script withhello.sh
.
1.3 Redirecting command events #
Each command can use three channels, either for input or output:
Standard output. This is the default output channel. Whenever a command prints something, it uses the standard output channel.
Standard input. If a command needs input from users or other commands, it uses this channel.
Standard error. Commands use this channel for error reporting.
To redirect these channels, there are the following possibilities:
Command > File
Saves the output of the command into a file, an existing file will be deleted. For example, the
ls
command writes its output into the filelisting.txt
:>
ls > listing.txtCommand >> File
Appends the output of the command to a file. For example, the
ls
command appends its output to the filelisting.txt
:>
ls >> listing.txtCommand < File
Reads the file as input for the given command. For example, the
read
command reads in the content of the file into the variable:>
read a < fooCommand1 | Command2
Redirects the output of the left command as input for the right command. For example, the
cat
command outputs the content of the/proc/cpuinfo
file. This output is used bygrep
to filter only those lines which containcpu
:>
cat /proc/cpuinfo | grep cpu
Every channel has a file descriptor: 0 (zero) for
standard input, 1 for standard output and 2 for standard error. It is
allowed to insert this file descriptor before a <
or
>
character. For example, the following line searches
for a file starting with foo
, but suppresses its errors
by redirecting it to /dev/null
:
>
find / -name "foo*" 2>/dev/null
1.4 Using aliases #
An alias is a shortcut definition of one or more commands. The syntax for an alias is:
alias NAME=DEFINITION
For example, the following line defines an alias lt
that
outputs a long listing (option -l
), sorts it by
modification time (-t
), and prints it in reverse sorted order (-r
):
>
alias lt='ls -ltr'
To view all alias definitions, use alias
. Remove your
alias with unalias
and the corresponding alias name.
1.5 Using variables in Bash #
A shell variable can be global or local. Global variables, or environment variables, can be accessed in all shells. In contrast, local variables are visible in the current shell only.
To view all environment variables, use the printenv
command. If you need to know the value of a variable, insert the name of
your variable as an argument:
>
printenv PATH
A variable, be it global or local, can also be viewed with
echo
:
>
echo $PATH
To set a local variable, use a variable name followed by the equal sign, followed by the value:
>
PROJECT="SLED"
Do not insert spaces around the equal sign, otherwise you get an error. To
set an environment variable, use export
:
>
export NAME="tux"
To remove a variable, use unset
:
>
unset NAME
The following table contains some common environment variables which can be used in you shell scripts:
|
the home directory of the current user |
|
the current host name |
|
when a tool is localized, it uses the language from this environment
variable. English can also be set to |
|
the search path of the shell, a list of directories separated by colon |
|
specifies the normal prompt printed before each command |
|
specifies the secondary prompt printed when you execute a multi-line command |
|
current working directory |
|
the current user |
1.5.1 Using argument variables #
For example, if you have the script foo.sh
you can
execute it like this:
>
foo.sh "Tux Penguin" 2000
To access all the arguments which are passed to your script, you need
positional parameters. These are $1
for the first argument,
$2
for the second, and so on. You can have up to nine
parameters. To get the script name, use $0
.
The following script foo.sh
prints all arguments from 1
to 4:
#!/bin/sh echo \"$1\" \"$2\" \"$3\" \"$4\"
If you execute this script with the above arguments, you get:
"Tux Penguin" "2000" "" ""
1.5.2 Using variable substitution #
Variable substitutions apply a pattern to the content of a variable either from the left or right side. The following list contains the possible syntax forms:
${VAR#pattern}
removes the shortest possible match from the left:
>
file=/home/tux/book/book.tar.bz2>
echo ${file#*/} home/tux/book/book.tar.bz2${VAR##pattern}
removes the longest possible match from the left:
>
file=/home/tux/book/book.tar.bz2>
echo ${file##*/} book.tar.bz2${VAR%pattern}
removes the shortest possible match from the right:
>
file=/home/tux/book/book.tar.bz2>
echo ${file%.*} /home/tux/book/book.tar${VAR%%pattern}
removes the longest possible match from the right:
>
file=/home/tux/book/book.tar.bz2>
echo ${file%%.*} /home/tux/book/book${VAR/pattern_1/pattern_2}
substitutes the content of VAR from the PATTERN_1 with PATTERN_2:
>
file=/home/tux/book/book.tar.bz2>
echo ${file/tux/wilber} /home/wilber/book/book.tar.bz2
1.6 Grouping and combining commands #
Shells allow you to concatenate and group commands for conditional execution. Each command returns an exit code which determines the success or failure of its operation. If it is 0 (zero) the command was successful, everything else marks an error which is specific to the command.
The following list shows, how commands can be grouped:
Command1 ; Command2
executes the commands in sequential order. The exit code is not checked. The following line displays the content of the file with
cat
and then prints its file properties withls
regardless of their exit codes:>
cat filelist.txt ; ls -l filelist.txtCommand1 && Command2
runs the right command, if the left command was successful (logical AND). The following line displays the content of the file and prints its file properties only, when the previous command was successful (compare it with the previous entry in this list):
>
cat filelist.txt && ls -l filelist.txtCommand1 || Command2
runs the right command, when the left command has failed (logical OR). The following line creates only a directory in
/home/wilber/bar
when the creation of the directory in/home/tux/foo
has failed:>
mkdir /home/tux/foo || mkdir /home/wilber/barfuncname(){ ... }
creates a shell function. You can use the positional parameters to access its arguments. The following line defines the function
hello
to print a short message:>
hello() { echo "Hello $1"; }You can call this function like this:
>
hello Tuxwhich prints:
Hello Tux
1.7 Working with common flow constructs #
To control the flow of your script, a shell has while
,
if
, for
and case
constructs.
1.7.1 The if control command #
The if
command is used to check expressions. For
example, the following code tests whether the current user is Tux:
if test $USER = "tux"; then echo "Hello Tux." else echo "You are not Tux." fi
The test expression can be as complex or simple as possible. The following
expression checks if the file foo.txt
exists:
if test -e /tmp/foo.txt ; then echo "Found foo.txt" fi
The test expression can also be abbreviated in square brackets:
if [ -e /tmp/foo.txt ] ; then echo "Found foo.txt" fi
Find more useful expressions at https://bash.cyberciti.biz/guide/If..else..fi.
1.7.2 Creating loops with the for
command #
The for
loop allows you to execute commands to a list of
entries. For example, the following code prints some information about PNG
files in the current directory:
for i in *.png; do ls -l $i done
1.8 More information #
Important information about Bash is provided in the man pages man
bash
. More about this topic can be found in the following list:
https://tldp.org/LDP/Bash-Beginners-Guide/html/index.html—Bash Guide for Beginners
https://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html—BASH Programming - Introduction HOW-TO
https://tldp.org/LDP/abs/html/index.html—Advanced Bash-Scripting Guide
http://www.grymoire.com/Unix/Sh.html—Sh - the Bourne Shell