查看文章 |
ABS notes (1)
2009-07-05 10:17
TODO: 添加12,13章未完的命令的参数 一、重用的模块: 清空或创建文件: cat /dev/null > /var/log/message :> /var/log/message > /var/log/message 当前用户ID: $UID 退出码归操作方法: E_XCD=66 cd不进去 E_NOTROOT=67 不是root 使用时: exit $E_XCD 即可,正常时 exit 0 查看用户是否为root: ROOT_UID=0 if [ "$UID" -ne "$ROOT_UID" ];then echo "Must be root" exit $E_NOTROOT fi 判断参数个数是否足够: E_WRONGARGS=65 if [ $# -ne 期望的个数 ];then echo "Usage: ..." exit $E_WRONGARGS fi 参数检查,给予默认值: (1)方法一(简单) LINES=10 if [ -n "$1" ];then #不为空,或者换为-z表示为空,但是用-z一般是用于如果没有参数,就退出的情况更方便些。 lines=$1 else lines=$LINES #为空,给予默认值 fi (2)方法二(高效) E_WRONGARGS=65 case "$1" in "") lines=50;; #为空,给予默认值 *[!0-9]*) echo "Usage:..."; exit $E_WRONGARGS;; #写各种case判断错误情况,并给出回应 *) lines=$1;; #正确,用$1赋值 esac (3)方法三(更高效) lines=${1:-50} 如果没有$1,就给50默认值,注意前面有个dash(-) 判断是否进入了某个目录: (1)方法一(简单) cd $LOG_DIR if [ `pwd` != "$LOG_DIR" ];then echo "Can't change to $LOG_DIR" exit $E_XCD fi (2)方法二(高效) cd $LOG_DIR || { echo "Can't change to $LOG_DIR" exit $E_XCD } 保留日志的最新几行: 假设cd /var/log/成功 tail -$lines messages > mesg.tmp mv mesg.tmp > messages sort和uniq: cat *.lst|sort|uniq 将file的小写改为大写: tr 'a-z' 'A-Z' < file 看哪个文件更老: if [ $file1 -ot $file2 ];then echo "file1 is older than file2" fi 将过去24小时内修改过的文件做备份: (1)GNU find快速版本: find . −mtime −1 −type f −print0 | xargs −0 tar rvf "$archive.tar" (2)Unix通用find慢速版本: find . −mtime −1 −type f −exec tar rvf "$archive.tar" '{}' \; 注意两点: 一是 -mtime +n 表示 n*24小时前,-n表示n*24小时内,但都是现在时间以前! 二是 tar 的参数,要是rvf了,不是cvf,因为find是每找到一次,就执行一次tar,所以要用r表示append文件到已有的tar中。而用cvf的话,每次都会重新刷掉以前所有的。 做运算: (1)复杂写法: var=$(((1+2)*(1+2))) (2)简单写法——和书写相同 let "var=(1+2)*(1+2)" eval:形成命令,再执行之! eval "`seq 10000 | sed -e 's/.*/export var&=ZZZZZZZZZZZZZZ/'`" 解释:先形成10000个export,再执行之。这里,seq的作用是控制export次数。 注意后面有个 & 这个是sed的非常有用的东西,等于前面匹配好的字串, 比如 echo "abc 123 bcd" | sed -e 's/[0-9]\{3\}/dd'; 结果是 abc dd bcd 取出最后一个参数: (1) 使用eval: eval echo '${'$#'}' (2) 使用!: args=$# echo ${!args} #不过真的没看懂,比如这里直接用 ${!$#}就不行 shift参数:每shift一次,参数就往左边挪动一次,这样一直读$1就可以读到所有参数,shift也可以作用于函数参数 until [ -z "$1" ] do echo -n "$1 " shift done 省略if的做法: ———— 效果,使得语句很简练! 一行搞定!! [ "$var1" −ne "$var2" ] && echo "$var1 is not equal to $var2" [ −d "$home" ] || echo "$home directory does not exist." P71给了一个限时输入的例子 版本1,用的是trap捕获kill信号来打断正在执行的read 版本2,用的是stty的设置 版本3,搞笑,发现read本身提供了-t设置timeout 用shell来处理一个文件的每一行: count=0 while read line do let "count+=1" echo $count $line done < /etc/passwd 用shell来处理每一行的每一个字段:(用IFS在while中设置临时输入分割符) count=0 while IFS=: read name passwd uid gid fullname ignore do let "count+=1" echo $count $name $passwd $uid $gid $fullname $ignore done < /etc/passwd 其实,while循环,也接受管道导入的数据: cat /etc/passwd | while read line do let "count+=1" echo $count $line done 得到一个进程名对应的进程号: ps ax | grep -v "ps ax" | grep -v grep | grep $PROCNAME | awk '{ print $1 }' 二、命令 id -nu 得到当前用户的名字(与euid对应的) id 得到你参加的所有组名和id 三、基础: 第1,2章 木有 第3章 特殊字符 #的两个特例: echo ${PATH#*:} 参数替换(没看懂) echo $(( 2#101011 )) 基本转换(没看懂) case语句后面要加两个;; case "$var" in shell匹配式) echo "...";; *) echo "...";; esac true和:只不过是两个命令 前者是系统命令,后者是bash内置命令,更高效,但是不能跨平台 true echo $? : echo $? :的妙用: 1. 可做占位符 (1) 用在if里: if condition then : else take-some-action fi (2) 用在二元运算符: : ${username=`whoami`} 如果不要 :,则会包错,除非username刚好是一个命令。因为这里就是替换username的实际东西放在这里,然后执行(但我还不知道这个有什么用) 2. 可做查看是否变量为空 : ${HOSTNAME?} ${USER?} 这会把后面跟的空的环境变量等变量打印出来,注意加? 3. 同>>连用,update文件的access/modification time : >> filename 效果同touch !的作用: inverts后面跟的命令的退出码,如 if [ $name != "cow" ];then ... *的作用: shell中,*表示0个或多个字符(和正则式中一致) **在shell计算中,用于指数运算 ?表示字符,和做测试的: shell中,?表示1个字符(不同于正则式中表示0个或1个) (1)同C语言三目运算符 (2)在参数替代式中,测试前面的变量是否设置,见上面 : 的作用。 ${}参数替代式:就是把参数放里面,代表参数值 $*,$@ 位置参数,两者只有在被引号引起的时候表现不同,因为前者把所有的参数当做一个参数,后者把每个参数都用引号隔开.你用for arg in "$*",你就可以看出差别了,这句话只会循环一次。 $? 刚启的进程或内置命令(无进程)的退出码 $$ 本脚本执行进程的id () 命令组,即subshell,如(a=hello; echo $a),是启动一个子shell来执行命令组,所以,命令组中的变量,在()外面是看不到的 数组初始化: array=(value1 value2 value3) brace常用: 1. brace扩展:即{value1,value2}可被展开为value1或者value2 grep linux file*.{txt,htm*} 可以匹配txt和htm*结尾的,{}里面一定要有两个以上,只有一个是错的。 echo {file1,file2}\:{\ A," B",' C'} 的结果是 file1: A file1: B file1: C file2: A file2: B file2: C 注意:{}内的空格,需要用\, "", ''来包含。 2. brace代码块: {}也可以做代码块,与()的区别是,不会启动子shell,且{}可以用其他的变量,{}的变量也可以在外面可见,即都是全局的概念。 另外,一个重要的特点是,{}可以被重定向,如: { read line1 read line2 } < $file { echo "haha" echo "hehe" } > $file 3. brace用在find后,指代find找到的文件的路径集合 find ./ -name "*.h" -exec ls -l {} \; 其中 -exec引出后面的命令,{}指代找出的路径,;用于命令结尾,但是是给exec用的,所以用\转义一下,避免shell使用它。如果不用{}来处理文件,就不写{}。 (-ok 有时替代-exec,它和exec一样,只是是交互性的,对每个路径,先提示你,再执行) 小括号常用: 1. 测试,还有[[]]这种测试。 2. 引用数组元素: 数组元素下标从0开始!!!! array[0]=cow; echo ${array[0]} 3. 正则式中用于给出一个字符集合,匹配其中任何一个 command &>filename: 把在>前加个&,表示把stdout和stderr都导出到filename \<\> 用做正则式中的单词界限: grep '\<the\>' textfile 只查找the单词的,但不关心 *the* 的。 dash的好处: 1. 用stdout/stdin替代文件,并和| > < 结合完成处理功能: grep linux file1 | diff file2 - 表示本来diff后跟两个文件做参数,但是用-替代,即原本期待为文件名的地方,这里将会取自标准输入。而这里由于shell管道,所以,标准输入其实来自|左边的命令。对于stdout也同样的道理,如果这里期望的是个写出的文件名,那么如果你用-替代的话,实际是会写到stdout上。但显然,打印到屏幕上,很多时候是没有用,所以一般是和管道或重定向符连用。 注意,- 的作用是命令个人的行为,不是shell的操作符,和|><等不一样。不过好多命令都提供-功能。 2. 在参数默认值中: value=${1:-2} 表示,如果没有 $1的话,value值为2,否则为$1。2可以换为$引出的其他变量 3. cd - 退回到上次cd的地方! 等于 cd $OLDPWD 第4章 变量和参数介绍 $var 是 ${var}的简写,后者使得一些情况下不会出错 设置一个变量为空,可以用var= 或者 unset var 空白符和引号: a=$(ls -l) echo $a #各种空白符被当作echo的参数的分割符了 echo "$a" #各种空白符被保留,比如tab和换行会被正确打印 bash变量是什么? 是字符串,只有当某个变量的值全是数字时,才可能在某种context下进行比较和运算 (1) 非数字和数字运算,结果为那个数字: e="" #令e为空字符串 let "e+=1" echo "e=$e" # e=1 所谓数字,是说那个变量的值全部是数字。 (2) 字符串和数字的简洁替代: c=BB34 d=${c/BB/12} #表示把变量c中的BB换为12,这个和${1:-2}那种设置参数默认值的格式很像 echo $d #1234 (3) 非数字和非数字运算: 结果是未定义的 特殊变量类型: (1)local: 函数或代码块中的变量 (2)environmental:环境变量 如$IFS,用于分割参数 var="'(]\\{}\$\"" echo $var # '(]\{}$" echo "$var" # '(]\{}$" Doesn't make a difference. echo IFS='\' echo $var # '(] {}$" \ converted to space. echo "$var" # '(]\{}$" (3)positional: 位置变量 $1, $2, ... $9, ${10}, ... 其中$0还是脚本在执行时写的路径 $*和$@指代所有的变量,$#指代参数的个数(不包含$0) 第5章 引号 打印ascii字符\0表示八进制,\x表示十六进制 echo -e "\058\x59" 给变量用八进制,十六进制赋值: value=$'\058\x59' #注意用$'..' 这种形式来做 echo $value //// 无数个如何理解: 比如这个例子: ////z 其实是//z,因为从左往右,每一个/escape后面一个/,所以是//z。 如果是/////z,结果还是//z,因为最右边一个/,由于是z,没有可escape的东西,它等效于没有它 更多的/同理 \ 转义换行符: var=\ echo "$var" 这个结果是\把换行符escape掉,似的 var=echo "$var" 就是说,有一个赋值操作,然后后面 "$var"是一个企图被执行的命令,这会报错,找不到这个命令。 恩?看不出来吗,所有的命令,如果太长了,都要用\来换行,表示转义换行符,表示下面一行也要连接上来 ""可以防止参数分割:(用'',或者用\转义一下中间的空格也一样) ls -l "cow.txt sheep.txt" 的结果是cow.txt sheep.txt作为一个文件名传给ls,然后ls报错,找不到这个文件。 ""中只有三种特殊字符可以保持特殊: $,/,`` 第6章 退出 默认的exit,返回的是exit上面最后一条命令的返回码。等于exit $?或者不写exit. 重要的是,一个shell函数的返回码,也是用$?看的,所以shell函数的返回码,就是函数中最后一条命令的返回码。 第7章 测试 if测试的是命令exit后的返回值,为0为真。 命令至少有这么几种: (1) test和[] :这两个命令,都是shell内置命令(虽然在/usr/bin下都有,但是不会用系统那个),意义相同,参数都是“比较式”,返回0或非0。但注意 []中,不能用 && || > <。所以,如果有多重条件,[]中用 -a 表示 and,-o表示or,或者在两个[]之间用 ||和&& (2) 其他任何系统命令 (3) [[]] 这是bash扩展的运算符,可以看做内置命令,参数也是“比较式”,好处是和C语言语法近似: && || > < 可以在[[]]中用,但在[]中会报错.所以,实际上[[]]啥都可以用!! (4) (())这个我发现很有用,它用于测试数学表达式的值,只用 < <= > >= 这几个,可以用在if或者for,while中!!不能用-引出的,形式上和C语句相似,所以看起来只是[[]]的一部分功能,但是和C写法一样!! if基本语法: if [ condition ];then cmd1 cmd2 elif [ condition ];then cmd3 cmd4 else cmd5 fi 运算符号: 1. file test operator:n多,可联系的是,在我们经常用的find命令中,用 -type 参数给出的也有文件类型。 2. comparison operator (这和我们的C体验相反,数字比较要用-引出的英文,字符串比较却用比较数字的 = > <等) integer: -eq -ne -gt -ge -lt -le < <= > >= (后面4个在(())中用到) string: = 和 == 一样 != < > -z -n 3. ! 可以在1和2之前,表示取反 4. compound comparison -a 用在[]或test中, 等于[[]]中用&& -o 用在[]或test中, 等于[[]]中用|| 例子: if [ -f /usr/bin/netscape -a -f /usr/share/doc/HTML/index.html ];then ... 测试式中的变量,一定要加"",因为有很多说不清楚的历史设计问题,导致加与不加,结果不同!!!! 比如你可以测试下一个未定义的变量是否为空,你用[ -n $string ]和[ -n "$string" ]的结果是不同的,前者非空,后者为空.书上的解释是: [ -n $string ]其实-n看到的是],所以认为非空,这真是好笑. 第8章 操作符 ** 指数 一些位运算,和C一样,参见P59 数学运算方式: (1) 整数运算: 除了let比较简洁,$(())比较熟悉之外,在书57页还介绍了好多其他的方法,我去,真不人性化,懒得理 (2) 浮点运算: shell不支持!!! 只能用外置命令,如bc 如果把一个“浮点数”赋为一个变量,那个变量仅仅是一个带点的字符串。 以任意进制赋值的方法: 0开头的是8进制的,0x开头的是16进制的 n#开头的是n进制的,比如2#11表示3.但是n的范围是2到64 注意,赋值可用 let 形式,直接var=n#11 这只会被当做字符串. 四、进阶: 第9章 变量重访问 内部变量:一大堆,P64开始, 有好多有趣和有用的,其中有个IFS(input filed separator),这个作用是设置其他的分割参数的,取代默认的白空格. 还有$SECONDS,居然是保存这个脚本到现在执行了的秒数 $! 在后台跑的最后一个任务的PID $$ 当前这个脚本的PID,这个拿来,常作为唯一标识,比如创建临时文件 shell的字符串处理: 有大量内置的字符串处理,基本就是用${.....}模样的式子,或者用expr也可以处理。除了一些简单的,如字符串长度外,他们不太统一,难记,比较混乱。 其实,在上面已经接触过一些简单的了,比如${string/substring/replacement}做替换。如果要查阅,见P80 如果不考虑效率 和书写简介,还是用awk吧,由于awk期望文件名做参数,用法如下: String=23skidoo1 mv cow.txt `echo | awk '{print substr("'"${String}"'",3,4)}'` 这里,mv的第二个参数是用echo来使得awk不必等待文件,但是注意到这里其实是有两'',它们之间是"${String}",这就是命令的构造法!!! 以前我的思路是echo $String | awk ... 这样来处理也行。但是我们看到,虽然你对awk熟悉,这样写远远没有shell内置的来的简洁。 参数替代 如${1:-2}这种上面已经见过了,就是参数1没有设置的话,另其值为2。还有其他意义的替代,见P85,他们的好处是,不必写复杂一些的if或case语句,一句话就搞定。缺点当然是不直观,只有经常用他们的人才会记得住。 长度: ${#var} 返回 var字符串的长度 ${#array[*]} 和 ${#array[@]} 返回array数组中元素个数 随机数: $RANDOM 这个东西,你每访问一次就是一个随机数,范围为 0-32767 (14位) 注意的是:这个东西,和C的 random/srandom函数不同,C中的表现是,你先用srandom设置一个seed,然后用random就可以返回一系列随机数了,默认的seed为1,就是你不调用srandom的话。这样,每次运行C程序,你得到的是一个相同的随机序列。但是bash的$RANDOM已经包含了"srandom"了,即:每次运行shell,序列是不一样的!!!!!! 除非你自己指定RANDOM的seed。指定方法就是直接对RANDOM赋值,比如RANDOM=1. seed随机化: 我觉得可以用 date +%s 返回的秒数即可。但是书上从/dev/urandom取这个数,我汗,代码如下: SEED=$(head −1 /dev/urandom | od −N 1 | awk '{ print $2 }') 第10章 循环和分支 写个典型的while循环吧: while [ "$count" -lt "$MAXCOUNT" ] do ... #do sth let "count += 1" done 再看看for循环: for i in "1 2 3" 和 for i in 1 2 3 效果是不同的。 前者由于加了引号,将是一个东西而已,只循环一次。注意,这里"1 2 3"可能是一个变量,如 $files,它可能包含一些由白空格分开的元素,这都没有关系,关系只是,$files 本身在for语句中,是否用""包围起来了,这个才决定循环一次,还是多次。 另外,for中可以用shell匹配,如 for file in [jx]* 将匹配所有以j,x开头的文件,还可和find结合,处理一定的文件 或者,复杂的命令也可: for name in $(awk 'BEGIN{FS=":"}{print $1}' < "$PASSWORD_FILE" ) 最后,写个更惊奇的吧: ———— 原来 for可以利用(())来写成C格式的for啊,以前一直被一些书误导了...方便吧...好吧,while中一样可以用: for ((a=0;a<10;a++)) do echo $a done until的用法: until [ "$var1" = "end" ] do ... # 输入end才退出 done 好吧,这里的continue n表示到第几层循环继续。最里面的循环为1,外面为2,类推。 case语句其实和if不同,不侧重条件,它主要用于shell匹配!!!! 如判断是否全为英文字母: case "$1" in *[!a-zA-Z]*|"") echo "cow!";; *) echo "OK";; esac 第11章 内置命令 echo 不说了,如果在shell里用echo,用的是系统内置的,不是系统的那个 printf 只能说这个太好用了,和C相同,只是没有括号和分割的逗号 printf "Pi to 2 decimal places = %1.2f" $PI sprintf ?木有sprintf,不过可以这样模拟: Pi12=$(printf "%1.12f" $PI) read 这个吗,很简单,有个-t用于timeout的,有个-n3用于一次读入3个字符,即读够自动退出!!!!这个够爽吧。 不过这里给的例子是告诉我们,箭头等键的输入是3个字符,所以这里用 read -n3 来读取: arrowup='\[A' arrowdown='\[B' arrowrt='\[C' arrowleft='\[D' insert='\[2' delete='\[3' SUCCESS=0 read -n3 key 判断的语句可以用grep(其实用case更简单) echo −n "$key" | grep "$arrowup" if [ "$?" −eq $SUCCESS ] then echo "Up−arrow key pressed." exit $SUCCESS fi 用read来读取文件中的每一行,每一个字段(见一部分中的例子) 如果你不用这种方法来作的话,那么大概只能用外置命令来做,除非你全部都用外置命令来做,否则你只是用外置命令取一行,再用shell处理的话,会导致重复调用外置命令。取某一行的命令,可以用sed的行号匹配等来轻易完成。 pushd,popd,dirs 用于用一个栈保存你的cd记录,使得可以容易回到早先压入栈的路径里。pushd dir就是cd到dir,并且将这个路径压栈的意思,popd用于退出当前目录到上一个目录,并将当前路径出栈的意思。dirs列出当前栈的情况。 set如果不带参数跑的话,是列出shell中所有设置的环境变量。如果带参数,有些复杂,不理会了。 getopts 这个是shell内置的用于获取以-/+开头的option,它和getopt命令和库用法一致,需要的话见P143 source和.:如果在命令行下,等于执行一个脚本,如果在文件中,等于包含入令一个脚本,比如共享的数据,或者函数。 exec 这个就是替代shell进程本身,来执行一个命令,导致的结果是命令结束,"shell"结束。 wait 等待进程终止,和API的wait是一样的,启动多条命令或子shell在后台运行,然后你再wait.注意,这里的wait,等待的是多条后台命令结束!!!!全部结束后,它才返回。 典型的启动子shell来做并行计算如: ( count=0 while [ "$count" -lt 5 ] do echo 1 sleep 1 let "count+=1" done )& ( count=0 while [ "$count" -lt 10 ] do echo 2 sleep 1 let "count+=1" done )& wait echo "fuck!" job的标识见P153,其中$!表示最后一个后台任务的PID |
最近读者: