Shell 编程 基础速查1
参考网上一系列shell基础讲解的博客。
1. 零、shell中的内部变量:
1. $?: 表示shell命令的返回值.
2. $$: 表示当前shell的pid.
3. $!: 最后一个放入后台作业的PID值.
4. $0: 表示脚本的名字.
5. $1--$9,${10}: 表示脚本的第一到九个参数,和第十个参数.
6. $#: 表示参数的个数.
7. $*,$@: 表示所有的参数.
两者的区别如下: //都是双引号惹的祸^-^
/> set 'apple pie' pears peaches
/> for i in $*
> do
> echo $i
> done
apple
pie
pears
peaches
/> set 'apple pie' pears peaches
/> for i in $@
> do
> echo $i
> done
apple
pie
pears
peaches
/> set 'apple pie' pears peaches
/> for i in "$*"
> do
> echo $i
> done
apple pie pears peaches
/> set 'apple pie' pears peaches
/> for i in "$@"
> do
> echo $i
> done
apple pie //这里的单引号将两个单词合成一个.
pears
peaches
2. 读取用户变量:
read命令是用于从终端或者文件中读取输入的内建命令,read命令读取整行输入,每行末尾的换行符不被读入。在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY。下面的列表给出了read命令的常用方式:
| 命令格式 | 描述 |
| read answer | 从标准输入读取输入并赋值给变量answer。 |
| read first last | 从标准输入读取输入到第一个空格或者回车,将输入的第一个单词放到变量first中,并将该行其他的输入放在变量last中。 |
| read | 从标准输入读取一行并赋值给特定变量REPLY。 |
| read -a arrayname | 把单词清单读入arrayname的数组里。 |
| read -p prompt | 打印提示,等待输入,并将输入存储在REPLY中。 |
| read -r line | 允许输入包含反斜杠。 |
见下面的示例(绿色高亮部分的文本为控制台手工输入信息):
/> read answer #等待读取输入,直到回车后表示输入完毕,并将输入赋值给变量answer
Hello #控制台输入Hello
/> echo $answer #打印变量
Hello
#等待一组输入,每个单词之间使用空格隔开,直到回车结束,并分别将单词依次赋值给这三个读入变量。
/> read one two three
1 2 3 #在控制台输入1 2 3,它们之间用空格隔开。
/> echo "one = $one, two = $two, three = $three"
one = 1, two = 2, three = 3
/> read #等待控制台输入,并将结果赋值给特定内置变量REPLY。
This is REPLY #在控制台输入该行。
/> echo $REPLY #打印输出特定内置变量REPLY,以确认是否被正确赋值。
This is REPLY
/> read -p "Enter your name: " #输出"Enter your name: "文本提示,同时等待输入,并将结果赋值给REPLY。
Enter you name: stephen #在提示文本之后输入stephen
/> echo $REPLY
stephen
#等待控制台输入,并将输入信息视为数组,赋值给数组变量friends,输入信息用空格隔开数组的每个元素
/> read -a friends
Tim Tom Helen
/> echo "I have ${#friends} friends"
I have 3 friends
/> echo "They are ${friends[0]}, ${friends[1]} and ${friends[2]}."
They are Tim, Tom and Helen.
3. 状态判断:
test是Shell中提供的内置命令,主要用于状态的检验,如果结果为0,表示成功,否则表示失败。见如下示例:
/> name=stephen
/> test $name != stephen
/> echo $?
1
需要注意的是test命令不支持Shell中提供的各种通配符,如:
/> test $name = [Ss]tephen
/> echo $?
1
test命令还可以中括号予以替换,其语义保持不变,如:
/> [ $name = stephen ]
/> echo $?
0
在Shell中还提供了另外一种用于状态判断的方式:[[ expr ]],和test不同的是,该方式中的表达式支持通配符,如:
/> name=stephen
/> [[ $name == [Ss]tephen ]]
/> echo $?
0
#在[[ expression ]]中,expression可以包含&&(逻辑与)和||(逻辑或)。
/> [[ $name == [Ss]tephen && $friend == "Jose" ]]
/> echo $?
1
/> shopt -s extglob #打开Shell的扩展匹配模式。
/> name=Tommy
# "[Tt]o+(m)y"的含义为,以T或t开头,后面跟着一个o,再跟着一个或者多个m,最后以一个y结尾。
/> [[ $name == [Tt]o+(m)y ]]
/> echo $?
0
在Shell中还提供了let命令的判断方式: (( expr )),该方式的expr部分,和C语言提供的表达式规则一致,如:
/> x=2
/> y=3
/> (( x > 2 ))
/> echo $?
1
/> (( x < 2 ))
/> echo $?
0
/> (( x == 2 && y == 3 ))
/> echo $?
0
/> (( x > 2 || y < 3 ))
/> echo $?
1
下面的表格是test命令支持的操作符:
| 判断操作符 | 判断为真的条件 |
| 字符串判断 | |
| [ stringA=stringB ] | stringA等于stringB |
| [ stringA==stringB ] | stringA等于stringB |
| [ stringA!=stringB ] | stringA不等于stringB |
| [ string ] | string不为空 |
| [ -z string ] | string长度为0 |
| [ -n string ] | string长度不为0 |
| 逻辑判断 | |
| [ stringA -a stringB ] | stringA和stringB都是真 |
| [ stringA -o stringB ] | stringA或stringB是真 |
| [ !string ] | string不为真 |
| 逻辑判断(复合判断) | |
| [[ pattern1 && pattern2 ]] | pattern1和pattern2都是真 |
| [[ pattern1 || pattern2 ] | pattern1或pattern2是真 |
| [[ !pattern ]] | pattern不为真 |
| 整数判断 | |
| [ intA -eq intB ] | intA等于intB |
| [ intA -ne intB ] | intA不等于intB |
| [ intA -gt intB ] | intA大于intB |
| [ intA -ge intB ] | intA大于等于intB |
| [ intA -lt intB ] | intA小于intB |
| [ intA -le intB ] | intA小于等于intB |
| 文件判断中的二进制操作 | |
| [ fileA -nt fileB ] | fileA比fileB新 |
| [ fileA -ot fileB ] | fileA比fileB旧 |
| [ fileA -ef fileB ] | fileA和fileB有相同的设备或者inode值 |
| 文件检验 | |
| [ -d $file ] or [[ -d $file ]] | file为目录且存在时为真 |
| [ -e $file ] or [[ -e $file ]] | file为文件且存在时为真 |
| [ -f $file ] or [[ -f $file ]] | file为非目录普通文件存在时为真 |
| [ -s $file ] or [[ -s $file ]] | file文件存在, 且长度不为0时为真 |
| [ -L $file ] or [[ -L $file ]] | file为链接符且存在时为真 |
| [ -r $file ] or [[ -r $file ]] | file文件存在且可读时为真 |
| [ -w $file ] or [[ -w $file ]] | file文件存在且可写时为真 |
| [ -x $file ] or [[ -x $file ]] | file文件存在且可执行时为真 |
注:在逻辑判断(复合判读中),pattern可以包含元字符,在字符串的判断中,pattern2必须被包含在引号中。
let命令支持的操作符和C语言中支持的操作符完全相同,如:
+,-,*,/,% 加,减,乘,除,去模
>>,<< 右移和左移
>=,<=,==,!= 大于等于,小于等于,等于,不等于
&,|,^ 按位与,或,非
&&,||,! 逻辑与,逻辑或和取反
还有其含义和C语言等同的快捷操作符,如=,*=,/=,%=,+=,-=,<<=,>>=,&=,|=,^=。
4. 流程控制语句:
if语句格式如下:
#if语句的后面是Shell命令,如果该命令执行成功返回0,则执行then后面的命令。
if command
then
command
command
fi
#用test命令测试其后面expression的结果,如果为真,则执行then后面的命令。
if test expression
then
command
fi
#下面的格式和test expression等同
if [ string/numeric expression ]
then
command
fi
#下面的两种格式也可以用于判断语句的条件表达式,而且它们也是目前比较常用的两种。
if [[ string expression ]]
then
command
fi
if (( numeric expression )) #let表达式
then
command
fi
见如下示例:
/> cat > test1.sh #从命令行直接编辑test1.sh文件。
echo -e "Are you OK(y/n)? \c"
read answer
#这里的$answer变量必须要用双引号扩住,否则判断将失败。当变量$answer等于y或Y时,支持下面的echo命令。
if [ "$answer" = y -o "$answer" = Y ]
then
echo "Glad to see it."
fi
CTRL+D
/> . ./test1.sh
Are you OK(y/n)? y
Glad to see it.
上面的判断还可以替换为:
/> cat > test2.sh
echo -e "Are you OK(y/n or Maybe)? \c"
read answer
# [[ ]]复合命令操作符允许其中的表达式包含元字符,这里输入以y或Y开头的任意单词,或Maybe都执行then后面的echo。
if [[ $answer == [yY]* || $answer = Maybe ]]
then
echo "Glad to hear it.
fi
CTRL+D
/> . ./test2.sh
Are you OK(y/n or Maybe)? yes
Glad to hear it.
下面的例子将使用Shell中的扩展通配模式。
/> shopt -s extglob #打开该扩展模式
/> answer="not really"
/> if [[ $answer = [Nn]o?( way |t really) ]]
> then
> echo "I am sorry."
> fi
I am sorry.
对于本示例中的扩展通配符,这里需要给出一个具体的解释。[Nn]o匹配No或no,?( way|t really)则表示0个或1个( way或t really),因此answer变量匹配的字符串为No、no、Not really、not really、No way、no way。
下面的示例使用了let命令操作符,如:
/> cat > test3.sh
if (( $# != 2 )) #等同于 [ $# -ne 2 ]
then
echo "Usage: $0 arg1 arg2" 1>&2
exit 1 #exit退出值为0-255之间,只有0表示成功。
fi
if (( $1 < 0 || $1 > 30 )) #等同于 [ $1 -lt 0 -o $1 -gt 30 ]
then
echo "arg1 is out of range."
exit 2
fi
if (( $2 <= 20 )) #等同于 [ $2 -le 20 ]
then
echo "arg2 is out of range."
fi
CTRL+D
/> sh ./test3.sh
Usage: ./test3.sh arg1 arg2
/> echo $? #Shell脚本的退出值为exit的参数值。
1
/> sh ./test3.sh 40 30
arg1 is out of range.
/> echo $?
2
下面的示例为如何在if的条件表达式中检验空变量:
/> cat > test4.sh
if [ "$name" = "" ] #双引号就表示空字符串。
then
echo "name is null."
fi
CTRL+D
/> . ./test4.sh
name is null.
if/elif/else语句的使用方式和if语句极为相似,相信有编程经验的人都不会陌生,这里就不在赘述了,其格式如下:
if command
then
command
elif command
then
command
else
command
fi
见如下示例脚本:
/> cat > test5.sh
echo -e "How old are you? \c"
read age
if [ $age -lt 0 -o $age -gt 120 ] #等同于 (( age < 0 || age > 120 ))
then
echo "You are so old."
elif [ $age -ge 0 -a $age -le 12 ] #等同于 (( age >= 0 && age <= 12 ))
then
echo "You are child."
elif [ $age -ge 13 -a $age -le 19 ] #等同于 (( age >= 13 && age <= 19 ))
then
echo "You are 13--19 years old."
elif [ $age -ge 20 -a $age -le 29 ] #等同于 (( age >= 20 && age <= 29 ))
then
echo "You are 20--29 years old."
elif [ $age -ge 30 -a $age -le 39 ] #等同于 (( age >= 30 && age <= 39 ))
then
echo "You are 30--39 years old."
else
echo "You are above 40."
fi
CTRL+D
/> . ./test5.sh
How old are you? 50
You are above 40.
case语句格式如下:
case variable in
value1)
command
;; #相同于C语言中case语句内的break。
value2)
command
;;
*) #相同于C语言中switch语句内的default
command
;;
esac
见如下示例脚本:
/> cat > test6.sh
#!/bin/sh
echo -n "Choose a color: "
read color
case "$color" in
[Bb]l??)
echo "you select blue color."
;;
[Gg]ree*)
echo "you select green color."
;;
red|orange)
echo "you select red or orange."
;;
*)
echo "you select other color."
;;
esac
echo "Out of case command."
/> . ./test6.sh
Choose a color: green
you select green color.
Out of case command.
5. 循环语句:
Bash Shell中主要提供了三种循环方式:for、while和until。
for循环声明格式:
for variable in word_list
do
command
done
见如下示例脚本:
/> cat > test7.sh
for score in math english physics chemist #for将循环读取in后面的单词列表,类似于Java的for-each。
do
echo "score = $score"
done
echo "out of for loop"
CTRL+D
/> . ./test7.sh
score = math
score = english
score = physics
score = chemist
out of for loop
/> cat > mylist #构造数据文件
tom
patty
ann
jake
CTRL+D
/> cat > test8.sh
#!/bin/sh
for person in $(cat mylist) #for将循环读取cat mylist命令的执行结果。
do
echo "person = $person"
done
echo "out of for loop."
CTRL+D
/> . ./test8.sh
person = tom
person = patty
person = ann
person = jake
out of for loop.
/> cat > test9.sh
for file in test[1-8].sh #for将读取test1-test8,后缀为.sh的文件
do
if [ -f $file ] #判断文件在当前目录是否存在。
then
echo "$file exists."
fi
done
CTRL+D
/> . ./test9.sh
test2.sh exists.
test3.sh exists.
test4.sh exists.
test5.sh exists.
test6.sh exists.
test7.sh exists.
test8.sh exists.
/> cat > test10.sh
for name in $* #读取脚本的命令行参数数组,还可以写成for name的简化形式。
do
echo "Hi, $name"
done
CTRL+D
/> . ./test10.sh stephen ann
Hi, stephen
Hi, ann
while循环声明格式:
while command #如果command命令的执行结果为0,或条件判断为真时,执行循环体内的命令。
do
command
done
见如下示例脚本:
/> cat > test1.sh
num=0
while (( num < 10 )) #等同于 [ $num -lt 10 ]
do
echo -n "$num "
let num+=1
done
echo -e "\nHere's out of loop."
CTRL+D
/> . ./test1.sh
0 1 2 3 4 5 6 7 8 9
Here's out of loop.
/> cat > test2.sh
go=start
echo Type q to quit.
while [[ -n $go ]] #等同于[ -n "$go" ],如使用该风格,$go需要被双引号括起。
do
echo -n How are you.
read word
if [[ $word == [Qq] ]] #等同于[ "$word" = Q -o "$word" = q ]
then
echo Bye.
go= #将go变量的值置空。
fi
done
CTRL+D
/> . ./test2.sh
How are you. Hi
How are you. q
Bye.
until循环声明格式:
until command #其判断条件和while正好相反,即command返回非0,或条件为假时执行循环体内的命令。
do
command
done
见如下示例脚本:
/> cat > test3.sh
until who | grep stephen #循环体内的命令将被执行,直到stephen登录,即grep命令的返回值为0时才退出循环。
do
sleep 1
echo "Stephen still doesn't login."
done
CTRL+D
shift命令声明格式:shift [n]
shift命令用来把脚本的位置参数列表向左移动指定的位数(n),如果shift没有参数,则将参数列表向左移动一位。一旦移位发生,被移出列表的参数就被永远删除了。通常在while循环中,shift用来读取列表中的参数变量。
见如下示例脚本:
/> set stephen ann sheryl mark #设置4个参数变量。
/> shift #向左移动参数列表一次,将stephen移出参数列表。
/> echo $*
ann sheryl mark
/> shift 2 #继续向左移动两位,将sheryl和ann移出参数列表
/> echo $*
mark
/> shift 2 #继续向左移动两位,由于参数列表中只有mark了,因此本次移动失败。
/> echo $*
mark
/> cat > test4.sh
while (( $# > 0 )) #等同于 [ $# -gt 0 ]
do
echo $*
shift
done
CTRL+D
/> . ./test4.sh a b c d e
a b c d e
b c d e
c d e
d e
e
break命令声明格式:break [n]
和C语言不同的是,Shell中break命令携带一个参数,即可以指定退出循环的层数。如果没有指定,其行为和C语言一样,即退出最内层循环。如果指定循环的层数,则退出指定层数的循环体。如果有3层嵌套循环,其中最外层的为1,中间的为2,最里面的是3。
见如下示例脚本:
/> cat > test5.sh
while true
do
echo -n "Are you ready to move on?"
read answer
if [[ $answer == [Yy] ]]
then
break
else
echo "Come on."
fi
done
echo "Here we are."
CTRL+D
/> . ./test5.sh
Are you ready to move on? y
Here we are
continue命令声明格式:continue [n]
和C语言不同的是,Shell中continue命令携带一个参数,即可以跳转到指定层级的循环顶部。如果没有指定,其行为和C语言一样,即跳转到最内层循环的顶部。如果指定循环的层数,则跳转到指定层级循环的顶部。如果有3层嵌套循环,其中最外层的为3,中间的为2,最里面的是1。
/> cat maillist #测试数据文件maillist的内容为以下信息。
stephen
ann
sheryl
mark
/> cat > test6.sh
for name in $(cat maillist)
do
if [[ $name == stephen ]]; then
continue
else
echo "Hello, $name."
fi
done
CTRL+D
/> . ./test6.sh
Hello, ann.
Hello, sheryl.
Hello, mark.
I/O重新定向和子Shell:
文件中的输入可以通过管道重新定向给一个循环,输出也可以通过管道重新定向给一个文件。Shell启动一个子Shell来处理I/O重新定向和管道。在循环终止时,循环内部定义的任何变量对于脚本的其他部分来说都是不看见的。
/> cat > demodata #为下面的脚本构造册数数据
abc
def
ghi
CRTL+D
/> cat > test7.sh
if (( $# < 1 )) #如果脚本参数的数量小于1,则给出错误提示后退出。
then
echo "Usage: $0 filename " >&2
exit 1
fi
count=1
cat $1 | while read line #参数一中的文件被cat命令输出后,通过管道逐行输出给while read line。
do
let $((count == 1)) && echo "Processing file $1..." > /dev/tty #该行的echo将输出到当前终端窗口。
echo -e "$count\t$line" #将输出行号和文件中该行的内容,中间用制表符隔开。
let count+=1
done > outfile #将while循环中所有的输出,除了>/dev/tty之外,其它的全部输出到outfile文件。
CTRL+D
/> . ./test7.sh demodata #只有一行输出,其余的都输出到outfile中了。
Processing file demodata...
/> cat outfile
1 abc
2 def
3 ghi
/> cat > test8.sh
for i in 9 7 2 3 5 4
do
echo $i
done | sort -n #直接将echo的输出通过管道重定向sort命令。
CTRL+D
/> . ./test8.sh
2
3
4
5
7
9
IFS和循环:
Shell的内部域分隔符可以是空格、制表符和换行符。它可以作为命令的分隔符用在例如read、set和for等命令中。如果在列表中使用不同的分隔符,用户可以自己定义这个符号。在修改之前将IFS原始符号的值保存在另外一个变量中,这样在需要的时候还可以还原。
见如下示例脚本:
/> cat > test9.sh
names=Stephen:Ann:Sheryl:John #names变量包含的值用冒号分隔。
oldifs=$IFS #保留原有IFS到oldifs变量,便于后面的还原。
IFS=":"
for friends in $names #这是遍历以冒号分隔的names变量值。
do
echo Hi $friends
done
IFS=$oldifs #将IFS还原为原有的值。
set Jerry Tom Angela
for classmates in $* #再以原有IFS的值变量参数列表。
do
echo Hello $classmates
done
CTRL+D
/> . ./test9.sh
Hi Stephen
Hi Ann
Hi Sheryl
Hi John
Hello Jerry
Hello Tom
Hello Angela
6. 函数:
Shell中函数的职能以及优势和C语言或其它开发语言基本相同,只是语法格式上的一些差异。下面是Shell中使用函数的一些基本规则:
1) 函数在使用前必须定义。
2) 函数在当前环境下运行,它和调用它的脚本共享变量,并通过位置参量传递参数。而该位置参量将仅限于该函数,不会影响到脚本的其它地方。
3) 通过local函数可以在函数内建立本地变量,该变量在出了函数的作用域之后将不在有效。
4) 函数中调用exit,也将退出整个脚本。
5) 函数中的return命令返回函数中最后一个命令的退出状态或给定的参数值,该参数值的范围是0-256之间。如果没有return命令,函数将返回最后一个Shell的退出值。
6) 如果函数保存在其它文件中,就必须通过source或dot命令把它们装入当前脚本。
7) 函数可以递归。
8) 将函数从Shell中清空需要执行:unset -f function_name。
9) 将函数输出到子Shell需要执行:export -f function_name。
10) 可以像捕捉Shell命令的返回值一样获取函数的返回值,如$(function_name)。
Shell中函数的声明格式如下:
function function_name { command; command; }
见如下示例脚本:
/> cat > test1.sh
function increment() { #定义函数increment。
local sum #定义本地变量sum。
let "sum=$1+1"
return $sum #返回值是sum的值。
}
echo -n "The num is "
increment 5 #increment函数调用。
echo $? #输出increment函数的返回值。
CTRL+D
/> . ./test1.sh
The num is 6
BASH SHELL编程:
1. 初始化顺序: /etc/profile ( ~/.bash_profile | ~/.bash_login | ~/.profile ) ~/.bashrc
2. set -o allexport 当前shell变量对其所有子shell都有效.
set +o allexport 当前shell变量对其所有子shell都无效.
set -o noclobber 重定向输出时,如果输出文件已经存在则提示输出失败, date > out; date > out, 第二次操作失败
set +o noclobber 缺省shell行为. date > out; date > out, 第二次操作成功
shopt -s extglob 使用扩展通配符,如 abc?(2|9)K, abc*([0-9]), abc+([0-9]), no@(thing|body), no!(thing|body)
其中?,*,+,@和!都是用于修饰后面的()的.
3. 变量声明: declare, 在赋值的时候等号的两边不需要空格. variable=value.
declare -r variable=value 声明只读变量.
declare -x variable=value 相当于export variable=value.
数组声明:
declare -a variable=(1 2 3 4)
or
name=(tom tim helen)
or
x[0]=5
x[4]=10
数组的声明可以不是连续的, 这一点和awk中的数组比较类似.
e.g.
/> declare -a friends
/> friends=(sheryl peter louise)
/> echo ${friends[0]}
sheryl
/> echo ${friends[1]}
peter
/> echo ${friends[2]}
louise
/> echo ${friends[*]}
shery1 peter louise
/> echo ${#friends[*]}
3
unset friends
/> declare -a states=(ME [3]=CA [2]=CT)
/> echo ${states[*]}
ME CA CT
/> echo ${#states[*]}
3
/> echo ${states[0]}
ME
/> echo ${states[1]}
/> echo ${states[2]}
CT
/> echo ${states[3]}
CA
unset states
4. 函数声明:
function greeting
{
echo "Hi $1 and $2";
}
/> greeting tom joe
Hi tom and joe
unset -f greeting
5. printf其参数类型类似于awk的printf.
6. 变量扩展修改符:
${variable:+word}
if (NULL != variable)
echo word
else
echo $variable
e.g.
/> unset var_name
/> var_name=
/> echo ${var_name:+AA}
/> var_name=BB
/> echo ${var_name:+AA}
AA
/> echo $var_name
BB
${variable:-word}
if (NULL == variable)
echo word
else
echo $variable
e.g.
/> unset var_name
/> var_name=
/> echo ${var_name:-AA}
/> AA
/> var_name=BB
/> echo ${var_name:-AA}
BB
/> echo $var_name
BB
${variable:=word}
if (NULL == variable)
{
variable=word
echo word
}
else
{
echo $variable
}
e.g.
/> unset var_name
/> echo ${var_name:=AA}
AA
/> echo $var_name
AA
/> echo ${var_name:=CC}
AA
/> echo $var_name
AA
${variable:offset}/${variable:offset:length}
e.g.
/> var_name=notebook
/> echo ${var_name:0:4}
/> note
/> echo ${var_name:4:4}
/> book
/> echo ${var_name:2}
/> tebook
/> echo ${var_name:0}
/> notebook
${variable%pattern} 从variable尾部开始,最小化的删除pattern
e.g.
/> variable="/usr/bin/local/bin"
/> echo ${variable%/bin*}
/usr/bin/local
${variable%%pattern} 从variable尾部开始,最大化的删除pattern
/> variable="/usr/bin/local/bin"
/> echo ${variable%%/bin*}
/usr
${variable#pattern} 从variable头部开始,最小化的删除pattern
/> variable="/home/lilliput/jake/.bashrc
/> echo ${variable#/home}
/lilliput/jake/.bashrc
${variable##pattern} 从variable头部开始,最大化的删除pattern
/> variable="/home/lilliput/jake/.bashrc
/> echo ${variable##*/}
.bashrc
${#pattern} 返回patter的字符数量.
/> variable=abc123
6
7. 引用:
\: 可以史shell中的元字符无效, 如?, < >, \, $, *, [ ], |, ( ), ;, &, { }
': 单引号可以史其内的所有元字符无效, 也包括\.
": 双引号也可以史其内的所有元字符无效, 变量和命令替换除外. 如 echo "What's time? $(date)", 这里date命令将被执行.
Shell中提供三种引用字符,分别是:反斜杠、单引号和双引号,它们可以使Shell中所有元字符失去其特殊功能,而还原其本意。见以下元字符列表:
| 元字符 | 描述 |
| ; | 命令分隔符 |
| & | 后台处理Shell命令 |
| () | 命令组,创建一个子Shell |
| {} | 命令组,但是不创建子Shell |
| | | 管道 |
| < > | 输入输出重定向 |
| $ | 变量前缀 |
| *[]? | 用于文件名扩展的Shell通配符 |
注:单引号和双引号唯一的区别就是,双引号内可以包含变量和命令替换,而单引号则不会解释这些,见如下示例:
/> name=Stephen
/> echo "Hi $name, I'm glad to meet you! " #name变量被替换
Hi Stephen, I'm glad to meet you!
/> echo 'Hi $name, I am glad to meet you! ' #name变量没有被替换
Hi $name, I am glad to meet you!
/> echo "Hey $name, the time is $(date)" #name变量和date命令均被替换
Hey Stephen, the time is Fri Nov 18 16:27:31 CST 2011
/> echo 'Hey $name, the time is $(date)'
Hey $name, the time is $(date) #name变量和date命令均未被替换
8. 命令替换:
variable=$(date) or variable=`date`
variable=`basename \`pwd\`` or variable=$(basename $(pwd)) 命令替换可以嵌套, 第一种方法中,嵌套的命令必须使用\进行转义.
eval: 可以进行命令行求值操作.
e.g.
/> set a b c d
/> echo The last argument is \$$#
/> The last argument is $4
/> eval echo The last argument is \$$#
/> The last argument is d
9. 数学计算:
/> echo $[5+4-2]
7
/> echo $[5+2*3]
11
/> echo $((5+4-2))
7
/> echo $((5+2*3))
11
/> declare -i num //必须声明-i, 以表示整型变量.
/> num=5+5
/> echo $num
10 //如果没有声明declare -i, 则返回5+5.
/> num=5 + 5
-bash: + 5: command not found.
/> num="5 + 5"
/> echo $num
10
/> num=4*6
/> echo $num
24
使用不同进制(2~36)表示数字.
/> declare -i x=017
/> echo $x
15
/> x=2#101
/> echo $x
5
/> x=8#17
/> echo $x
15
/> x=16#b
/> echo $x
11
let专门用于数学运算的bash内置命令.
/> let i=5
/> let i=i+1
/> echo $i
6
/> let "i = i + 2"
/> echo $i
8
/> let "i+=1"
/> echo $i
9
10. 读取用户输入(read)命令:
/> read answer
yes
/> echo "$answer is the right response."
yes is the right reponse.
/> read first middle last
Jon Jake Jones
/> echo "Hello $first"
Hello Jon
/> read //如果没有变量时, $REPLY是缺省变量.
the Chico Nut factory
/> echo "I guess $REPLY keeps you busy!"
I guess the Chico Nut factory keeps you busy!
/> read -p "Enter your job titile: "
Enter your job title: Accountant
/> echo "I thought you might be an $REPLY".
I thought you might be an accountant.
/> read -a friends
Melvin Tim Ernest
/> echo "Say hi to ${friends[2]}
Say hi to Ernest.
/> echo "Say hi to ${friends[$[${#friends[*]}-1]]}"
Say hi to Ernest.
11. 条件结构和流控制:
1) 条件判断方法: test, [ ], [[ ]], (( )), 当$?为0时表示成功和true, 否则失败和false.
/> name=tom
/> test $name != tom
/> echo $?
1 //Failure
/> [ $name=Tom ]
/> echo $?
0
/> [ $name = [Tt]?? ] //[]和test不允许使用通配符.
/> echo $?
1
/> x=5
/> y=20
/> [ $x -gt $y ]
/> echo $?
1
/> [ $x -le $y ]
/> echo $?
0
/> name=Tom
/> friend=Joseph
/> [[ $name == [Tt]om ]]
/> echo $?
0
/> [[ $name == [Tt]om && $friend == "Jose" ]]
/> echo $?
1
/> x=2
/> y=3
/> (( x > 2))
/> echo $?
1
/> (( x < 2))
/> echo $?
1
/> (( x == 2 && y == 3))
/> echo $?
0
2) test命令操作符:
字符串判断:
[ string1 = string2 ] or 两个字符串相等时返回true.
[ string1 == string2 ]
[ string1 != string2 ] 两个字符串不等时返回true.
[ string ] string非空时返回true.
[ -z string ] 为空时返回true.
[ -n string ] 为非空时返回true.
逻辑判断:(cond1可以包含元字符)
[ string1 -a string2 ] or string1和string2都非空时
[[ cond1 && cond2 ]] cond1和cond2都为true
[ string1 -o string2 ] or string1或string2为非空时
[[ cond1 || cond2 ]] cond1或cond2为true.
[ ! string ] or string为空
[[ !cond ]] cond为false.
整数判断:
[ int1 -eq int2 ] int1等于int2
[ int1 -ne int2 ] int1不等于int2
[ int1 -gt int2 ] int1大于int2
[ int1 -ge int2 ] int1大于等于int2
[ int1 -lt int2 ] int1小于int2
[ int1 -le int2 ] int1小于等于int2
文件判断
[ file1 -nt file2 ] file1比file2新
[ file1 -ot file2 ] file1比file2旧
文件检验:
[ -d $file ] or 表示判断目录是否存在
[[ -d $file ]]
[ -e $file ] or 表示判断文件是否存在
[[ -e $file ]]
[ -f $file ] or 表示判断非目录普通文件是否存在
[[ -f $file ]]
[ -s $file ] or 表示判断文件存在, 并且非0.
[[ -s $file ]]
[ -L $file ] or 表示判断为链接符号存在
[[ -L $file ]]
[ -r $file ] or 表示判断文件存在, 并且可读.
[[ -r $file ]]
[ -w $file ] or 表示判断文件存在, 并且可写.
[[ -w $file ]]
[ -x $file ] or 表示判断文件存在, 并且可执行.
[[ -x $file ]]
3) if语句:
if command //当command的返回状态为0时执行then后面的语句.
then
command
fi
if test expr
then
command
fi
if [ string/numeric expr ]
then
command
fi
一下两种为new format, 建议使用.
if [[ string expr ]] //支持通配符和扩展通配符(shopt -s extglob)
then
command
fi
if (( numeric expr ))
then
command
fi
if command
then
command1
else
command2
fi
if command
then
command1
elif command
then
command2
elif command
then
command3
else
command4
fi
4) 空语句:
用冒号表示.
if expr
then
:
fi
5) case语句: //;;相当于c语言中break.
case variable in
value1 | value2) // value1支持通配符和|作为or的关系
command1
;;
value3)
command2
;;
[vV]alue4)
command3
;;
*)
command4
;;
esac
6) 循环语句:
IFS 变量表示缺省的分隔符whitespace, tab, newline等. 可以自行修改该值, 但是建议再改之前赋值给其他变量作为备份, 有利于再恢复到缺省值.
for variable in word_list
do
commands
done
e.g.
for pal in Tom Dick Harry Joe
do
echo "Hi $pal"
done
echo "Out of loop"
for file in testfile[1-5]
do
if [[ -f $file ]]
then
echo "$file exist"
fi
done
for name in $* // 等同于 for name
do
echo "Hi $name"
done
while command //这里当条件为true的时候, 或者command的退出为0时执行循环体的命令.
do
commands
done
while (( $num < 10 ))
do
echo -n "$num"
let num+=1
done
until !command //这里当条件为false的时候, 或者command的退出为非0时执行循环体的命令.
do
commands
done
break/continue: 功能等同于c语言中的break和continue.
重定向循环和后台执行的循环:
while command
do
command
done > file //重定向到文件
while command
do
command
done | command //重定向到其他程序的管道
while command
do
command
done & //后台执行.
shift [n]命令: 用于把脚本的参数列表移出指定的位数, 不指定为左移一位, 一旦发生位移, 被移出的参数将永远被删除.
e.g.
[scriptname: doit]
while (( $# > 0 ))
do
echo $*
shift
done
/> doit a b c d e
a b c d e
b c d e
c d e
d e
e
12. 特殊文件: /dev/null和/dev/tty
Linux系统提供了两个对Shell编程非常有用的特殊文件,/dev/null和/dev/tty。其中/dev/null将会丢掉所有写入它的数据,换句换说,当程序将数据写入到此文件时,会认为它已经成功完成写入数据的操作,但实际上什么事都没有做。如果你需要的是命令的退出状态,而非它的输出,此功能会非常有用,见如下Shell代码:
/> vi test_dev_null.sh
#!/bin/bash
if grep hello TestFile > /dev/null
then
echo "Found"
else
echo "NOT Found"
fi
在vi中保存并退出后执行以下命令:
/> chmod +x test_dev_null.sh #使该文件成为可执行文件
/> cat > TestFile
hello my friend
CTRL + D #退出命令行文件编辑状态
/> ./test_dev_null.sh
Found #这里并没有输出grep命令的执行结果。
将以上Shell脚本做如下修改:
/> vi test_dev_null.sh
#!/bin/bash
if grep hello TestFile
then
echo "Found"
else
echo "NOT Found"
fi
在vi中保存退出后,再次执行该脚本:
/> ./test_dev_null.sh
hello my friend #grep命令的执行结果被输出了。
Found
下面我们再来看/dev/tty的用途。当程序打开此文件是,Linux会自动将它重定向到一个终端窗口,因此该文件对于读取人工输入时特别有用。见如下Shell代码:
/> vi test_dev_tty.sh
#!/bin/bash
printf "Enter new password: " #提示输入
stty -echo #关闭自动打印输入字符的功能
read password < /dev/tty #读取密码
printf "\nEnter again: " #换行后提示再输入一次
read password2 < /dev/tty #再读取一次以确认
printf "\n" #换行
stty echo #记着打开自动打印输入字符的功能
echo "Password = " $password #输出读入变量
echo "Password2 = " $password2
echo "All Done"
在vi中保存并退出后执行以下命令:
/> chmod +x test_dev_tty.sh #使该文件成为可执行文件
/> ./test_dev_tty
Enter new password: #这里密码的输入被读入到脚本中的password变量
Enter again: #这里密码的输入被读入到脚本中的password2变量
Password = hello
Password2 = hello
All Done
13. 简单的命令跟踪:
Linux Shell提供了两种方式来跟踪Shell脚本中的命令,以帮助我们准确的定位程序中存在的问题。下面的代码为第一种方式,该方式会将Shell脚本中所有被执行的命令打印到终端,并在命令前加"+":加号的后面还跟着一个空格。
/> cat > trace_all_command.sh
who | wc -l #这两条Shell命令将输出当前Linux服务器登录的用户数量
CTRL + D #退出命令行文件编辑状态
/> chmod +x trace_all_command.sh
/> sh -x ./trace_all_command.sh #Shell执行器的-x选项将打开脚本的执行跟踪功能。
+ wc -l #被跟踪的两条Shell命令
+ who
2 #实际输出结果。
Linux Shell提供的另一种方式可以只打印部分被执行的Shell命令,该方法在调试较为复杂的脚本时,显得尤为有用。
/> cat > trace_patial_command.sh
#! /bin/bash
set -x #从该命令之后打开跟踪功能
echo 1st echo #将被打印输出的Shell命令
set +x #该Shell命令也将被打印输出,然而在该命令被执行之后,所有的命令将不再打印输出
echo 2nd echo #该Shell命令将不再被打印输出。
CTRL + D #退出命令行文件编辑状态
/> chmod +x trace_patial_command.sh
/> ./trace_patial_command.sh
+ echo 1st echo
1st echo
+ set +x
2nd echo
14. 重定向:
下面的列表为Shell中支持的重新定向操作符。
| 操作符 | 功能 |
| < | 重新定向输入 |
| > | 重新定向输出 |
| >> | 追加输出 |
| 2> | 重新定向错误 |
| &> | 重新定向错误和输出 |
| >& | 重新定向错误和输出 |
| 2>&1 | 重新定向错误到标准输出 |
| 1>&2 | 重新定向标准输出到错误 |
| >| | 重新定向输出的时候覆盖noclobber选项 |
在操作系统中,0,1,2分别表示输入/输出流,含义如下:
0- 标准输入,即C中的stdin,或者C++中的cin,或者Java中的System.in
1- 标准输出,即C中的stdout,或者C++中的cout,或者Java中的System.out
2- 错误输出,即C中的stderr,或者C++中的cerr,或者Java中的System.err
2>&1表示将错误输出重定向到标准输出流中,即将标准输出和错误输出都重定向到一个文件中。
myrun.sh > run.log 2>&1
#find命令将搜索结果输出到foundit文件,把错误信息输出到/dev/null
/> find . -name "*.c" -print > foundit 2> /dev/null
#将find命令的搜索结果和错误信息均输出到foundit文件中。
/> find . -name "*.c" -print >& foundit
#同上。
/> find . -name "*.c" -print > foundit 2>&1
#echo命令先将错误输出到errfile,再把信息发送到标准错误,该信息标准错误与标准输出合并在一起(errfile中)。
/> echo "File needs an argument" 2> errfile 1>&2
/> cat errfile
File needs an argument
一、正则表达式在vi中的用法:
1. ^: 如/^love,表示所有以love开头的行.
2. $: 如/love$,表示所有以love结尾的行.
3. .: 如/l..e, dot表示任意字符,如love,l22e,live等.
4. *: 如/*love, *表示0多多个字符,这里表示love前面可以有0个多任意多个空格字符,如/go*gle,可以表示ggle,gogle,google,goooooooogle.
5. []: 如/[Ll]ove,[]中的任意一个字符都可能成为候选者,如Love和love.
6. [x-y]: 如/[A-Z]t, 表示[]中指定范围内的字符都可能成为候选者,如At, It等, 也可表示多个区间段如:[a-zA-TV-Z]表示所有除V之外的所有大小写英文字符.
7. [^]: 如/[^A-Z]ove,表示A-Z之内的任意字符都是非法的, 如Love,Dove等.
8. \: 转义符, 如果想表示任何meta字符的原义, 需使用在meta字符前加转义符\, 如\.将只表示dot,而不能在表示任何其他字符了.
9. \<: 如/\<love, 表示任何单词的开始, 如love和lover, 但是glove将非法.
10. \>: 如/love\>, 表示任何单词的结束, 如love和glove, 但是lover将非法.
11. \(..\): 如/\(love\)able/\1rs/, 这里的\1表示love, 这种标签替代最多达到\9, 该例子表示用lovers代替loveable.
12. x\{m\}: 如x\{5\}, 表示x被重复5次,如xxxxx.
13. x\{m,\}: 如x\{5,\}, 表示x被至少重复5次,如xxxxx,xxxxxxxx.
14. x\{m,n\}:如x\{5,10\}, 表示x被重复5-10次,如xxxxx,xxxxxxxx.
以下为grep的正则表示式用法:
15. \w和\W: 等同于[a-zA-Z0-9].
16. \b: 等同于\<和\>,均表示单词的边界.
以下为grep的正则表示式的扩展用法(grep -E或egrep):
17. +: 如/lo+ve, +表示1个或者多个先前的字符,这里表示love,loove,但是lve非法.
18. ?: 如/lo?ve, ?表示0个或者1个先前的字符, 这里只表示love和lve.
19. (a|b|c): 如/l(o|i)ve, 表示或的意思,这里表示love和live. (o|i)和[oi]的主要区别就是(word|word)可以表示单词之间或的关系,[]只能表示字符.
20. x{m},x{m,},x{m,n} 等同于grep普通模式中的x\{m\},x\{m,\},x\{m,n\}.
二、grep家族:
1. 家族成员:
egrep: 执行带有扩展正则表达式元字符的grep搜索.
fgrep: 将关闭grep的所有正则功能, 即搜索字符串中所有正则元字符都将只是表示其字符本意.
2. 返回值:
0: 表示成功
1: 表示搜索字符串不存在
2: 表示搜索文件不存在.
3. grep的选项规则:
-#,-A#和-B#: 表示在输出匹配内容的时候同时也输出其上下指定数量的行数, 如grep -2 "love" *, 该例输出匹配love的上下两行,
grep -A2 "love" * 该例输出匹配love的后两行, grep -B2 "love" * 该例输出匹配love的前两行. 这里A表示after,B表示before.
-F: 等同于fgrep, 这个选项将关闭所有正则功能,即所有正则的元字符均表示其本身含义.
-c: 不输出找到的内容,只是输出在该文件中有多少匹配的行数.
-h: 不输出匹配搜索字符串的文件的文件名,只是输出内容.
-i: 搜索时忽略大小写.
-l: 只显示匹配搜索内容的文件名, 不显示具体的内容.
-L: 只显示没有包含搜索内容的文件名.
-n: 输出匹配内容的同时也输出其所在的行号.
-v: 反向搜索,输出不匹配搜索字符串的行.
-w:只打印以完整单词形式匹配的行, 如果该搜索字符为某个单词的部分内容,将不会被输出.
-x: 只打印以行形式匹配的行, 如果该搜索字符为行的部分内容,将不会被输出.
-q: 不会输出任何信息, 该选项主要用于测试某个搜索字符或搜索pattern在执行grep命令之后的返回值.
-r: 表示递归的搜索当前目录的子目录中的文件.
4. 对于普通模式的grep,如果搜索的字符中普通字符前面加入\,则该字符按照扩展grep(egrep或者grep -E)的正则规则进行查找.如grep "love\|live" filename,
将等同于egrep "love|live" filename,这里的\|将按照egrep中的|元字符处理, 再如, egrep "3+" filename等同于grep "3\+" filename.
三、sed:
1. sed命令:
,: 表示范围.
1) sed -n '/west/,/east/p' datafile 表示打印所有从包含west开始到包含east的行,如果直到文件的结尾都没有包含east的行,将打印west后面的所有行.
其实逻辑很简单, 就是sed在发现包含west行之后开发打印该行,直到发现包含east的行打印才结束,否则一直打印直到文件的末尾.
2) sed -n '5,/^northeast/p' datafile 表示从第五行开始打印,直到遇到以northeast开始的行结束打印.
!: 表示对匹配结果取反.
1) sed '/north/!d' datafile 将删除所有不包含north的行.
a: 追加命令.
1) sed '/^north/a first line \
second line \
third line' datafile 将会在所有包含north行的后面追加first line \r second line \n third line. 其中\表示下一行还有内容的连词. 如果是c-shell:
sed '/^north/a first line \\
second line \\
third line' datafile 其中多出来的\是转义符.
d: 表示删除.
1) sed '/north/d' datafile 将删除所有包含north的行.
2) sed '3d' datafile 将删除第三行.
3) sed '3,$d' datafile 将删除第三行到文件的结尾行.
4) sed 'd' datafile 将删除所有行.
e: 表示多点编辑.
1) sed -e '1,3d' -e 's/Hemenway/Jones/' datafile 一个sed语句执行多条编辑命令, 因此命令的顺序会影响其最终结果.
2) sed -e 's/Hemenway/Jones/' -e 's/Jones/Max/' datafile 先用Jones替换Hemenway, 再用Max替换Jones.
h和g/G: 保持和获取命令.
1) sed -e '/northeast/h' -e '$G' datafile sed将把所有包含northeast的行轮流缓存到其内部缓冲区, 最后将只是保留最后一个匹配的行,
$G是将缓冲区的行输出到$G匹配行的后面, 该例表示将最后一个包含northeast的行追加到文件的末尾.
2) sed -e '/WE/{h; d;}' -e '/CT/{G;}' datafile 表示将包含WE的行保存到缓冲区, 然后删除该行,最后将缓冲区中保存的那份输出到CT行的后面.
3) sed -e '/northeast/h' -e '$g' datafile 表示将包含northeast的行保存到缓冲区, 再将缓冲区中保存的那份替换文件的最后一行并输出.
再与h合用时, g表示替换, G表示追加到匹配行后面.
4) sed -e '/WE/{h; d;}' -e '/CT/{g;}' datafile 保留包含WE的行到缓冲区, 如果有新的匹配行出现将会替换上一个存在缓冲区中的行, 如果此时发现有
包含CT的行出现, 就用缓冲区中的当前行替换这个匹配CT的行, 之后如果有新的WE出现, 将会用该新行替换缓冲区中数据, 当前再次遇到CT的时候,将用最
新的缓冲区数据替换该CT行.
i: 表示插入.
1) sed '/north/i first line \
second line \
third line' datafile 其规则和a命令基本相同, 只是a是将额外的信息输出到匹配行的后面, i是将额外信息输出到匹配行的前面.
p: 表示打印.
1) sed '/north/p' datafile 将打印所有包含north的行.
2) sed '3p' datafile 将打印第三行.
3) sed '3,$p' datafile 将打印第三行到文件的结尾行.
4) sed 'p' datafile 将打印所有行.
注: 使用p的时候sed将会输出指定打印的行和所有行, 当其与-n选项组合时候,将只是打印输出匹配的行.
n: 下一行命令.
1) sed '/north/ {n; s/Chin/Joseph/}' datafile 将先定位包含north的行, 然后取其下一行作为目标行, 再在该目标行上执行s/Chin/Joseph/的替换操作.
2) sed '/north/ {n; n; s/Chin/Joseph/}' datafile 将取north包含行的后两行作为目标行.
注: {}作为嵌入的脚本执行.
q: 退出命令.
1) sed '5q' datafile 到第五行退出(输出第五行).
2) sed '/north/q' datafile 输出到包含north的行退出(输出包含north的行).
3) sed '/Lewis/ {s/Lewis/Joseph/; q}' datafile 将先定位包含Lewis的行, 然后用Joseph替换Lewis,最后退出sed操作.
r: 文件读入.
1) sed '/Suan/r newfile' datafile 在输出时,将newfile的文件内容跟随在datafile中包含Suan的行后面输出,如果多行都包含Suan,则文件被多次输出.
s: 表示替换.
1) sed 's/west/north/g' datafile 将所有west替换为north, g表示如果一行之内多次出现west,将全部替换, 如果没有g命令,将只是替换该行的第一个匹配.
2) sed -n 's/^west/north/p' datafile 将所有以west开头的行替换为north, 同时只是输出替换匹配的行.
3) sed -n '1,5 s/\(Mar\)got/\1ianne/p' datafile 将从第一行到第五行中所有的Margot替换为Marianne, \1是\(Mar\)的变量替代符.
w: 文件写入.
1) sed -n '/north/w newfile2' datafile 将datafile中所有包含north的行都写入到newfile2中.
x: 互换命令.
1) sed -e '/pat/h' -e '/Margot/x' datafile x命令表示当定位到包含Margot行,互换缓冲区和该匹配Margot行的数据, 即缓冲区中的数据替换该匹配行显示,
该匹配行进入缓冲区, 如果在交换时缓冲区是空, 则该匹配行被换入缓冲区, 空行将替换该行显示, 后面依此类推. 如果交换后, 再次出现匹配pat的行, 该
行将仍然会按照h命令的规则替换(不是交换, 交换只是发生在发现匹配Margot的时候)缓冲区中的数据.
y: 变形命令.
1) sed '1,3y/abcd/ABCD/' datafile 将1到3行中的小写abcd对应者替换为ABCD,注意abcd和ABCD是一一对应的. 如果他们的长度不匹配,sed将报错.
2) sed 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' datafile 将datafile中所有的小写字符替换为大写字母.
四、awk家族:
1. 执行方式:
1) awk 'pattern' filename 如awk '/Mary/' employees
2) awk '{action}' filename 如awk '{print $1}' employees
3) awk 'pattern {action}' filename 如awk '/Mary/ {print $1}' employees
注: 模式/Mary/对action的作用范围是从其后面的第一个左花括号开始,到第一个右花括号结束. 其后的pattern将不会影响前面的action.
2. 内置变量:
$0: 表示一整行(相当于数据库中一条记录).
NR: 当前行号.
NF: 当前记录的域(相当于数据库中的字段)数量
RS: 行分隔符(缺省为回车).
FS: 域分隔符,缺省为\t. awk -F: '{print $1,$2,$3}' employees 这里FS等于":".
OFS:输出域分隔符, awk -F: '{print $1,$2,$3}' employees 这里OFS等于" "空格, 因为在$1和$2之间是空格分开的.
ARGC: 命令行参数的数量.
ARGV: 命令行参数数组.
ENVIRON: 从shell传递来的包含当前环境变量的数组.
ERRNO: 错误号.
FILENAME: 当前的输入文件名.
3. 格式化输出:
转义码:
\b: Backspace.
\n: 换行.
\r: 回车.
\t: 制表符.
格式化说明符:
%c: 单个ASCII字符.
%d: 十进制数字.
%e: 科学记数法表示的数字.
%f: 浮点数.
%o: 八进制数字.
%s: 打印字符串.
%x: 十六进制数字.
-: 表示左对齐,如%-15d, 在十进制数字的后面会有一些空格,同时该数字是左对齐的. %+15d或%15d表示右对齐,当数字不足15位的时候.
#: 如%#o或%#x, 会在八进制的数字前面加入0,十六进制前加0x.
4. 操作符:
~: 匹配运算符. 如awk '$1~/Mary/' employees, 表示第一个域($1)中包含Mary的被打印, 如果其他域包含,第一个域没有,则仍然视为无效.
!~: 不匹配运算符. 如awk '$1!~/Mary/' employees, 表示第一个域($1)中不包含Mary的被打印, 如果其他域包含,第一个域没有,则仍然视为有效.
<,>,<=,>=,!=,==: 关系运算符. awk '$3>5000 {print $3}' datafile
cond ? expr1 : expr2 条件表达式 awk '{max = $1 > $2 ? $1 : $2; print max}' datafile
=,+=,-=,*=,/=,%=: 赋值运算符.
-,+,*,/,%,^(x^y[乘方]): 数学运算符.
&&, ||, !: 逻辑运算符.
,: 表示范围, awk '/Tom/,/Mary/' datafile 其规则可参照sed中逗号运算符.
5. 选项:
-F: 指定特定的分隔符,而不是缺省的\t, 如-F:,这里分隔符是":".
6. awk编程:
1) BEGIN: 其后紧跟着动作块, 该块将会在任何输入文件被读入之前执行, 如一些初始化工作, 或者打印一些输出标题.
awk 'BEGIN{FS=":"; OFS="\t";ORS="\n\n"} {print $1,$2,$3}' file
即使输入文件不存在, BEGIN块动作仍然会被执行.
2) END: 其后也紧随动作块, 该动作模块将在整个输入文件处理完毕之后被处理, 但是END需要有文件名的输入.
awk 'END {print "The end\n"} filename.
3) 输入输出重新定向:
awk 'BEGIN {print "Hello" > "newfile"}' datafile 文件名一定要用双引号扩起来, > 如果文件存在,则清空后重写新文件.
awk 'BEGIN {print "Hello" >> "newfile"}' datafile 文件名一定要用双引号扩起来, > 如果文件存在, 则在文件末尾追加写入.
awk 'BEGIN {getline name < "/dev/tty"; print name}' getline是awk的内置函数, 就像c语言的gets, 将输入赋值给name变量.
4) system函数可以执行shell中的命令,这些命令必须用双引号扩起.
awk 'END { system("clear"); system ("cat " FILENAME)}' filename
5) 条件语句:
if (expr) { stat; } else { stat; }
if (expr) { stat; } else if { stat; } else { stat; }
awk '{ if ($7 <= 2) { print "less than 2", $7 } else if ($7 <= 4) { print "less than 4", $7 } else { print "the others", $7 } }' datafile
6) 循环语句:
while (expr) { stat; }
for (i = 1; i <= NF; i++) { stat; }
break;
continue;
exit(exitcode); awk 将退出. 退出后的$?将会是这里的exitcode.
next; 读取下一条记录. awk '{ if ($7 == 3) { next } else { print $0 }}' datafile 将不会输出$7等于3的记录.
7) 数组:
awk的数组和pl/sql中数组有些类似, 都是通过哈希表来实现的,其下标可以是数字, 也可以是字符串.
awk '{name[x++]=$3};END{for(i = 0; i < NR; i++) { print i, name[i]}}' employees
awk '{id[NR]=$3};END{for (x = 1; x <= NR; x++) { print id[x]} }' employees
awk '/^Tom/{name[NR]=$1}; END{for (i in name) { print name[i]}}' employees 特殊的for语句
awk '/Tom/{count["tom"]++}; /Mary/{count["mary"]++}; END{print "count[tom] = ",count["tom"]; print "count[mary] = ", count["mary"]}' employees
awk '{count[$2]++};END{for (name in count) {print name,count[name]}}' datafile 域变量也可以作为数组的下标.
7. 内置函数:
1) sub/gsub(regexp, substitution string, [target string]); gsub和sub的差别是sub只是替换每条记录中第一个匹配正则的, gsub则替换该记录中所有匹配
正则的, 就是vi中s/src/dest/ 和s/src/dest/g的区别, 如果target string没有输入, 其缺省值是$0.
awk '{sub(/Tom/,"Thomas"); print}' employees
awk '{sub(/Tom/,"Thomas",$1); print}' employees
awk '{gsub(/Tom/,"Thomas"); print}' employees
awk '{gsub(/Tom/,"Thomas",$1); print}' employees
2) index(string ,substring) 返回子字符串第一次被匹配的位置(1开始)
awk 'BEGIN{print index("hollow", "low") }'
3) length(string) 返回字符串的长度.
awk 'BEGIN{print length("hello")}'
4) substr(string, starting position, [length])
awk 'BEGIN{print substr("Santa Claus",7,6)}'
awk 'BEGIN{print substr("Santa Claus",7)}'
5) match(string, regexp) 返回正则表示在string中的位置, 没有定位返回0
awk 'BEGIN{print match("Good ole USA",/[A-Z]+$/)}'
6) toupper(string)和tolower(string) 仅仅gawk有效.
awk 'BEGIN{print toupper("linux"), tolower("BASH")}'
7) split(string, array, [field seperator]) 如果不输入field seperator, FS内置变量作为其缺省值.
awk 'BEGIN{split("12/24/99",date,"/"); for (i in date) {print date[i]} }'
8) variable = sprintf(format, ...) 和printf的最大区别就是他返回格式化后的字符串.
awk '{line = sprintf("%-15s %6.2f ",$5,$6); print line}' datafile
9) systime() 返回1970/1/1到当前时间的整秒数.
10) variable = strftime(format, [timestamp])
11) 数学函数: atan2(x,y), cos(x), exp(x)[求幂], int(x)[求整数], log(x), rand()[随机数], sin(x), sqrt(x), srand(x)

浙公网安备 33010602011771号