百度空间 | 百度首页 
 
查看文章
 
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

类别:知识体系 | 添加到搜藏 | 浏览() | 评论 (0)
 
最近读者:
 
网友评论:
发表评论:
姓 名:
网址或邮箱: (选填)
内 容:
验证码: 请点击后输入四位验证码,字母不区分大小写
      

     

©2009 Baidu