When working in a terminal window, the shell is the program that reads input that you type in, forwards it to the operating system and then presents a prompt accepting the next input. It is therefore your interface to directly launching commands. Its power comes from the way how commands can be combined and wrapped up in scripts to perform more complex or repetitive operations. Using shell commands efficiently can save you a lot of time, avoiding repetition be it on the command line or in a graphical interface.
![]() | Slides |
---|---|
Slides accompanying this part of the introduction cover basic shell scripting and control structures. |
The bash version of the ubiquitous hello world example is below. The line starting with #! specifies the shell, i.e. the program to execute this file. The echo command prints the following string. The string can contain variables, which will be expanded, i.e. replaced by their contents.
![]() | Cut-and-paste this code into a file called hello.sh. Then set execute permission on that file and run it like this:
|
Bash distinguishes between read and write usages of shell variable. In a read usage, denoted by a $ prefix, the occurence of the variable is replaced by its value. In a write usage, just using the name of the variable, the variable stands for the location where to save a value. The following example first makes write use of variable v, storing a string, and then makes a read use, expanding and printing the contents of the variable. The variable USER is a so-called environment variable, which is automatically set when logging in.
Expansion of variables under strings is done differently, depending on the style of quotes that are used:
Within double-quotes ("), variables will be expanded and a sub-shell can be executed using back-ticks "
Within single-quotes ('), all characters are taken literally and no variable expansion is executed
If variable expansion is enabled, special characters can be escaped using a backslash (\)
$ what="variable expansion" $ s="testing $what" $ echo $s testing variable expansion $ s="testing \$what" $ echo $s testing $what $ s='testing $what' $ echo $s testing $what t="The current date is `date`" $ echo $t The current date is Thu Sep 6 16:26:42 BST 2012 |
![]() | Shell Quoting |
---|---|
Expansion of shell variables can be tricky. For more details see the Advanced Shell Scripting Guide. |
Other special variables are
$# the number of arguments.
$* the entire argument string.
$? the return code of the last command issued.
In a previous example we used the following pipe to compute the number of files in the current directory:
$ ls -l | wc -l |
$ x=`ls -l | wc -l` 52 $ let n=x-1 $ echo $n 51 |
$ echo $[ `ls -l | wc -l` - 1] 51 |
![]() | See this section in the Advanced Bash-Scripting Guide for details on arithmetic expressions, backtics and let-bindings. |
The following annotated sequence of commands demonstrates the usage of regular expressions, by selecting files and searching for text in files.
# list all C source or header files $ ls *.[ch] # search for Flags in these files $ egrep Flags *.[ch] # colourise $ egrep -n --color Flags *.[ch] # everything that accesses Flags, ie. a '.' afterwards $ egrep -n --color "(Par|Gc)Flags[.]" *.[ch] # several dots $ egrep -n --color "(Par|Gc)Flags[.]{3,}" *.[ch] # Flags but not with an 'n' after the '.' $ egrep -n --color "(Par|Gc)Flags[.][^n]" *.[ch] # recursively in all dirs $ find . -name "*.[ch]" -print # do something recursively in all dirs $ find . -name "*.[ch]" -print -exec egrep -n --color "(Par|Gc)Flags[.][^n]" \{\} \; # files modified within the last 24 hrs $ find . -mtime -1 -print # disk usage $ du -s *.[ch] | sort -n | (while read x nam ; do z=$[ $z + $x ] ; done; echo $z) |
# Pipes etc # check CVS status of the dir $ cvs status . # narrow it down to the main status line $ cvs status . | egrep "^File" # ignore all up-to-date lines $ cvs status . | egrep "^File" | sed '/Up-to-date/d' # ignore the "examning" output $ cvs status . 2>/dev/null | egrep "^File" | sed '/Up-to-date/d' # Getting a list of all working revision numbers # extract the revision number, using sed $ cvs status . | sed 's/Working revision[^0-9]*\([.0-9]*\).*$/\1/' # ignore all other lines $ cvs status . | sed '/Working revision/!d;s/Working revision[^0-9]*\([.0-9]*\).*$/\1/' # sort the numbers $ cvs status . | sed '/Working revision/!d;s/Working revision[^0-9]*\([.0-9]*\).*$/\1/' | sort # delete duplicate lines $ cvs status . | sed '/Working revision/!d;s/Working revision[^0-9]*\([.0-9]*\).*$/\1/' | sort | uniq # ignore stdout $ cvs status . 2>/dev/null | sed '/Working revision/!d;s/Working revision[^0-9]*\([.0-9].*\).*$/\1/' | sort | uniq |
$ loc1.sh *.[ch] |
The following example takes a log file of a series of parallel runs and turns it into a gnuplot-generated speedup graph. Details on which data is shown is not important. It mainly serves as an example on how to use basic Linux tools, composed via pipes, to do data processing (with the alias qp defined below) and visualisation (using the GNU tool gnuplot) from the command-line or from within a shell script. After running the last command you should see a new window with a speedup graph.
# Input: sequence of ghc -sstderr outputs, first one is sequential runtime # Output: .gp files of speedups # NB: this must be executed in the .bashrc; on the commandline more escaping is needed $ alias qp="cat LOG | sed -n '/SPARKS/p;/^\*/p;/Total time/p' | sed '/Total time/a\X' | sed '/SPARKS/d;s/^.*PEs \([0-9]*\)/\1/;s/^.*Total time[^(]*[(][^0-9]*\([.0-9]*\).*\$/\1/g' | sed '/^\*\*/a\X' | sed ':a;N;\$!ba;s/\n/ /g' | sed 's/X/\n/g' | sed '1d' | awk '/^[ ]*\*/ { print \"# \" \$_ };/^[ ]*[0-9]/ { if ( seq == 0) seq = \$1 ; if (\$2 > 0) print \$1 \" \" seq/\$2; }' " # download the data file, containing measurement data, via the web $ wget http://www.macs.hw.ac.uk/~hwloidl/Courses/F21SC/Data/LOG-2010-06-10-0105 # create symbolic link, ie. an indirection, from LOG-2010-06-10-0105 to LOG; both names now refer to the same file $ ln -s LOG-2010-06-10-0105 LOG # now calculate the speedups for all executions in the data file: # ignore everything after Strategy 3, then delete comment lines, and take last 8 lines $ qp | sed '/Strategy 2/q' | sed '/^#/d' | tail -8 >q.dat # showing speedup the speedup graph $ echo "set term x11; plot \"q.dat\" with lines; pause 10 " | gnuplot |
Similar in behaviour to the if statement in other programming languages with few differences: The command is executed which could be a script. Based on the return value, the statement after the then or else get executed. Return value 0 is true and any other value is considered to be false.
![]() | Online examples |
---|---|
Some examples in this section are taken from the Linux Classes online course. |
if script1; then ls -l else echo "script1 returned false" fi |
Example 4. Conditionals
#!/bin/bash FIRST_ARGUMENT="$1" if [ x$FIRST_ARGUMENT = "xHans" ]; then echo "Hello Hans, good to see you" else echo "Hello World" fi |
Example 5. File Copy
#!/bin/bash # copy a file, creating a backup if the target file exists if [ $# -lt 2 ] then echo "Usage: $0 fromfile tofile" exit 1 fi if [ -f $2 ] then mv $2 $2.bak fi cp $1 $2 |
This script prints all arguments passed to it, using a while loop. The shift command moves the values stored in the command line variables one position to the left. The first command line argument is $1, and a $* is a string composed of all arguments.
$ ./echoall.sh one two three value is one value is two value is three |
The following example uses an until loop instead:
Example 7. Until loop
#!/bin/bash # Source: http://lowfatlinux.com/linux-script-looping.html count=1 until [ "$*" = "" ] do echo "value number $count $1 " shift count=$[ $count + 1 ] # count= `expr $count + 1` done |
The following example uses a for loop instead:
Functions in shell-scripts are very restricted, and the syntax is given below.
The () indicates that this is a function, but it is always empty.
Arguments are referred to in the body of the function as $1, $2
etc.
A function may return an byte-size integer value as the return code.
functionname () { statement } |
See the section on functions in the Advanced Bash-Scripting Guide:.
![]() | Further Reading |
---|---|
Study some of the examples in the Advanced Bash Scripting Guide, for example this days-between function. |
![]() | Exercise | |
---|---|---|
As a simple exercise, write a shell script that prints the file type for each file passed as an argument to the script, e.g.
|
<<< Previous | Home | Next >>> |
Basic Linux Usage | Shell Script Examples |