Bash Programming

From Training Material
Jump to navigation Jump to search


title
Bash Programming Training Materials
author
王昊 Wang Hao (NobleProg Ltd)

Bash Programming

Bash Programming Training Materials


General Topics …⌘

  • Shell Introduction
  • Commands
  • Bash Special Characters
  • Variables
  • Statements

The simplest script ⌘

A list of command



#!/bin/bash
cal
echo "This is (echo $$) SHELL Version `bash --version` "

Choosing the shell (magic number #!, Sha-bang) ⌘



echo $0
chsh -l
chsh -s /bin/csh
  • /bin/bash - Bourne Again Shell
  • /bin/sh - Standard shell
  • /bin/csh - C Shell
  • /bin/tcsh - The Turbo C Shell
  1. Bash can run them; reverse might not be true.
  2. POSIX (Portable Operating System Interface for Unix)
  3. POSIX is standard but you still must test the real world shell.



#!/bin/bash

Calling the script ⌘



bash scriptname

chmod 555 scriptname
chmod +rx scriptname
chmod u+rx scriptname

./scriptname

External and built-in commands - Simple Commands ⌘

man ⌘

  • man command
  • man 7 signal
  • man -k signal

type ⌘

system command - binary program

builtin - bash reimplementation, maybe same "name", same usage but essentially different - no subshell, no child process, fast.

keyword - As examples, for, while, do, and ! are keywords. Similar to a builtin, a keyword is hard-coded into Bash, but unlike a builtin, a keyword is not in itself a command, but a subunit of a command construct.

type cat
type ls
type which
type true
type [
type [[

# Do not create duplicated new command, check via which, whatis,locate commmand

help ⌘



help

cd ⌘



cd ..
cd $OLDPWD

mkdir -p ⌘



mkdir 2
mkdir -p 3/3a/3aa
mkdir -m a=rwx newfolder

rmdir ⌘



rmdir -v 2
rmdir -p 3/3a/3aa

cp -i ⌘



cp file1 1
cp -i file1 1

mv -i ⌘



mv file 1/file
cp 1/file file
mv -i file 1/file

rm ⌘



rm -i 1/file

rm -r ⌘



rm -r 3

?,*,^,[],[-],{} ⌘



echo
rm -r * # <TAB> <TAB>
ls -l h??.txt
ls -l [hf]*
ls -l [b-h]*
ls -l [^hf]*
ls -l {b*,p*,*tuf*}

Filename expansion or globbing ⌘



bash$ ls -l
total 2
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
 -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
 -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt

bash$ ls -l t?.sh
-rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh

bash$ ls -l [ab]*
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1

Filename expansion or globbing ⌘



bash$ ls -l
total 2
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
 -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
 -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt

bash$ ls -l [a-c]*
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1

bash$ ls -l [^ab]*
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
 -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
 -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt

Filename expansion or globbing ⌘



bash$ ls -l
total 2
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 a.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
 -rw-rw-r--    1 bozo  bozo       466 Aug  6 17:48 t2.sh
 -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt

bash$ ls -l {b*,c*,*est*}
-rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 b.1
 -rw-rw-r--    1 bozo  bozo         0 Aug  6 18:42 c.1
 -rw-rw-r--    1 bozo  bozo       758 Jul 30 09:02 test1.txt

External and built-in commands - Complex Commands ⌘

date ⌘



date
date -u
zdump EST
zdump PST
cal
cal -3
cal -y

sleep ⌘



sleep 3

head, tail ⌘



head -n
tail -n

wc ⌘



wc -l
wc -m

find ⌘

  • Expression
  • -type
  • -exec



find /usr/ -name *.xml
find . -type d -name "*char*"
find . -name "*.sh" -exec ls -l {} \;

grep ⌘



grep some-word file
grep some-word -r directory

# use piping mostly
ps aux | grep some-word

sed ⌘

sed(意为流编辑器,源自英语“stream editor”的缩写)用来把文档或字符串里面的文字经过一系列编辑命令转换为另一种格式输出。

s(search) ⌘

# disable it
sudo sed -i s/^/#/ /etc/cron.d/roundcube

Regular Expression ⌘

^ 表示一行的开头。如:/^#/ 以#开头的匹配。
$ 表示一行的结尾。如:/}$/ 以}结尾的匹配。
\< 表示词首。 如 \<abc 表示以 abc 为首的詞。
\> 表示词尾。 如 abc\> 表示以 abc 結尾的詞。
. 表示任何单个字符。
* 表示某个字符出现了0次或多次。
[ ] 字符集合。 如:[abc]表示匹配a或b或c,还有[a-zA-Z]表示匹配所有的26个字符。如果其中有^表示反,如[^a]表示非a的字符

Strip Tags ⌘

# <b>This</b> is what <span style="text-decoration: underline;">I</span> meant. Understand?
sed 's/<.*>//g' html.txt <-- not working
sed 's/<[^>]*>//g' html.txt

N ⌘

sed 'N;s/my/your/' data.file
sed 'N;s/\n/,/' data.file

a(append),i(insert) ⌘

sed "1 i This is my monkey, my monkey's name is wukong" data.file 
sed "$ a This is my monkey, my monkey's name is wukong" data.file 
sed "/fish/a This is my monkey, my monkey's name is wukong" data.file 
sed "/my/a ----" data.file

c(replace),d(delete) ⌘

sed "2 c This is my monkey, my monkey's name is wukong" data.file
sed "/fish/c This is my monkey, my monkey's name is wukong" data.file
sed '/fish/d' data.file 
sed '2d' data.file 
sed '2,$d' data.file

awk ⌘

AWK(其名称得自于它的创始人阿尔佛雷德·艾侯、彼得·温伯格和布莱恩·柯林汉姓氏的首个字母)是一种优良的文本处理工具。

语言的基础是,只要在输入数据中有模式匹配,就执行一系列指令。该实用工具扫描文件中的每一行,查找与命令行中所给定内容相匹配的模式。如果发现匹配内容,则进行下一个编程步骤。如果找不到匹配内容,则继续处理下一行。

AWK提供了极其强大的功能:可以进行正则表达式的匹配,样式装入、流控制、数学运算符、进程控制语句甚至于内置的变量和函数。

Break into fields ⌘



# $1 is field #1, $2 is field #2, etc.

echo one two | awk '{print $1}'
# one

echo one two | awk '{print $2}'
# two

# But what is field #0 ($0)?
echo one two | awk '{print $0}'
# one two
# All the fields!

example ⌘



# backup all *.sh files
ls *.sh | awk '{print "cp "$0" "$0".bak"}' | bash
df -h | awk '{ print $2 " " $4 }'

# guess result
df -h | grep -v File | awk '{ print $5 }' | cut -d "%" -f1 -
 
ls */* | sort -t / -k 1,1R -k 2,2
 
awk '{print length, $0}' /etc/passwd | sort -n | cut -f2- -d' '

tar ⌘



tar cvf fil.tar.gz a
tar cvf fil.tar.gz 1 2 3
tar xvf fil.tar.gz

rpm ⌘



rpm -qa
rpm -qf package.rpm
rpm -i package.rpm

zip,unzip ⌘



zip
unzip

diff ⌘



diff -u ff1 ff2

exec ⌘



exec bash

getopts ⌘

  • The arguments passed from the command-line to the script must be preceded by a dash (-). It is the prefixed - that lets getopts recognize command-line arguments as options. In fact, getopts will not process arguments without the prefixed -, and will terminate option processing at the first argument encountered lacking them.
  • The getopts template differs slightly from the standard while loop, in that it lacks condition brackets.
  • The getopts construct is a highly functional replacement for the traditional getopt external command.



trap ⌘

Specifies an action on receipt of a signal; also useful for debugging.



trap '' 2
# Ignore interrupt 2 (Control-C), with no action specified. 

trap '' TSTP
# Ignore Ctrl-z ( exit can be used for disabling keystrokes )

trap 'echo "Control-C disabled."' 2
# Message when Control-C pressed.
trap '' 2  # Signal 2 is Control-C, now disabled.
command
command
command
trap 2     # Reenables Control-C


set ⌘

The set command enables options within a script.



#!/bin/bash
set -o verbose
# Echoes all commands before executing.

set -v
# Exact same effect as above.

set +v
# Command echoing off.
command
# Not echoed.

printf ⌘

  • Pretty echo

export ⌘

The export command makes available variables to all child processes of the running script or shell.



cat ~/.bash_profile

Some special characters ⌘

Comment (#) ⌘



# This line is a comment.


echo "A comment will follow." # Comment here.
#                            ^ Note whitespace before #

Command separator (;) ⌘



echo hello; echo there


if [ -x "$filename" ]; then    #  Note the space after the semicolon.
#+                   ^^
  echo "File $filename exists."; cp $filename $filename.bak
else   #                       ^^
  echo "File $filename not found."; touch $filename
fi; echo "File test complete."

;; in case ⌘



case "$variable" in
  abc)  echo "\$variable = abc" ;;
  xyz)  echo "\$variable = xyz" ;;
esac

dot (.) ⌘

source(类似一般程序语言中的include)



. ~/.bashrc # /etc/bashrc rc = run commands

. data-file    # Load a data file.
# Same effect as "source data-file", but more portable.

Redirection of input output (>, <, >>) ⌘

  • Input and output of a command may be redirected before it is executed
  • May be used to open and close files for the current shell execution environment
  • File descriptors: stdin(0), stdout(1) and stderr(2)




ls -l /dev/std*
ls -l /proc/self/fd/[0-2]

date
at 1020
ls -l /proc/self/fd > /var/tmp/fdtest.at
<CTRL+D>


Here Document ⌘



COMMAND <<InputComesFromHERE
...
...
...
InputComesFromHERE

Hello Example



#!/bin/bash
# Another 'cat' here document, using parameter substitution.

Another Example



#!/bin/bash

Redirecting Input - using < symbol ⌘





read p1 < header.txt
read p2 < body.txt
read p3 < footer.txt
echo $p1 $p2 $p3 >> hbf.txt

Redirecting output ⌘



# see stdout, stderr redirection examples

Redirection of input ⌘



read p1 < header.txt
read p2 < body.txt
read p3 < footer.txt
echo $p1 $p2 $p3

Piping - using | symbol ⌘

Output of one command becomes the input of a second




ls | grep Do
gunzip file.tar.gz | tar xvf -

Start the process in the background (&) ⌘



sleep 6 &
jobs

Variables and Parameters

Variable Assignment

#!/bin/bash

a=23              # Simple case
echo $a
b=$a
echo $b

# Now, getting a little bit fancier (command substitution).

a=`echo Hello!`   # Assigns result of 'echo' command to 'a' ...
echo $a
#  Note that including an exclamation mark (!) within a
#+ command substitution construct will not work from the command-line,
#+ since this triggers the Bash "history mechanism."
#  Inside a script, however, the history functions are disabled by default.

a=`ls -l`         # Assigns result of 'ls -l' command to 'a'
echo $a           # Unquoted, however, it removes tabs and newlines.
echo
echo "$a"         # The quoted variable preserves whitespace.

exit 0

Arithmetic Operation

#/bin/bash

let a=11
let "a = a + 5"
echo "a = "$a""

z=1
z=$(( z + 3 ))
echo "z = "$z""

n=0
(( n += 1 ))
echo "n = "$n""

Arrays

Newer versions of Bash support one-dimensional arrays.

Simple array

  • initialized with the variable[xx] notation
area2=( zero one two three four )
area[6]=`expr ${area[11]} + ${area[51]}`
echo ${area2[0]}

Declare array

  • a script may introduce the entire array by an explicit declare -a variable statement.
declare -a bigOne=( /home/jusfeel/shell-scripting/Day-1/* )

Dereference array

  • To dereference (retrieve the contents of) an array element, use curly bracket notation, that is, ${element[xx]}.
for f in ${bigOne[@]}
do
        echo $f
done


Destruction of variables (unset) ⌘



unset p1 p2 p2



#!/bin/bash

variable=hello                     
echo "variable = $variable"
unset variable

Variable with a null value ⌘



#!/bin/bash

echo "$uninitialized"                                # (blank line)
let "uninitialized += 5"                             # Add 5 to it.
echo "$uninitialized"                                # 5

Save the result to a variable $(...) ⌘

Command substitution



script_name=$(basename $0)
echo "The name of this script is $script_name."

# The $(...) form of command substitution treats a double backslash in a different way than `...`.
echo `echo \\`
echo $(echo \\)

Positional parameters ($0, $#, $1, $2, $3 ...) ⌘

  • Arguments passed to the script from the command line [1] : $0, $1, $2, $3 . . .
  • $0 is the name of the script itself, $1 is the first argument, $2 the second, $3 the third, and so forth.
  • After $9, the arguments must be enclosed in brackets, for example, ${10}, ${11}, ${12}



#!/bin/bash
# scriptname 1 2 3

MINPARAMS=3
echo "The number of parameters is \"$#\"."
echo "The name of this script is \"$0\"."
echo "The name of this script is \"`basename $0`\"."
echo
if [ -n "$1" ]
then
 echo "Parameter #1 is $1"
fi 

if [ -n "$2" ]
then
 echo "Parameter #2 is $2"
fi 

if [ -n "$3" ]
then
 echo "Parameter #3 is $3"
fi

$@ and $* ⌘

The special variables $* and $@ denote all the positional parameters.



#!/bin/bash
# Try multiple parameters with white spaces as well

for vars in $*
do
  echo "Looping \$*, \$vars is:"
  echo $vars
done

for vars in $@
do
  echo "Looping \$*, \$vars is:"
  echo $vars
done

exit 0

Manual shift ⌘



#!/bin/bash

if [[ $# -lt 3 ]]
then
	echo "Try give more than 3 args..."; 
	exit 1
fi
echo $*
shift 3
echo $*

Quoting. ⌘



#!/bin/bash

a=375
hello=$a

echo "$hello"    # 375
echo "${hello}"  # 375
echo
hello="A B  C   D"
echo $hello   # A B C D
echo "$hello" # A B  C   D
echo
echo '$hello'  # $hello

Escape character (\) ⌘



echo "This will print
as two lines."

echo "This will print \
as one line."

p=20
echo "\$p = $p"

Conditional statements ⌘

The if / then ⌘



if [ condition-true ]
then
   command 1
   command 2
   ...
fi

The if / then / else ⌘



if [ condition-true ]
then
   command 1
   command 2
   ...
else   
   command 3
   command 4
   ...
fi

One liner if,then ⌘

Keywords (or commands) begin statements, and before a new statement on the same line begins, the old one must terminate.



if [ -x "$filename" ]; then ...


What is truth ⌘



if [ 0 ]      # zero
then
  echo "0 is true."
else          # Or else ...
  echo "0 is false."
fi            # 0 is true.

Nesting if statements ⌘



a=3

if [ "$a" -gt 0 ]
then
  if [ "$a" -lt 5 ]
  then
    echo "The value of \"a\" lies somewhere between 0 and 5."
  fi
fi

# Same result as:

if [ "$a" -gt 0 ] && [ "$a" -lt 5 ]
then
  echo "The value of \"a\" lies somewhere between 0 and 5."
fi

Quoting variables being tested ⌘

  • A Good Practice



#!/bin/bash
# if-3.sh

if [[ -z "$1" ]]
then
  echo "Need one argument!"
fi

and list (1) ⌘



#!/bin/bash
# if-4.sh, compare to if-4-x.sh
# command-1 && command-2 && command-3 && ... command-n

if [ ! -z "$1" ] && echo "Argument #1 = $1" && [ ! -z "$2" ] && \
	echo "Argument #2 = "$2""
then
	echo "2 arguments passed to script."
else
	echo "Fewer than 2 arguments passed to script."
fi

exit $?

and list (2) ⌘



#!/bin/bash
# if-5.sh

ARGS=1        
E_BADARGS=85

[[ $# -ne "$ARGS" ]] && echo "Usage: $(basename $0) "$ARGS" argument(s)" && exit "$E_BADARGS"

echo "Correct number of arguments passed to this script."
exit 0


or list (1) ⌘



#!/bin/bash
# if-7.sh
# command-1 || command-2 || command-3 || ... command-n

E_BADARGS=85

[[ -z "$1" ]] && echo "Usage: `basename $0` filename" && exit $E_BADARGS  

file=$1
  
[ ! -f "$file" ] && echo "File \"$file\" not found. Refusing to delete a nonexistent file."
[ ! -f "$file" ] || (rm -f $file; echo "File \"$file\" deleted.")

exit $?

or list (2)



# /etc/rc.d/init.d/single
[ -x /usr/bin/clear ] && /usr/bin/clear

for i in /etc/rc1.d/S[0-9][0-9]* ; do
        [ -x "$i" ] || continue

        # Reject backup files and files generated by rpm.
        case "$1" in
                *.rpmsave|*.rpmorig|*.rpmnew|*~|*.orig)
                        continue;;
        esac
        [ "$i" = "/etc/rc1.d/S00single" ] && continue
  # ==> Set script name, but don't execute it yet.
        $i start
done

The if / elif ⌘



if [ condition1 ]
then
   command1
   command2
   command3
elif [ condition2 ]
# Same as else if
then
   command4
   command5
else
   default-command
fi

case statement ⌘



case "$variable" in 

 "$condition1" ) 
 command... 
 ;; 

 "$condition2" ) 
 command... 
 ;; 

esac

case



#!/bin/bash

case $( arch ) in   # $( arch ) returns machine architecture.
                    # Equivalent to 'uname -m' ...
  i386 ) echo "80386-based machine";;
  i486 ) echo "80486-based machine";;
  i586 ) echo "Pentium-based machine";;
  i686 ) echo "Pentium2+-based machine";;
  *    ) echo "Other type of machine";;
esac

exit 0

Operators ⌘

No floating point arithmetic ⌘



a=1.5

let "b = $a + 1.3"  # Error.

Operators and, or (&&, | |) ⌘

if [ $condition1 ] && [ $condition2 ]
#  Same as:  if [ $condition1 -a $condition2 ]

if [[ $condition1 && $condition2 ]] 

if [ $condition1 ] || [ $condition2 ]
# Same as:  if [ $condition1 -o $condition2 ]

if [[ $condition1 || $condition2 ]]

test command ⌘



if test -x $file
then
...

if [[ -x $file ]]
then
...

bash$ whereis [
: /usr/bin/[ /usr/share/man/man1/[.1.gz
bash$ type test
test is a shell builtin <---- no need to call /usr/bin/test

bash$ info test

Completing scrypt

The exit statement ⌘



exit $?

# status code: 64 - 113

E_NOARGS=85
E_NOTFOUND=86
E_NOTGZIP=87

Use the status of completion of the program in a script ($?) ⌘



# return last command status code
echo $?

Advanced Topics

Test operators

Test file ⌘

  • -e exists ( file or directory both true )
  • -s file not zero size

Test the file type ⌘

  • -f regular file ( not directory, or device file)
  • -d is directory

Test file attributes ⌘

  • -r file has read permission (for the user running the test)
  • -w file has write permission (for the user running the test)
  • -x file has execute permission (for the user running the test)

Operators ⌘

The assignment operator ⌘



# assignment
var=27
category=minerals

Arithmetic operators ⌘

let "z=5**3"    # 5 * 5 * 5
echo "z = $z"   # z = 125
bash$ expr 5 % 3 # 2

let "var += 5"
let "var -= 4"
let "var *= 5"
let "var /= 4"
let "var %= 4"

Arithmetic Expansion ⌘

Translating a string into a numerical expression backticks, double parentheses, or let.



z=`expr $z + 3`
z=$((z+3)) #    Not to be confused with command substitution. $()
let "z = z + 4"
echo $z

Operators operations on bits (bitwise operators) ⌘



<< # left shift 1 bit ( x2 )
<<= # left-shift-equal
let "var <<= 2" # results in var left-shifted 2 bits (multiplied by 4)
>> # bitwise right shift (divides by 2 for each shift position)
>>= # right-shift-equal (inverse of <<=)
& # bitwise AND
&= # bitwise AND-equal
| # bitwise OR
|= # bitwise OR-equal
~ # bitwise NOT
^ # bitwise XOR 异或
^= # bitwise XOR-equal

Logical operators ⌘



# ! NOT
if [ ! -f $FILENAME ]
then
  ...

# && AND
if [ $condition1 -a $condition2 ]
if [ $condition1 ] && [ $condition2 ]
if [[ $condition1 && $condition2 ]]    # Also works.
#  Note that && operator not permitted inside brackets of [ ... ] construct.

# || OR
if [ $condition1 -o $condition2 ]
if [ $condition1 ] || [ $condition2 ]
if [[ $condition1 || $condition2 ]]    # Also works.
#  Note that || operator not permitted inside brackets

Numeric constants ⌘



# 一般是十进制。
let "dec = 32"
echo "decimal number = $dec"             # 32

# 除非数字前面有
# "0" - 8进制
let "oct = 032"
echo "octal number = $oct"               # 26

# "0x" - 16进制
let "hex = 0x32"
echo "hexadecimal number = $hex"         # 50

# 含有"#" - BASE#NUMBER
let "bin = 2#111100111001101"
echo "binary number = $bin"              # 31181

Comparison operators

Comparing arithmetic using the command if ⌘



if [ "$a" -ne "$b" ]
then
  echo "$a is not equal to $b"
  echo "(arithmetic comparison)"
fi

Integer comparison operators ⌘



if [ "$a" -eq "$b" ]
if [ "$a" -ne "$b" ]
if [ "$a" -gt "$b" ]
if [ "$a" -ge "$b" ]
if [ "$a" -lt "$b" ]
if [ "$a" -le "$b" ]
(("$a" < "$b"))
(("$a" <= "$b"))
(("$a" > "$b"))
(("$a" >= "$b"))

Comparing strings ⌘

Always quote a tested string.



if [ "$a" = "$b" ]
if [ "$a" != "$b" ]
if [ -z "$String" ] # zero length (null)
if [ -n "$String" ] # not null

Comparing the complex (and, or) ⌘



if [ "$a" -eq 24 -a "$b" -eq 47 ]

if [ "$a" -eq 98 -o "$b" -eq 47 ]

if [[ "$v1" -gt "$v2" ]] || [[ "$v1" -lt "$v2" ]] && [[ -e "$filename" ]]

Functions



function function_name { 
command... 
} 

function_name () { 
command... 
}
  • Local variables
  • Without parameters
  • With parameters

Loop

The loop for / in ⌘



for arg in [list]
do 
 command(s)... 
done

for loop - 1 ⌘



for arg in "$var1" "$var2" "$var3" ... "$varN"  
# In pass 1 of the loop, arg = $var1	    
# In pass 2 of the loop, arg = $var2	    
# In pass 3 of the loop, arg = $var3	    
# ...
# In pass N of the loop, arg = $varN

# Arguments in [list] quoted to prevent possible word splitting.

= for - 2 ⌘



for arg in [list] ; do

= for - 3 ⌘



#!/bin/bash

for planet in Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto
do
  echo $planet  # Each planet on a separate line.
done

= for - 4 ⌘



#!/bin/bash

for planet in "Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune Pluto"
do
  echo $planet
done

exit 0

= for - 5 ⌘



#!/bin/bash

for planet in "Mercury 36" "Venus 67" "Earth 93"  "Mars 142" "Jupiter 483"
do
  set -- $planet  
  echo "$1		$2,000,000 miles from the sun"
done

exit 0

= for - 6 ⌘



#!/bin/bash

FILES="/usr/sbin/accept
/usr/bin/fakefile
/sbin/ypbind"    

for file in $FILES
do
  if [ ! -e "$file" ]      
  then
    echo "$file does not exist."; echo
    continue                
  fi
  ls -l $file | awk '{ print $8 "         file size: " $5 }'  # Print 2 fields.
  whatis `basename $file`   # File info.
  echo
done  

exit 0

= for - 7 ⌘



#!/bin/bash

filename="*txt"

for file in $filename
do
 echo "Contents of $file"
 echo "---"
 cat "$file"
 echo
done

The command seq ⌘

seq 5
seq 5 10
seq 5 2 9
seq -s ,5

The while loop ⌘



while [ condition ]
do 
 command(s)... 
done

Example 1 - Simple while loop ⌘



#!/bin/bash

var0=0
LIMIT=10

while [ "$var0" -lt "$LIMIT" ].
do
  echo -n "$var0 "
  let "var0 += 1"
done

Example 2 - Another while loop ⌘



#!/bin/bash

while [ "$var1" != "end" ]
do
  echo "Input variable #1 (end to exit) "
  read var1
  echo "variable #1 = $var1"
  echo
done  

exit 0

Example 3 - while loop with multiple conditions ⌘



#!/bin/bash
# while-2.sh

Example 4 - C-style syntax in a while loop ⌘



#!/bin/bash
# while-4.sh

LIMIT=10
((a = 1))

while (( a <= LIMIT ))   
do                       
  echo -n "$a "
  ((a += 1))             

done

Example 5 - call function in test condition ⌘



t=0

condition ()
{
  ((t++))
  if [ $t -lt 5 ]
  then
    return 0  # true
  else
    return 1  # false
  fi
}

while condition
do
  echo "Still going: t = $t"
done

Example 6 - reading file ⌘



#!/bin/bash

[ $# -lt 1 ] && echo "Usage: `basename $0` filename" && exit || filename=$1

cat $filename |   # Supply input from a file.
while read line   # As long as there is another line to read ...
do
  echo $line
done

Loop until ⌘



until [ condition-is-true ]
do 
 command(s)... 
done

Example 1 - until loop ⌘



#!/bin/bash

END_CONDITION=end

until [ "$var1" = "$END_CONDITION" ]
do
  echo "Input variable #1 "
  echo "($END_CONDITION to exit)"
  read var1
  echo "variable #1 = $var1"
  echo
done

Example 2 - C-style ⌘



#!/bin/bash

LIMIT=10
var=0

until (( var > LIMIT ))
do
  echo -n "$var "
  (( var++ ))
done

exit 0

Controlling the loop (break, continue) ⌘

Break



#!/bin/bash

LIMIT=20
a=0

while [ "$a" -le "$LIMIT" ]
do
 a=$(($a+1))

 if [ "$a" -gt 2 ]
 then
   break  # Skip entire rest of loop.
 fi

 echo -n "$a "
done

Continue



#!/bin/bash

LIMIT=19
echo "Printing Numbers 1 through 20 (but not 3 and 11)."

a=0
while [ $a -le "$LIMIT" ]
do
 a=$(($a+1))
 if [ "$a" -eq 3 ] || [ "$a" -eq 11 ]
 then
   continue
 fi
 echo -n "$a "
done 

# Exercise:
# Why does the loop print up to 20?

Regular Expressions (RE) ⌘

Commonly used in scripts, such as grep, sed and awk. As of version 3, Bash has acquired its own RE-match operator: =~.

Know RE ⌘

  • A character set
  • An anchor - ^, $
  • Modifiers - *, (), \, []

*,.,^,$ ⌘

  • "1133*" : 113, 1133, 1133333...

. ⌘

  • "13." : 1133, 11333

*,.,^,$ ⌘

  • "^" : line start

^,$ ⌘

  • "$" : line end "XXX$"
  • "^$": empty line

[...] ⌘

  • "[xyz]"
  • "[c-n]"
  • "[B-Pk-y]"
  • "[a-z0-9]"
  • "[^b-d]"
  • "[Yy][Ee][Ss]" : yes, Yes, YES, yEs ..
  • "[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]" : NNN-NN-NNNN

\ ⌘

  • "\$"
  • "\\"

\<\> ⌘

word boundary

"\<the\>" matches "the," but not "them," "there," "other," etc.


Extended RE + ⌘

+ ⌘

"+" matches one or more of previous RE



echo a111b | sed -ne '/a1\+b/p'
echo a111b | grep 'a1\+b'

\{\} ⌘

number of occurrences



# 5 digits exact
echo -e "23\n2345\n23413\n234" | grep "[0-9]\{5\}"



if [[ $input =~ ^[0-9]{3}-[0-9]{2}-[0-9]{4}$ ]]

(),| ⌘

  • () group REs
  • | alternatives



echo -e "read\nreed\nraad" | grep -E 'r(ee|aa)d'


Text Processing

Command head, tail ⌘



head -n 
tail -n 
tail -f

Sort, Uniq ⌘

Sort ⌘



cat /etc/passwd | head | sort
cat /etc/passwd | head | sort -r
cat /etc/passwd | head | sort -t: -k3
cat /etc/passwd | head | sort -t: -k3 -n

Uniq ⌘



sort -k5 messages.dat | cut -d ' ' -f '5' | uniq -c | sort -n -r

Cut, Paste, Join, Tr ⌘

Cut Paste ⌘



cat /etc/passwd | cut -d: -f1
cat /etc/passwd | cut -d: -f1 > paste1.dat 
cat /etc/passwd | cut -d: -f5
cat /etc/passwd | cut -d: -f5 > paste2.data 
paste paste1.dat paste2.data

ps -ely | cut -d ' ' -f '1 2' --complement
ps -ef | tail -n 10 | tr -s ' ' | cut -d ' ' -f '8'

Join ⌘



cat join-1.data | head -n 2
# memcached Memcached daemon
# mysql MySQL Server
cat join-2.data | head -n 2
# memcached /var/run/memcached
# mysql /var/lib/mysql
join join-1.data join-2.data | head -n 2
# memcached Memcached daemon /var/run/memcached
# mysql MySQL Server /var/lib/mysql

Tr ⌘



tr "*" "%" < translate.data
ps -ely | tail -n 10 | tr -s ' ' # --squeeze-repeats

Text search (grep)



grep -r . "text-to-search"

Interactive programs

Read command ⌘



read var
echo $var
read var1 var2 var3
echo $var{1..3}

User select ⌘



select variable [in list]
do 
 command... 
 break 
done

PS1-4 ⌘



export PS1="\u@\h \w> "

export PS2="continue-> "

PS3='Choose your favorite vegetable: '

export PS4='$0.$LINENO+ '

If the script does not work? (debugging)

Set Trap ⌘



  • set -x

Trap

#!/bin/bash
# traptest.sh

trap "echo Booh!" SIGINT SIGTERM # man 7 signal , kill -l
echo "pid is $$"

while :			# This is the same as "while true".
do
        sleep 60	# This script is not really doing anything.
done

References

Executing Commands

Special Shell Variables

Test Operators - Files

Miscellaneous Constructs

Regular Expression

Comparison of Command Shells

Exercises

1. Prepare alias for this command:
find /etc/ -name apache


2. Create script 'generator':
- it should add '#!/bin/bash' to the file
- change permission (x)	
- echo message "Done."


3. Extend our generator:
- it should take as an argument the name of the file
- pass as a second argument name of the catalog in which you want to save the file


4. Improve our generator:
- add usage info with if statement


5. Change the generator:
- test how many variables user provided
- allow to provide 1 or 2, but no 0 arguments


6. Add menu to our generator


7. Create a variable, which will be an array with names of our scripts:
- echo all elements from this array
- add one more element to this array, but do it in a smart way(check how long this array is)
- echo only last element from this array


8. Prepare even smarter version of generator:
- replace repetitive code with a function
- create sources.sh file and place the function there


9. Create script 'howmany' which will use two options:
- '-a' will count how many different users are logged in
- '-b' will show how many cores/processors is available on server