Bash Programming
Bash Programming
Bash Programming Training Materials
Copyright Notice
Copyright © 2004-2023 by NobleProg Limited All rights reserved.
This publication is protected by copyright, and permission must be obtained from the publisher prior to any prohibited reproduction, storage in a retrieval system, or transmission in any form or by any means, electronic, mechanical, photocopying, recording, or likewise.
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
- Bash can run them; reverse might not be true.
- POSIX (Portable Operating System Interface for Unix)
- 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
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