Linux Shell家族树
Shell中的set命令,比较常用的是set -x用于脚本调试 ,记住下面特性前的,-是开启,+是关闭
Expect命令
expect是建立在tcl基础上的一个工具,Expect上的语法基于tcl的语法,它用来让一些需要交互的任务自动化地完成。expect中最重要的是三个指令spawn,send和expect。spawn是启动一个新的进程开始一个任务,expect是表示执行这个任务中的可能结果,expect脚本是顺序执行的,那么当程序执行到这个expect的时候就会阻塞,所以程序会一直等待到timeout然后退出。expect{}中的并列表达式则是相当于switch的行为,只要列出的几项内容有一项得到满足,expect命令就得到满足,于是程序可以正常执行。然后使用send来模拟人为输入,注最后别忽略\n用来表示回车。
expect脚本支持简单的循环,while, Break之类。下面的Expect 脚本是通过ftp下载一个或多个文件(如果参数中含有*即为下载多个),注意expect的标准语法,通过set 来设置参数
#!/usr/bin/expect
if { $argc !=1 } {
puts "ERROR: $argv0 vmware name to be downloaded "
return 1
}
set ftpserver "155.252.181.54"
set username "xiaoxiao"
set password "<span style="font-size: 11.6667px; ">xiaoxiao</span>"
set vmdir "/Data/vmware/wo/"
set vmname [lindex $argv 0]
set timeout -1
log_user 1
cd /local/xiaoxiao/vmware
set crr [exec pwd]
puts "CD to dir: $crr"
rm -rf *
spawn -noecho ftp $ftpserver
expect "Name"
send "$username\n"
expect "Password"
send "$password\n"
expect {
"fail*" {
puts "$arg0: FTP auth fail"
return 1
}
"ftp>" {
puts "Log successfully"
}
}
send "cd $vmdir\n"
expect "ftp>"
set tes [ string first $vmname "*" ]
puts $tes
if { $tes == -1 } {
puts "Downloading the $vmname from the ftp server"
send "get $vmname\n"
} else {
puts "Downloading multi files $vmname from the ftp server"
send "prompt\n"
expect "ftp>"
send "mget $vmname\n"
}
expect {
"send OK" {
expect "ftp>"
send "bye\n"
# puts "FTP done success"
puts "ok"
return "ok"
}
"No such file" {
puts "$argv0: FTP fail"
return "fail"
}
"Fail*" {
puts "$argv0: FTP fail"
return "fail"
}
"fail*" {
puts "$argv0: FTP fail"
return "fail"
}
"rror*" {
puts "$argv0: FTP fail"
return "fail"
}
}
put "$argv0: FTP fail"
return 1
对于该脚本其中比较注意的是
(1)[]的使用,[]内可以嵌入tcl或expect 语句,[]中括号:执行命令,在此为判断一个字符串是否还有*
(2)expect并不是基于Bash的语法,故很多命令在expect中不能直接使用,使用exec 来实行shell命令,比如set crr [exec pwd] 即时将pwd命令的执行结果复制给crr变量
(3)expect的脚本中关于字符串的比较语法,可以参考一篇总结的比较好的: Expect和tcl 字符串语法。
(4)笔者在编写此脚本时遇到一些错误,很多都是由于[] {} 的格式不对导致,特别注意的是,一定要给他们空间,即和其他语句之间都有空格。Expect中没有小括号(),所有的if/else, while, for的条件全部使用大括号{}, 并且{ 与左边要有空格,否则会报错。另,else 不能单独占一行,否则会报错。{ }大括号:保留所有字符原有的意思,而不做解释,类似于shell中的单引号,{}的另外一个作用是可以续行,(其实是左大括号)
(5)for循环:foreach flag $flags {} ,其中flags: set flags {COMM_flags OS_flags SS1_flags PLSI_flags SD01_flags}
缺省下,expect在标准输出(终端)输出所有来自应用程序的回应信息,可以用下面的两个命令重定向这些信息:
- log_file [文件名] : 这个命令让expect在设置的文件中记录输出信息。必须注意,这个选项并不影响控制台输出信息,例如: log_file expect.log
- log_user 0/1 :这个选项设置是否显示输出信息,设置为1时是缺省值,为0 的话,expect将不产生任何输出信息,或者说简单地过滤掉控制台输出。必须记住,如果用log_user 0关闭了控制台输出,那么同时也就关闭了对记录文件的输出。 这一点很让人困扰,但若确实想要记录expect的输出却不想让它在控制台上制造垃圾的话,可以简单地把expect的输出重定向到/dev/null: ./test.exp > /dev/null
使用一对fork和disconnect命令。expect的disconnect命令将使得相应的进程到后台执行,输入和输出被重定向到/dev/null; fork命令会产生出一个子进程,而且它产生返回值,如果返回的是0,说明这是一个子进程,如果不为0,那么是父进程。因此,执行了fork命令之后,父进程死亡而子进程被disconnect命令放到后台执行。注意disconnect命令只能对子进程使用。
if [fork]!=0 exit
disconnect
AWK Linux 命令大结 中已经有了部分awk的介绍,这里继续
替换字符:sub匹配第一次出现的,gsub匹配所有的,与sed 's//' 和sed 's//g'一致: cat runVMs | awk '{ sub(/vmware/,"vmware -x");print }' | tee runVMs.auto
简单的awk应用:awk '{if($5<100){print $0}}' test 读文件test,判断第五列是否小于100,仅输出<100的行
在awk进行文本处理时候,我们可能会遇到。将多行合并到一行显示问题。 有点象sql里面,经常遇到的行转列的问题。 这里需要用到next语句。
除了本博客之外,还有一个awk介绍 ,awk详解博客,很详细的介绍了awk的应用
text.txt 内容是:
a
b
c
d
e
[chengmo@centos5 shell]$ awk 'NR%2==1{next}{print NR,$0;}' text.txt
2 b
4 d
当记录行号除以2余 1,就跳过当前行。下面的print NR,$0也不会执行。 下一行开始,程序有开始判断NR%2 值。这个时候记录行号是:2 ,就会执行下面语句块:'print NR,$0'
在进行文件合并的时候需要用到这样awk 的关键字next. awk中的内置关键字中比较重要的有FNR和NR,FNR,与NR功用类似,不同的是awk每打开一个新文件,FNR便从1重新开始累计. 故下面的awk 'FNR==1{print "\r\n" FILENAME}{print $0}' a.txt b.txt的意思就很明显了,分别处理两个文件,在每处理一个文件时,第一行输出FILENAME。
实例文本:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[chengmo@centos5 shell]$
awk
'FNR==1{print "\r\n"FILENAME}{print $0}'
a.txt b.txt
a.txt
100 wang
man
200 wangsan woman
300 wangming
man
400 wangzheng
man
b.txt
100 90 80
200 80 70
300 60 50
400 70 20
|
需要合并得到结果:
100 wang man 90 80
200 wangsan woman 80 70
300 wangming man 60 50
400 wangzheng man 70 20
实现思路:
通过外部命令合并文件,然后通过排序,然后通过awk进行合并操作。
首先:
1
2
3
4
5
6
7
8
9
|
[chengmo@centos5 shell]$
cat
a.txt b.txt |
sort
-n -k1 |
awk
'{print}'
100 90 80
100 wang
man
200 80 70
200 wangsan woman
300 60 50
300 wangming
man
400 70 20
400 wangzheng
man
|
现在需要把:第一列相同的处理合并到一行,这里需要用“next”语句。它操作,可以参考awk 多行合并【next 使用介绍】(常见应用4)
继续:
1
2
3
4
5
|
[chengmo@centos5 shell]$
cat
a.txt b.txt |
sort
-n -k1 |
awk
'NR%2==1{fd1=$2"\t"$3;next}{print $0"\t"fd1}'
100 wang
man
90 80
200 wangsan woman 80 70
300 wangming
man
60 50
400 wangzheng
man
70 20
|
需要把几行合并,经常用到方法是:NR%num 然后将行值保存下来,next该行。在输出时候打印出来。
定义变量,CMD=pickup_ci, 等价于 CMD="pickup_ci"
export TMP_DIR=/tmp/$HANDLE.$CMD.$$
export设置只对当前的bash登录session有效。这是存在内存里面的。
/etc/profile、/etc/bashrc等式“全局”,开机之后自动加载,所有用户共享着些文件。而每个用户的家目录下的 .bashrc、.barsh_profile等脚本是“局部”的,只对该用户有效。这样就满足了各个用户不同的需求。
until [ ${#} -eq 0 ]
do
case $1 in
-ci) CHOOSE_CI_NAME=$2
if [ "$CHOOSE_CI_NAME" = "" ]
then
ascm_error "ci name is required"
print -u2 "Usage: $CMD_LINE"
exit 1
fi
shift
;;
-debug) ASCM_DEBUG=Y
;;
-debugjava) ASCM_JAVA_DEBUG=Y
;;
-mer*) MRG_CMD=merge
MRG_PARMS="-merge"
;;
-\?|-help|-h)
print "$CMD: $CMD_ABS"
print
print "$CMD $CMD_LINE"
exit 0
;;
*) print -u2 "$CMD: ERROR: $1 is not a valid option."
print -u2 "Usage: $CMD_LINE"
exit 1
;;
esac
shift
done
释疑点:(1)${#} 与 $# 参数个数看,${ } 吧... 它其实就是用来作变量替换用的啦。一般情况下,$var 与 ${var} 并没有啥不一样。但是用 ${ } 会比较精确的界定变量名称的范围。关于这个可以查看本博中的:Shell编程总结(2)$? 表示上一次程序退出值,主次
$# 是传给脚本的参数个数
$@ 是传给脚本的所有参数的列表
$* 是以一个单字符串显示所有向脚本传递的参数,与位置变量不同,参数可超过9个
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误$0 The name of current program.$n $1 the first parameter,$2 the second...
$? Last command or function's return value.
$$ The program's PID.
$! Last program's PID.
(3)shift命令的使用:
(4)以上代码片段经常用于对参数个数不确定的脚本,根据输入确定输入参数含义。
比较操作符小结,Linux 中的IF 条件,比较严格的准则为,但没有那么必须
(1)数学逻辑和文件操作符比较,条件使用[ 条件 ] 注意[]与条件中的空格, [ $var1 -ne 0 -a $var2 -gt 2 ] # "-a" 等效于"&&" , 而 [ $var -ne 0 -o var2 -gt 2 ] # "-o" 等效于"||"
(2)字符串条件比较使用[[ 条件 ]]
(3)使用 test 命令进行条件判断可以避免使用方括号,即(1)和(2),如if test $var -eq 0; 常用比较操作符,请查阅本博文章:SHELL 编程总结。
if [ "`cleartool lsview -s $TMP_VIEW 2>/dev/null`" = "$TMP_VIEW" ] ,
DID_PICKUP_LIST=`echo "$DID_PICKUP_LIST" | sed "s/,*\(.*\),/\1/g"`
双引号中的``会进行执行,双引号中的$被视为变量,而单引号将阻止一切。 第二行中的sed,
正则表达式的快速记忆
在当前Shell脚本中引用其他脚本中的function的方法:. $UserName/function.sh 由此可以将公用function提取到function.sh中。
如何将命令的执行结果,赋值给一个变量呢?
在shell中abc=`grep 430 /tmp/opstat.tmp | awk '{print $2$4}'` 然后echo $abc 即可将abc的值输出
对于一般SHELL脚本的执行,如果脚本本身具有可执行权限可以执行./运行,否则用bash或sh 脚本名称。
关于标准输出和错误输出 Linux会将其区分开来,默认都是输出到屏幕,而使用>只会将标准输出而去除错误输出重定向到文件中。如果要将错误输出重定向要使用 2> ,如$ find /home -name lost* 2> err_result,
如果将不同信息分别重定向到不同的文件中:$ find /home -name lost* > normal_result 2 2>err_result
如果要将所有输出都重定向到同一个文件,则$ find /home -name lost* > all_result 2>&1 (这种写法的就是将2重定向到&1)
或 $ find /home -name lost* >& all_result 后者为前者的简写
将错误信息丢弃:$ find /home -name lost* 2> /dev/null
NFS服务
- rpc.nfsd 最主要的 NFS 服务提供商。这个 daemon 主要的功能就是在管理客户端是否能够使用服务器文件系统挂载信息等, 其中还包含这个登入者的 ID 的判别喔!
- rpc.mountd 这个 daemon 主要的功能,则是在管理 NFS 的文件系统哩!当客户端顺利的通过 rpc.nfsd 而登入服务器之后,在他可以使用 NFS 服务器提供的档案之前,还会经过档案权限 (就是那个 -rwxrwxrwx 与 owner, group 那几个权限啦) 的认证程序!他会去读 NFS 的配置文件 /etc/exports 来比对客户端的权限,当通过这一关之后客户端就可以取得使用 NFS 档案的权限啦!(注:这个也是我们用来管理 NFS 分享之目录的权限与安全设定的地方哩!)
[root@www ~]# vim /etc/exports
/tmp 192.168.100.0/24(ro) localhost(rw) *.ev.ncku.edu.tw(ro,sync)
[分享目录] [第一部主机(权限)] [可用主机名] [可用通配符]
除了鸟哥介绍的意外,关于NFS还可以,在启动NFS时,通过rpc.mountd, 和 rpc.nfsd 自定义端口号和Exports文件,并将其整合进脚本nfs_start,中进行自定义通用NFS的启动,为啥需要同样呢?一方面是为了脚本化,自动化和可扩展化,如下所示
$BIN_PATH/rpc.mountd -F -P $((port+11000)) -f $EXPORTS &
pidmnt=$!
$BIN_PATH/rpc.nfsd -r -F -P $((port+12000)) -f $EXPORTS &
pidnfs=$!
nfs_start 脚本执行后, 输出,之后可以使用输出的mount命令在其他地方,可以将服务端的/local/, /home mount到其他的客户端上。
- bl1227{xiaoxiao}/local/xiaoxiao>nfs_start
- NFS user-land server is running...
- Use these commands to mount filesystems on the client machine :
- mount -o port=42233,mountport=41233,nolock,udp,vers=2 vhost:/local /local
- mount -o port=42233,mountport=41233,nolock,udp,vers=2 vhost:/home /home
Bash shell 的算术运算有四种方式
1:使用 expr, 如 r=`expr 4 \* 5` 即可 echo $r,expr中使用在*和/之前必须冠以反斜线,已防被SHELL先行解释。
2:使用 $(( )),如 r=$(( 4 * 5 )),echo $r
3:使用 $[ ], 如r=$[ 4 * 5 ] echo $r, 由此可见$(())和$[]基本是等价的。
4:使用let ,如 let m=n*10,echo $m
虽然Bash shell 有四种算术运算方法,但并不是每一种都是跨平台的,建议使用expr。
另外,我们在 script 中经常有加1操作,以下四法皆可:
m=$[ m + 1]
m=`expr $m + 1`
m=$(($m + 1))
let m=m+1