Linux 命令大结 (2)

来源:互联网 发布:淘宝网店利润 编辑:程序博客网 时间:2024/05/14 00:03

Linux Shell家族树

工作中遇到使用sh, ksh, bash, 和 csh, Bourne Shell(即sh)是UNIX最初使用的shell,平且在每种UNIX上都可以使用。Bourne Shell在shell编程方便相当优秀,但在处理与用户的交互方便作得不如其他几种shell。
LinuxOS默认的是Bourne Again Shell,它是Bourne Shell的扩展,简称bash,与Bourne Shell完全兼容,并且在Bourne Shell的基础上增加,增强了很多特性。可以提供命令补全,命令编辑和命令历史等功能。它还包含了很多C Shell和Korn Shell中的优点,有灵活和强大的编辑接口,同时又很友好的用户界面。

C Shell是一种比Bourne Shell更适合的变种Shell,它的语法与C语言很相似。Linux为喜欢使用C Shell的人提供了Tcsh。
Tcsh是C Shell的一个扩展版本。Tcsh包括命令行编辑,可编程单词补全,拼写校正,历史命令替换,作业控制和类似C语言的语法,他不仅和Bash Shell提示符兼容,而且还提供比Bash Shell更多的提示符参数。

Korn Shell集合了C Shell和Bourne Shell的优点并且和Bourne Shell完全兼容。Linux系统提供了pdksh(ksh的扩展),它支持人物控制,可以在命令行上挂起,后台执行,唤醒或终止程序。





Shell中的set命令,比较常用的是set -x用于脚本调试 ,记住下面特性前的,-是开启,+是关闭

set指令能设置所使用shell的执行方式,可依照不同的需求来做设置
 -a  标示已修改的变量,以供输出至环境变量。 
 -b  使被中止的后台程序立刻回报执行状态。 
 -C  转向所产生的文件无法覆盖已存在的文件。 
 -d  Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。 
 -e  若指令传回值不等于0,则立即退出shell。   
 -f   取消使用通配符。 
 -h  自动记录函数的所在位置。 
 -H     Shell  可利用"!"加<指令编号>的方式来执行history中记录的指令。 
 -k  指令所给的参数都会被视为此指令的环境变量。 
 -l  记录for循环的变量名称。 
 -m  使用监视模式。 
 -n  只读取指令,而不实际执行。 
 -p  启动优先顺序模式。 
 -P  启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。 
 -t  执行完随后的指令,即退出shell。 
 -u  当执行时使用到未定义过的变量,则显示错误信息。 
 -v  显示shell所读取的输入值。 
 -x  执行指令后,会先显示该指令及所下的参数。 
 +<参数>  取消某个set曾启动的参数。


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/expectif { $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 -1log_user 1cd /local/xiaoxiao/vmwareset crr [exec pwd]puts "CD to dir: $crr"rm -rf *spawn -noecho ftp $ftpserverexpect "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 $tesif { $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在标准输出(终端)输出所有来自应用程序的回应信息,可以用下面的两个命令重定向这些信息: 

  1.   log_file [文件名] : 这个命令让expect在设置的文件中记录输出信息。必须注意,这个选项并不影响控制台输出信息,例如: log_file expect.log 
  2.   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 内容是:abcde[chengmo@centos5 shell]$ awk 'NR%2==1{next}{print NR,$0;}' text.txt     2 b4 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    shiftdone

释疑点:(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命令的使用:  对于某个命令行参数,其参数个数可以不固定,但是可以通过$*或$@进行循环操作。若用户要求Shell在不知道变量个数的情况下,还能逐个处理参数,也就是在$1之后是$2,$2之后是$3。在未运行shift命令之前$1是可用的,当使用shift命令之后,原来的$2会变成$1,并且原有的$1变得不可用,通过$#命令获得的参数个数也会少1。示例下面代码用until和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,正则表达式的快速记忆

软连接只是一个文件,即使它连接的是一个文件夹,故cp -r 不会自动将连接的文件夹里的文件都copy过来。

在当前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服务

NFS是Network File System的简写,即网络文件系统。 点击,可看 鸟哥私房菜中关于NFS文件服务器的介绍。它是FreeBSD支持的文件系统中的一种,也被称为NFS. NFS允许一个系统在网络上与他人共享目录和文件。通过使用NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件。 简单的来说,通过NFS,在客户端可以将服务端的文件mount到客户端,在客户端就相当于操作本地文件一样!
摘取鸟哥上关于NFS的介绍中的重点:
NFS非常复杂,还需要支持其他功能来支持,不同的功能由不同的程序来启动,每种程序都选择<1024的随机端口进行服务,但客户端如何知道呢?通过RPC(Remote Process Call)!RPC最主要的功能就是支持为每个NFS的功能指定一个端口号然后报给客户端,然后客户端即可知道每种功能对应的Port。而RPC知道每个NFS的每个功能的端口号是因为NFS在启动时即向RPC注册,RPC的监听客户端的端口号固定是111,NFS服务固定端口号是2049。所以NFS是依赖于RPC的正常启动的。因为NFS的主要任务是进行文件的分享,而文件系统的分享都与权限有关,对于比较简单的NFS服务端来说:
  • rpc.nfsd 最主要的 NFS 服务提供商。这个 daemon 主要的功能就是在管理客户端是否能够使用服务器文件系统挂载信息等, 其中还包含这个登入者的 ID 的判别喔!

  • rpc.mountd 这个 daemon 主要的功能,则是在管理 NFS 的文件系统哩!当客户端顺利的通过 rpc.nfsd 而登入服务器之后,在他可以使用 NFS 服务器提供的档案之前,还会经过档案权限 (就是那个 -rwxrwxrwx 与 owner, group 那几个权限啦) 的认证程序!他会去读 NFS 的配置文件 /etc/exports 来比对客户端的权限,当通过这一关之后客户端就可以取得使用 NFS 档案的权限啦!(注:这个也是我们用来管理 NFS 分享之目录的权限与安全设定的地方哩!)
注意NFS服务本身没有对客户端上用户权限的识别,服务器端会以客户端的使用者 UID 与 GID 等身份来尝试读取服务器端的文件系统,这时有权限的问题,可以查看鸟哥官方: 13.1.4 NFS访问权限。关于NFS所使用的配置文件,exports文件比较简单,如:

[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到其他的客户端上。
[plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. bl1227{xiaoxiao}/local/xiaoxiao>nfs_start  
  2. NFS user-land server is running...  
  3. Use these commands to mount filesystems on the client machine :  
  4. mount -o port=42233,mountport=41233,nolock,udp,vers=2 vhost:/local /local  
  5. 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



0 0
原创粉丝点击