Unix Shell Scripting Advanced chapter2 -chapter7
时间:2007-08-06 来源:yixiaoyun
chapter2
echo 让echo后不换行可以用-n参数 不能起作用的时候可以用 "string\c"
重定向
>
错误信息重定向
2>
正常信息和错误信息输出到同一个文件
2>&1
example : backup > a.out 2>&1
不需要的信息可以重定向到
/dev/null
后台运行程序(一般运行程序如果logout程序就会终止,可以用此命令保证程序持续运行)
nohup programname & 特殊字符
1 \ 可以使特殊字符失去其意义
2 " 双引号可以把一些包括在其中的特殊字符的特殊意义去掉如#。但是对$无效果
3 ' 单引号可以把除了单引号以外的所有特殊字符的特殊意义去掉 chapter3 4种方式在命令行下运行脚本(脚本必须有执行权限) 1 myscript
直接输入脚本名称(脚本内运行的变量在脚本运行结束后脚本内变量的内容清空)
2 sh myscript
此时myscript不需要由执行权限
3 . myscript
使用当前shell运行脚本中的命令(不重新调用一个shell,在script里定义的变量都可以使用,不会因为shell不同而无法共享变量)
4 exec myscript
结束当前的shell,建立一个新的shell执行脚本。当script执行完毕的时候,用户退出
vi使用
:! 运行单独的unix命令(包括正在被编辑的脚本)可以用在正在编辑的脚本的测试
如:! myscript :!! 可以再重复运行刚才运行的命令 % 运行正在编辑的脚本时,可以用%来替代当前脚本的名字
如 :!chmod +x %
给当前正在编辑的文件增加执行权限
PATH和bin
1一般都把script放在一个目录里,大多数都放在home目录下的bin文件夹下
2再把这个目录加入PATH中。这样登陆时在任何目录下都可以运行script了。 script interpreters
可以运行script的程序
bourne shell
cshell
sed 字符串编辑器
awk 文本处理器
perl 强大的文本处理器
tcl
还有许多其它种类的 script interpreters 工作过程 1 找到文件并打开
2 检查文件的开头的字符决定是那种类型的可执行文件 (二进制或者脚本)
3 如果是个脚本(并且没有定义用那种interpreter执行),shell会尝试去解释和运 行脚本
4 让shell分辨使用哪种script interpreters可以通过在script第一行定义使用哪个
#!/bin/csh
or#!/usr/local/bin/perl
这项设置对于CGI programming是绝对基础要求 使用type perl 可以找到perl程序的执行文件的路径
如 type perl
结果 perl is /usr/local/bin/perl CGI scripts
bsh 可以用在CGI scripts web programming
CGI 程序是一个用来提供互动,动画或者数据库联接到一个web站点的程序 一个CGI程序可以是任何一种程序,包括bourne shell scripts,perl scripts 需要的条件
1 必须是可以执行的
2 必须以#!/bin/sh 开头
3 脚本产生的输出必须可以被认出是一个合法的http流。这个要求标准输出开头有 类似Content-type:text/html #!/bin/sh
echo Content-type:text/plain
echo
ls /bin
chapter 4 shell variables
variable name要求
1 名字必须唯一
2 只包括a-z,A-Z,0-9和下划线
3 不能以数字开头,可以用字母或者下划线开头
4 变量名区分大小写 所有的shell变量是strings处理 ,不是numbers 。但是可以用expr进行运算 sport=basketball 等号左右不能有空格。
sport= "basketball football" 如果输入的值包含空格,必须用双引号包括起来 $sport 可以得到变量名的内容
echo I like $sport
Environment variables
1 HOME
2 PATH
3 LOGNAME
4 TERM terminal type 现在正在使用的、 变量内容可以被脚本改变,但是这个改变随脚本运行走,并不影响login shell的变量。除非脚本是用 . 点来运行 除非把新创建的变量加入到环境变量里,否则新的变量并不能被其他程序(包括脚本)使用 把一个变量加入到环境变量里使用export命令
month=January
export month
The Trouble with Quotes
unix shell 用3种不同的引用
1 单引号 '
2 双引号 "
3 反引号 `
1单引号
单引号可以把单引用以内的所有的特殊符号变为普通的符号。
可以用两个单引号把一个单引号包括起来,使其只作普通符号使用 2双引号
双引号可以允许一些特殊符号不变为普通符号
如 $ ` 还有其他的如 \"或者\$
echo "$LOGNAME made \$1000 in `date+%B`"
结果类似 peter made $1000 in November 单引号包括的内容会失去特殊意义,所以可以当作字符串使用,在find,grep可以用到
双引号包括的内容特殊符号可以保持特殊意义。应用更广泛
3反引号
任何包括在反引号内的文本会被当作命令看待,并且调用一个单独的shell执行
这个命令执行的输出结果代替反引号里的内容 list=`who|sort`
echo $list echo the date is `date +%m%d%y` today
the date is 08/22/01 today
echo the date is `date` today
the date is Wed Aug 22 16:38:42 EST 2001 today
Grouping Commands
使用命令的不同的行为
1 运行在后台
2 重定向输出到文件或者其他程序
3 等等 如果希望应用相同的行为到一些连贯的程序,可以把程序变成组,然后应用行为到组
$ (echo the date is date )>output_file
line control
可以在一行同时运行两个命令,但是要用;分开 echo This command is split \
over several lines
chapter5
Conditional Code
true
echo $?
false
echo $? bsh
echo $?
返回0表示true,程序正常退出。其他值有问题为false。
csh
echo $?
返回非0表示正常,为0表示false conditiional command execution 1 command1 && comand2 命令2只会在命令1正常完成(带一个exit status 0)时运行
如 ls file1 && cp file1 /tmp 只有在file1在ls列出才运行cp命令
cp abc xyz && echo the file was copied okay 2 command3 || comand4 命令4只会在命令3错误完成(带一个exit status not 0)时运行 如 diff filea fileb || echo the files are different
ls file2 || exit 这种命令结构也有一定限制
1 如果条件符合,只能运行一条命令(在有可能需要运行一组命令时不合适) 2 如果条件不符合,不能定义运行的第二个命令
if statement
格式 if command1
then
command2
command3
...
fi 例
if diff file1 file2 > /dev/null
then
echo the files are the same
rm file2
exit
fi
else clause
在if条件的命令执行结果返回exit status not 0时运行命令
即在给定的条件不符合时执行命令 else的格式
if command1
then
one set of commands
else
another set of commands
fi 例子
if diff file1 file2 > /dev/null
then
echo the files are the same
rm file2
else
echo the files are different!
echo please review the differences:
difff file1 file2
fi
: 表示什么也不做的命令 如
if ls file1 > /dev/null
then
: #什么也不做
else
echo the file does not exit - exiting
exit
fi the elif clause
当我们需要写一个超过两个互相排除选项的条件代码结构时用 elif即 else if
例
if command1
then
command set 1
elif command2
then
command set 2
else
command set 3
fi 例子
if ls $file > /dev/null 2>&1
then
echo sorry,the file already exists
elif who > $file
echo $file now contains the userlist
else
echo could not create $file
fi elif分支可以重复无穷次,但是else分支只能使用一次 using test
bourne shell 本身不支持比较。但是有另外一个程序test支持 如
var1=10
test $var1 = 20
echo $?
返回1 test没有输出,不能告诉你这个变量是否相等。但是test通过改变exit status来表示运算的结果,所以可以用echo $?来判断 test的主要目的是返回一个对条件测试的相应exit status。
这个exit status 与true和false的意义一致 test专门被设计用来和if语句一起使用 if test $var1 -gt $max
then
echo that value is too large
fi 使用test的注意事项
1 在test命令行里 $var1 = 20 ,必须在等号的左右有空格。(这与分配变量相反)2 如果变量还没有设置或者设置为空(x=),那么用test检查变量将会得到syntax error .这一点可以用双引号包括变量来解决。
test "$var1" = 20
但是返回的exit status 仍然为错误即非0。 test的选项
1 test value1 = value2
返回true(0)如果值相同
2 test value1 != value2
返回true(0)如果值不同
3 test value1 -gt value2
返回true(0)如果value1和value2都是整数值并且value1大于value2
相似的选项包括 -lt , -ge(greater than or equal to) ,-le
4 test value
返回true(0)如果值非空
5 test -z value
返回true(0)如果值是空(0长度,但不是没有)
其他选项-用来检查files和directories
6 test -f filename
返回true(0)如果给的文件存在并且是一个常规文件(如,非目录的,设备文件等)
7 test -d filename
返回true(0)如果给的文件存在并且是一个目录。 8 test -s filename
返回true(0)如果给定的文件或者目录存在并且大小超过0
9 test -r|w|x filename
返回true(0)如果给定的文件存在并且是读|写|执行(运行脚本的进程的读写权限)
下面的选项可以和上面的选项混合使用
10 test !expression
返回true(0)如果表达式被认为是false(expression是上面提到的选项的一种)
!符号读作 not
test ! -w /etc/passwd
echo $?
如果当前进程对passwd文件没有写权限则echo返回true(0)
11 test expression1 -a expression2
返回true(0)如果两个表达式都为true 如 echo "Please enter a filename: \c"
read fname
if test -z "$fname"
then
echo You did not enter a filename
elif test -f "$fname" -a -w "$fname"
then
echo Everything is fine
else
echo the file is not a writable file
fi 12 test expression1 -o expression2
返回true(0)如果两个表达式有一个是true(0) test有一个alias 是 [
如 if [$var1 -gt $max]
then
echo that value is too large
fi case statement
caset 用来解决条件在2,3个以上的代码
case 格式 case $var1 in
val1)
code for case 1
;;
val2)
code for case 2
;;
val3)
code for case 3
;;
esac ---
echo " please enter a rock group name: \c"
read rname case $rname in
Beatles)
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
esac echo $rname made $no_albums albums.The last one was called \"$last_album\" ---
Case 命令有一个类似if命令里的else的结构。即用*代替条件,作为所有分支都不满足的执行的命令 case $var1 in
val1)
code for case 1
;;
...
*)
code for any case that is not covered above
;;
esac ---
echo " please enter a rock group name: \c"
read rname case $rname in
Beatles)
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
*)
echo Unknown rock group
exit
;;
esac echo $rname made $no_albums albums.The last one was called\"$last_album\" ---
其他用法
1 当相同的代码可以在多种cases下执行时 case $var1 in
val1|val2|val3)
code for case 1-3
;;
... 2 也可以用通配符来匹配变量的值 case $var1 in
d*)
code for anything that starts with d
;;
... ---
echo " please enter a rock group name: \c"
read rname case $rname in
[bB]eatles|"The Beatles")
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
*)
echo Unknown rock group
exit
;;
esac echo $rname made $no_albums albums.The last one was called\"$last_album\"
--- 不像if语句,case 语句并不返回true/false值和exit statuses chapter6
While Loop
如果if语句的条件符合,可以执行一个代码段,while loop用同样的方法重复执行代码段 代码格式
while command
do
code block
done 给定的代码块会被重复的执行,直到给定的命令返回一个non-zero exit status 如 read answer
while [ "$answer" != chickens ]
do
echo That answer is incorrect
echo Please try again
read answer
done ---
echo "please enter the name of a directory : \c"
read dir while [ ! -d "$dir" ]
do
echo "$dir is not a directory"
echo "Please try again: \c"
read dir
done echo Congratulations: $dir is a directory! ---
实际上可能使用一个while loop 去运行命令输出的每一行
如
who | while read user term time
do
echo $user has been on $term since $time
done break and continue这两个语句可以被用来更进一步控制任何loop的执行(不只while loop) break语句可以把loop立即终止。程序会从done的下一行开始继续向下执行 continue语句是放弃当前执行的循环命令,重新开始执行一个循环(循环条件必须再test一遍) ---
while [ "$filename" ]
do
if [ ! -d $filename ]
then
echo Must be a directory
continue
fi
if [ `ls $filename | wc -l ` -gt 100 ]
then
echo stopping - There was a huge directory
break
fi
#process the directory read filename
done 这段代码有问题,关键是看出break和continue的作用
--- Numerical Calculations
1 Bourne shell 不能认出数字(只认strings),并且不能进行数字运算
2 unix程序使用expr程序来做数字运算 如 expr 3 * 4 结果是12
(这样输入会报语法错误) 1 但是+可以直接使用。但是*在做乘法运算的时候输入expr 3 * 4,因为shell在读取命令的时候先读到*再读到expr。所以shell会把*当作通配符并不是乘号,所以要用转义字符\
正确的输入应该是 expr 3 \* 4 结果为12 2 运算符* ,+ ,— 等的左右要有空格,不然不能正确运算 3 括号()等在使用时也要用\ 进行转义
---
a=15
b=3
c=`expr $a / $b`
echo $c 结果
5
---
在给c赋值的时候不能直接输入15/3或者用$a / $b,但是可以用expr来实现 使用这个可以让loop重复一定的次数 ---
read count
i=1
while [ $i -le $count ]
do
echo This is loop $i of $count
i=`expr $i + 1 `
done
--- for loop
可以给一个变量设置一系列的值,并且为每一个值执行一次代码块里的内容 如
for var1 in bread meat dairy vegetables fruit
do
echo one of the main food groups is $var1
done for loop的常见用法
1 运行一系列的文件名
for fname in *.txt
do
[ -s $fname ] && cp $fname backups
done Text processing filter是一个unix命令行程序有下面的特点
1 它可以读取标准的输入或者一个,多个文件的内如
2 可以对上面的数据运行一些处理
3 可以根据输出产生一些运算后的输出 如 wc是一个filter。它是计算行数,单词数,和字符数。她的输出是对单词数,行数,字符数计算后的数字 常见的过滤器
cat
more
grep
sort
wc
tee 复制-写到文件和屏幕上
sed 基本编辑
awk anything grep
1 在简单的形式下,就是显示输入的文本里包括确定pattern的行
2 另一种说法就是,它删除或者过滤掉输入文本里不包括确定pattern的行。
3 格式
grep pattern [files] 常见表达式
1 grep 使用"global regular expression parser" 全局常见表达式语法分析器 2 常见的表达式是一个术语,用来描述一套特定文本匹配格式pattern。
如 ^abc 匹配任何以abc开头的文本行
grep '^abc' textname abc$ 匹配任何以abc结尾的文本行
^$ 匹配空行
a* 匹配任何0,或者以a开头的文本行
如ca*t可以匹配的类似 ct,caat,cat,caaaat等
a+ 匹配任何一个a或者多个a(这不是标准的常见表达式,但是可以用下面的方法实现)
如 grep 'ca+t' sampletxt 不能得到想要的行
grep -E 'ca+t' sampletxt 可以实现
c[aou]t 匹配cat,cot或者cut
c[ao]*t 匹配c跟着0个或者更多的a或o最后跟t。ct,cout,cooot,cuouut等
c.t 匹配一个c后跟任何一个字符再跟一个他结尾的
c.*t 匹配c后跟任何字符并且字符的数量不限值最后以t结尾的
X[a-zA-z0-9]*X 匹配任何被两个X包围的连续的(甚至0长度)的字母和数字
更多的可以通过manpage查 grep或者 regexp sort
一个简单的过滤器,排序从输入输入文本的一系列行
格式 sort datafile 或 who|sort 常见错误
sort file1 > file1 不能这样做.sort file1只是把输入的内容重新排序然后输出到屏幕上,但是并没有改变file1里面的内容。shell在读取命令的时候会先检查到>符号,它会先准备file1用来接收输入,所以file1原先的内容会先被清除,再准备sort。所以如果用上面的命令,结果只会得到空,file1的内容会被清空。
所以正确的做法是
sort file1 〉file2
mv file2 file1
不只是sort ,所有需要输入的都是这样,所以应该引起注意。
---
ls | while read aline
do
echo again ========
if [ ! -f $aline]
then
continue
fi
ls | while read aline
do
break 2
done
echo $aline
done
--- sed
是stream editor的缩写。是一个把其他程序的输入或者文件内容进行基本编辑的程序。(类似sort,文件本身在程序运行后并不会改变) sed action [files] 它可以运行一次运行一些actions,如下
1 sed -e action1 -e action2 [files]
2 sed -f scriptfile [files] 注意 scriptfile是需要运行的一些行为 命令行里这些actions通常包括在单引号里,用来阻止shell interpretation解释特殊字符 ---
常见的actions
text substitution (如果必要使用常见表达式)
a 替换文本
1 s/foo/bar/ 改变每行第一个foo为bar
sed 's/foo/bar' filename sed 's/bash/Bourne Again Shell/' testfile
2 同时用两个actions
sed -e 's/bash/Bourne Again Shell/' -e s/false/ERROR/ testfile
后一个没有用单引号因为没有特殊字符 3 s/foo/bar/g 改变所有的foo为bar 4 也可以限制一些actions在一定的行号范围内。
1,10s/foo/bar/ 只替换1-10行的或者 40,$s/foo/bar/ 替换40行道最后的
如果不列出行数,会替换输入的所有行
在这里$代表文件的最后一行 b删除行
1 11,20d 删除11至20行输入 2 /hopscotch/d 删除所有带hopscotch的行 如 sed '/bash/d' testfile
sed '1,98d' testfile 删除1-98行的内容 3 删除除了定义的行以外的所有行
3,$!d 只删除12行,保留3行到最后一行
/ducks/!d 不删除包含ducks的行 sedscript内容
---
#!/bin/sed -f s/mvirtue/THE AUTHOR OF THIS COURSE/ /gumby/d
---
who | sedscript
把who命令输入的内容里的所有mvirtue替换为THE AUTHOR OF THIS COURSE ,删除掉gumby。然后输出
awk
awk是创建这个程序的三个人的名字命名的。
它是一个文本处理工具和程序语言。它能进行几乎所有的能想象到的的任何文本的处理
awk的使用方法
awk action [files] 这里的action是一个用{}包括的连续的语句,并且语句之间用;分隔开。 who | awk '{print $1,"is on terminal", $2}' $1,$2,$3,etc是每行输入的令牌。令牌被空格或者tab分隔开 可以用-F选项,来定义分隔tokens的字符 awk -F : '{print $1, "home:", $6}' /etc/passwd 使用冒号作为分隔token的字符 可以运行不同的actions在符合确定(regular expression)的patterns的行上面, awk '/Australia/ {print $1}' database 把data文件里面所有带Australia的行的第一个token打印出来。 awk可以进行变量的数学运算 awk '{print $1,($3+$4)/$5}' database 一套复杂的actions最好放在一个单独的script文件里执行 awk -f scriptfile inputfile scriptfile不必必须有执行权限,不需要在第一行定义interpreter用来运行
但是可以创建独立的awk script
---
#!/bin/awk -f
{print $1, "home:", $6}、
然后运行myawkscript /etc/passwd
---
#!/bin/awk -f
BEGIN {
FS = ":"
}
{
printf("%s home dir is %s\n", $1, $6)
---
上面的脚本有执行权限
awk 提供了一个非常有利的print功能printf (类似于c和c++里的)
awk 'printf("%-12s%-20s\n", $1, $6)' database ---
#!/bin/awk -f
BEGIN {
FS = ":";
printf("Username Directory\n");
printf("================================\n");
}
{
printf("%-12s%-20s\n",$1,$6)
} ---
>
错误信息重定向
2>
正常信息和错误信息输出到同一个文件
2>&1
example : backup > a.out 2>&1
不需要的信息可以重定向到
/dev/null
后台运行程序(一般运行程序如果logout程序就会终止,可以用此命令保证程序持续运行)
nohup programname & 特殊字符
1 \ 可以使特殊字符失去其意义
2 " 双引号可以把一些包括在其中的特殊字符的特殊意义去掉如#。但是对$无效果
3 ' 单引号可以把除了单引号以外的所有特殊字符的特殊意义去掉 chapter3 4种方式在命令行下运行脚本(脚本必须有执行权限) 1 myscript
直接输入脚本名称(脚本内运行的变量在脚本运行结束后脚本内变量的内容清空)
2 sh myscript
此时myscript不需要由执行权限
3 . myscript
使用当前shell运行脚本中的命令(不重新调用一个shell,在script里定义的变量都可以使用,不会因为shell不同而无法共享变量)
4 exec myscript
结束当前的shell,建立一个新的shell执行脚本。当script执行完毕的时候,用户退出
vi使用
:! 运行单独的unix命令(包括正在被编辑的脚本)可以用在正在编辑的脚本的测试
如:! myscript :!! 可以再重复运行刚才运行的命令 % 运行正在编辑的脚本时,可以用%来替代当前脚本的名字
如 :!chmod +x %
给当前正在编辑的文件增加执行权限
PATH和bin
1一般都把script放在一个目录里,大多数都放在home目录下的bin文件夹下
2再把这个目录加入PATH中。这样登陆时在任何目录下都可以运行script了。 script interpreters
可以运行script的程序
bourne shell
cshell
sed 字符串编辑器
awk 文本处理器
perl 强大的文本处理器
tcl
还有许多其它种类的 script interpreters 工作过程 1 找到文件并打开
2 检查文件的开头的字符决定是那种类型的可执行文件 (二进制或者脚本)
3 如果是个脚本(并且没有定义用那种interpreter执行),shell会尝试去解释和运 行脚本
4 让shell分辨使用哪种script interpreters可以通过在script第一行定义使用哪个
#!/bin/csh
or#!/usr/local/bin/perl
这项设置对于CGI programming是绝对基础要求 使用type perl 可以找到perl程序的执行文件的路径
如 type perl
结果 perl is /usr/local/bin/perl CGI scripts
bsh 可以用在CGI scripts web programming
CGI 程序是一个用来提供互动,动画或者数据库联接到一个web站点的程序 一个CGI程序可以是任何一种程序,包括bourne shell scripts,perl scripts 需要的条件
1 必须是可以执行的
2 必须以#!/bin/sh 开头
3 脚本产生的输出必须可以被认出是一个合法的http流。这个要求标准输出开头有 类似Content-type:text/html #!/bin/sh
echo Content-type:text/plain
echo
ls /bin
chapter 4 shell variables
variable name要求
1 名字必须唯一
2 只包括a-z,A-Z,0-9和下划线
3 不能以数字开头,可以用字母或者下划线开头
4 变量名区分大小写 所有的shell变量是strings处理 ,不是numbers 。但是可以用expr进行运算 sport=basketball 等号左右不能有空格。
sport= "basketball football" 如果输入的值包含空格,必须用双引号包括起来 $sport 可以得到变量名的内容
echo I like $sport
Environment variables
1 HOME
2 PATH
3 LOGNAME
4 TERM terminal type 现在正在使用的、 变量内容可以被脚本改变,但是这个改变随脚本运行走,并不影响login shell的变量。除非脚本是用 . 点来运行 除非把新创建的变量加入到环境变量里,否则新的变量并不能被其他程序(包括脚本)使用 把一个变量加入到环境变量里使用export命令
month=January
export month
The Trouble with Quotes
unix shell 用3种不同的引用
1 单引号 '
2 双引号 "
3 反引号 `
1单引号
单引号可以把单引用以内的所有的特殊符号变为普通的符号。
可以用两个单引号把一个单引号包括起来,使其只作普通符号使用 2双引号
双引号可以允许一些特殊符号不变为普通符号
如 $ ` 还有其他的如 \"或者\$
echo "$LOGNAME made \$1000 in `date+%B`"
结果类似 peter made $1000 in November 单引号包括的内容会失去特殊意义,所以可以当作字符串使用,在find,grep可以用到
双引号包括的内容特殊符号可以保持特殊意义。应用更广泛
3反引号
任何包括在反引号内的文本会被当作命令看待,并且调用一个单独的shell执行
这个命令执行的输出结果代替反引号里的内容 list=`who|sort`
echo $list echo the date is `date +%m%d%y` today
the date is 08/22/01 today
echo the date is `date` today
the date is Wed Aug 22 16:38:42 EST 2001 today
Grouping Commands
使用命令的不同的行为
1 运行在后台
2 重定向输出到文件或者其他程序
3 等等 如果希望应用相同的行为到一些连贯的程序,可以把程序变成组,然后应用行为到组
$ (echo the date is date )>output_file
line control
可以在一行同时运行两个命令,但是要用;分开 echo This command is split \
over several lines
chapter5
Conditional Code
true
echo $?
false
echo $? bsh
echo $?
返回0表示true,程序正常退出。其他值有问题为false。
csh
echo $?
返回非0表示正常,为0表示false conditiional command execution 1 command1 && comand2 命令2只会在命令1正常完成(带一个exit status 0)时运行
如 ls file1 && cp file1 /tmp 只有在file1在ls列出才运行cp命令
cp abc xyz && echo the file was copied okay 2 command3 || comand4 命令4只会在命令3错误完成(带一个exit status not 0)时运行 如 diff filea fileb || echo the files are different
ls file2 || exit 这种命令结构也有一定限制
1 如果条件符合,只能运行一条命令(在有可能需要运行一组命令时不合适) 2 如果条件不符合,不能定义运行的第二个命令
if statement
格式 if command1
then
command2
command3
...
fi 例
if diff file1 file2 > /dev/null
then
echo the files are the same
rm file2
exit
fi
else clause
在if条件的命令执行结果返回exit status not 0时运行命令
即在给定的条件不符合时执行命令 else的格式
if command1
then
one set of commands
else
another set of commands
fi 例子
if diff file1 file2 > /dev/null
then
echo the files are the same
rm file2
else
echo the files are different!
echo please review the differences:
difff file1 file2
fi
: 表示什么也不做的命令 如
if ls file1 > /dev/null
then
: #什么也不做
else
echo the file does not exit - exiting
exit
fi the elif clause
当我们需要写一个超过两个互相排除选项的条件代码结构时用 elif即 else if
例
if command1
then
command set 1
elif command2
then
command set 2
else
command set 3
fi 例子
if ls $file > /dev/null 2>&1
then
echo sorry,the file already exists
elif who > $file
echo $file now contains the userlist
else
echo could not create $file
fi elif分支可以重复无穷次,但是else分支只能使用一次 using test
bourne shell 本身不支持比较。但是有另外一个程序test支持 如
var1=10
test $var1 = 20
echo $?
返回1 test没有输出,不能告诉你这个变量是否相等。但是test通过改变exit status来表示运算的结果,所以可以用echo $?来判断 test的主要目的是返回一个对条件测试的相应exit status。
这个exit status 与true和false的意义一致 test专门被设计用来和if语句一起使用 if test $var1 -gt $max
then
echo that value is too large
fi 使用test的注意事项
1 在test命令行里 $var1 = 20 ,必须在等号的左右有空格。(这与分配变量相反)2 如果变量还没有设置或者设置为空(x=),那么用test检查变量将会得到syntax error .这一点可以用双引号包括变量来解决。
test "$var1" = 20
但是返回的exit status 仍然为错误即非0。 test的选项
1 test value1 = value2
返回true(0)如果值相同
2 test value1 != value2
返回true(0)如果值不同
3 test value1 -gt value2
返回true(0)如果value1和value2都是整数值并且value1大于value2
相似的选项包括 -lt , -ge(greater than or equal to) ,-le
4 test value
返回true(0)如果值非空
5 test -z value
返回true(0)如果值是空(0长度,但不是没有)
其他选项-用来检查files和directories
6 test -f filename
返回true(0)如果给的文件存在并且是一个常规文件(如,非目录的,设备文件等)
7 test -d filename
返回true(0)如果给的文件存在并且是一个目录。 8 test -s filename
返回true(0)如果给定的文件或者目录存在并且大小超过0
9 test -r|w|x filename
返回true(0)如果给定的文件存在并且是读|写|执行(运行脚本的进程的读写权限)
下面的选项可以和上面的选项混合使用
10 test !expression
返回true(0)如果表达式被认为是false(expression是上面提到的选项的一种)
!符号读作 not
test ! -w /etc/passwd
echo $?
如果当前进程对passwd文件没有写权限则echo返回true(0)
11 test expression1 -a expression2
返回true(0)如果两个表达式都为true 如 echo "Please enter a filename: \c"
read fname
if test -z "$fname"
then
echo You did not enter a filename
elif test -f "$fname" -a -w "$fname"
then
echo Everything is fine
else
echo the file is not a writable file
fi 12 test expression1 -o expression2
返回true(0)如果两个表达式有一个是true(0) test有一个alias 是 [
如 if [$var1 -gt $max]
then
echo that value is too large
fi case statement
caset 用来解决条件在2,3个以上的代码
case 格式 case $var1 in
val1)
code for case 1
;;
val2)
code for case 2
;;
val3)
code for case 3
;;
esac ---
echo " please enter a rock group name: \c"
read rname case $rname in
Beatles)
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
esac echo $rname made $no_albums albums.The last one was called \"$last_album\" ---
Case 命令有一个类似if命令里的else的结构。即用*代替条件,作为所有分支都不满足的执行的命令 case $var1 in
val1)
code for case 1
;;
...
*)
code for any case that is not covered above
;;
esac ---
echo " please enter a rock group name: \c"
read rname case $rname in
Beatles)
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
*)
echo Unknown rock group
exit
;;
esac echo $rname made $no_albums albums.The last one was called\"$last_album\" ---
其他用法
1 当相同的代码可以在多种cases下执行时 case $var1 in
val1|val2|val3)
code for case 1-3
;;
... 2 也可以用通配符来匹配变量的值 case $var1 in
d*)
code for anything that starts with d
;;
... ---
echo " please enter a rock group name: \c"
read rname case $rname in
[bB]eatles|"The Beatles")
no_albums=12
last_album="Let it be"
;;
"Dire Straits")
no_albums=7
last_album="On Every Street"
;;
Abba)
no_albums=6
last_album="Abba Gold"
;;
*)
echo Unknown rock group
exit
;;
esac echo $rname made $no_albums albums.The last one was called\"$last_album\"
--- 不像if语句,case 语句并不返回true/false值和exit statuses chapter6
While Loop
如果if语句的条件符合,可以执行一个代码段,while loop用同样的方法重复执行代码段 代码格式
while command
do
code block
done 给定的代码块会被重复的执行,直到给定的命令返回一个non-zero exit status 如 read answer
while [ "$answer" != chickens ]
do
echo That answer is incorrect
echo Please try again
read answer
done ---
echo "please enter the name of a directory : \c"
read dir while [ ! -d "$dir" ]
do
echo "$dir is not a directory"
echo "Please try again: \c"
read dir
done echo Congratulations: $dir is a directory! ---
实际上可能使用一个while loop 去运行命令输出的每一行
如
who | while read user term time
do
echo $user has been on $term since $time
done break and continue这两个语句可以被用来更进一步控制任何loop的执行(不只while loop) break语句可以把loop立即终止。程序会从done的下一行开始继续向下执行 continue语句是放弃当前执行的循环命令,重新开始执行一个循环(循环条件必须再test一遍) ---
while [ "$filename" ]
do
if [ ! -d $filename ]
then
echo Must be a directory
continue
fi
if [ `ls $filename | wc -l ` -gt 100 ]
then
echo stopping - There was a huge directory
break
fi
#process the directory read filename
done 这段代码有问题,关键是看出break和continue的作用
--- Numerical Calculations
1 Bourne shell 不能认出数字(只认strings),并且不能进行数字运算
2 unix程序使用expr程序来做数字运算 如 expr 3 * 4 结果是12
(这样输入会报语法错误) 1 但是+可以直接使用。但是*在做乘法运算的时候输入expr 3 * 4,因为shell在读取命令的时候先读到*再读到expr。所以shell会把*当作通配符并不是乘号,所以要用转义字符\
正确的输入应该是 expr 3 \* 4 结果为12 2 运算符* ,+ ,— 等的左右要有空格,不然不能正确运算 3 括号()等在使用时也要用\ 进行转义
---
a=15
b=3
c=`expr $a / $b`
echo $c 结果
5
---
在给c赋值的时候不能直接输入15/3或者用$a / $b,但是可以用expr来实现 使用这个可以让loop重复一定的次数 ---
read count
i=1
while [ $i -le $count ]
do
echo This is loop $i of $count
i=`expr $i + 1 `
done
--- for loop
可以给一个变量设置一系列的值,并且为每一个值执行一次代码块里的内容 如
for var1 in bread meat dairy vegetables fruit
do
echo one of the main food groups is $var1
done for loop的常见用法
1 运行一系列的文件名
for fname in *.txt
do
[ -s $fname ] && cp $fname backups
done Text processing filter是一个unix命令行程序有下面的特点
1 它可以读取标准的输入或者一个,多个文件的内如
2 可以对上面的数据运行一些处理
3 可以根据输出产生一些运算后的输出 如 wc是一个filter。它是计算行数,单词数,和字符数。她的输出是对单词数,行数,字符数计算后的数字 常见的过滤器
cat
more
grep
sort
wc
tee 复制-写到文件和屏幕上
sed 基本编辑
awk anything grep
1 在简单的形式下,就是显示输入的文本里包括确定pattern的行
2 另一种说法就是,它删除或者过滤掉输入文本里不包括确定pattern的行。
3 格式
grep pattern [files] 常见表达式
1 grep 使用"global regular expression parser" 全局常见表达式语法分析器 2 常见的表达式是一个术语,用来描述一套特定文本匹配格式pattern。
如 ^abc 匹配任何以abc开头的文本行
grep '^abc' textname abc$ 匹配任何以abc结尾的文本行
^$ 匹配空行
a* 匹配任何0,或者以a开头的文本行
如ca*t可以匹配的类似 ct,caat,cat,caaaat等
a+ 匹配任何一个a或者多个a(这不是标准的常见表达式,但是可以用下面的方法实现)
如 grep 'ca+t' sampletxt 不能得到想要的行
grep -E 'ca+t' sampletxt 可以实现
c[aou]t 匹配cat,cot或者cut
c[ao]*t 匹配c跟着0个或者更多的a或o最后跟t。ct,cout,cooot,cuouut等
c.t 匹配一个c后跟任何一个字符再跟一个他结尾的
c.*t 匹配c后跟任何字符并且字符的数量不限值最后以t结尾的
X[a-zA-z0-9]*X 匹配任何被两个X包围的连续的(甚至0长度)的字母和数字
更多的可以通过manpage查 grep或者 regexp sort
一个简单的过滤器,排序从输入输入文本的一系列行
格式 sort datafile 或 who|sort 常见错误
sort file1 > file1 不能这样做.sort file1只是把输入的内容重新排序然后输出到屏幕上,但是并没有改变file1里面的内容。shell在读取命令的时候会先检查到>符号,它会先准备file1用来接收输入,所以file1原先的内容会先被清除,再准备sort。所以如果用上面的命令,结果只会得到空,file1的内容会被清空。
所以正确的做法是
sort file1 〉file2
mv file2 file1
不只是sort ,所有需要输入的都是这样,所以应该引起注意。
---
ls | while read aline
do
echo again ========
if [ ! -f $aline]
then
continue
fi
ls | while read aline
do
break 2
done
echo $aline
done
--- sed
是stream editor的缩写。是一个把其他程序的输入或者文件内容进行基本编辑的程序。(类似sort,文件本身在程序运行后并不会改变) sed action [files] 它可以运行一次运行一些actions,如下
1 sed -e action1 -e action2 [files]
2 sed -f scriptfile [files] 注意 scriptfile是需要运行的一些行为 命令行里这些actions通常包括在单引号里,用来阻止shell interpretation解释特殊字符 ---
常见的actions
text substitution (如果必要使用常见表达式)
a 替换文本
1 s/foo/bar/ 改变每行第一个foo为bar
sed 's/foo/bar' filename sed 's/bash/Bourne Again Shell/' testfile
2 同时用两个actions
sed -e 's/bash/Bourne Again Shell/' -e s/false/ERROR/ testfile
后一个没有用单引号因为没有特殊字符 3 s/foo/bar/g 改变所有的foo为bar 4 也可以限制一些actions在一定的行号范围内。
1,10s/foo/bar/ 只替换1-10行的或者 40,$s/foo/bar/ 替换40行道最后的
如果不列出行数,会替换输入的所有行
在这里$代表文件的最后一行 b删除行
1 11,20d 删除11至20行输入 2 /hopscotch/d 删除所有带hopscotch的行 如 sed '/bash/d' testfile
sed '1,98d' testfile 删除1-98行的内容 3 删除除了定义的行以外的所有行
3,$!d 只删除12行,保留3行到最后一行
/ducks/!d 不删除包含ducks的行 sedscript内容
---
#!/bin/sed -f s/mvirtue/THE AUTHOR OF THIS COURSE/ /gumby/d
---
who | sedscript
把who命令输入的内容里的所有mvirtue替换为THE AUTHOR OF THIS COURSE ,删除掉gumby。然后输出
awk
awk是创建这个程序的三个人的名字命名的。
它是一个文本处理工具和程序语言。它能进行几乎所有的能想象到的的任何文本的处理
awk的使用方法
awk action [files] 这里的action是一个用{}包括的连续的语句,并且语句之间用;分隔开。 who | awk '{print $1,"is on terminal", $2}' $1,$2,$3,etc是每行输入的令牌。令牌被空格或者tab分隔开 可以用-F选项,来定义分隔tokens的字符 awk -F : '{print $1, "home:", $6}' /etc/passwd 使用冒号作为分隔token的字符 可以运行不同的actions在符合确定(regular expression)的patterns的行上面, awk '/Australia/ {print $1}' database 把data文件里面所有带Australia的行的第一个token打印出来。 awk可以进行变量的数学运算 awk '{print $1,($3+$4)/$5}' database 一套复杂的actions最好放在一个单独的script文件里执行 awk -f scriptfile inputfile scriptfile不必必须有执行权限,不需要在第一行定义interpreter用来运行
但是可以创建独立的awk script
---
#!/bin/awk -f
{print $1, "home:", $6}、
然后运行myawkscript /etc/passwd
---
#!/bin/awk -f
BEGIN {
FS = ":"
}
{
printf("%s home dir is %s\n", $1, $6)
---
上面的脚本有执行权限
awk 提供了一个非常有利的print功能printf (类似于c和c++里的)
awk 'printf("%-12s%-20s\n", $1, $6)' database ---
#!/bin/awk -f
BEGIN {
FS = ":";
printf("Username Directory\n");
printf("================================\n");
}
{
printf("%-12s%-20s\n",$1,$6)
} ---
相关阅读 更多 +