分类: Python/Ruby
2009-09-08 12:36:45
SHELL编程基础
二、特殊变量:
$0 这个程序的执行名字
$n 这个程序的第n个参数值,n=1..9
$*($@) 这个程序的所有参数
$# 这个程序的参数个数
$$ 这个程序的PID
$! 执行上一个指令的PID
$? 执行上一个指令的返回值
四、几个特殊字符表示
\b 退回
\c 打印一行时没有换行符 这个我们经常会用到
\f 换页
\r 回车
\t 制表
\v 垂直制表
\\ 反斜线本身
五、判断文件的属性
格式:-操作符 filename
-e 文件存在返回1, 否则返回0
-r 文件可读返回1,否则返回0
-w 文件可写返回1,否则返回0
-x 文件可执行返回1,否则返回0
-o 文件属于用户本人返回1, 否则返回0
-z 文件长度为0返回1, 否则返回0.
-f 文件为普通文件返回1, 否则返回0
-d 文件为目录文件时返回1, 否则返回0
六、测试字符串
字符串1 = 字符串2 当两个字串相等时为真
字符串1 != 字符串2 当两个字串不等时为真
-n 字符串 当字符串的长度大于0时为真
-z 字符串 当字符串的长度为0时为真
字符串 当串字符串为非空时为真
七、测试两个整数关系
数字1 -eq 数字2 两数相等为真
数字1 -ne 数字2 两数不等为真
数字1 -gt 数字2 数字1大于数字2为真
数字1 -ge 数字2 数字1大于等于数字2为真
数字1 -lt 数字2 数字1小于数字2为真
数字1 -le 数字2 数字1小于等于数字2为真
八、逻辑测试
-a 与
-o 或
! 非
九,内部变量
$# 参数个数 ,从0开始计数
$* shell的所有参数
$@ 类似$*,具体请参看文章
$- shell用的选择项
$? 上次执行的命令的返回值
$$ shell的进程标识符
$! 用&启动的最后一个命令的进程标识符
$HOME cd命令的缺省参数
$IFS 参数分隔词的字符表
$MAIL 信件文件,当其改变时给出信息you have mail ... 没用过这个^_^
$PATH 搜索命令的目录表
$PS1 提示符串,缺省为$
$PS2 后继命令行提示符串 ,缺省为>
今天介绍shell元字符的引用
===============================
> prog > file 将标准输出重定向到文件
>> prog >> file 将标准输出附加到文件
< prog < file 从文件file中获取标准输入
| p1 | p2 将p1的标准输出作为p2的标准输入
<
? 匹配文件名中的任何单个字符
[abc] 匹配文件名中abc范围内的任何字符,如0-9或a-z都是合法的
; 命令结束符,例如p1; p2表示先执行p1,再运行p2
& 命令结束符,与 ; 类似,但不等p1结束
`...` 运行...中的命令,输出的结果代替`...`
(...) 在子shell中执行...中的命令
{...} 在当前shell中执行...中的命令(很少使用)
$1,$2等 $0...$9 代表shell文件的参数
$变量 shell变量的值
${变量} 变量的值,为避免在文本连接时混淆
\ \c 将 c 字符作为字符,但 \ 后面加换行符无效
'...' 作为文本,不作任何替换
"..." 在 ... 中的$、`...` 和 \ 得到替换后,将 ... 作为文本
# 表示注释
变量=值 为变量赋值
p1&&p2 运行p1;若成功,再运行p2
p1||p2 运行p1;若不成功,再运行p2
下面我一一举列说明
一、$符号
1、echo $? 显示的是上一条指令退出状态
2、echo "$?" 效果同上
3、echo '$?' 显示的是$?
4、echo \$? 显示的是$?
5、echo "\$?" 显示的是$?
大家可能已经看出 $符号在双引号中具有特殊意义 双引号对$符号不起作用
而单引号可以将特殊字符的的特殊意义屏蔽掉,使其能显示为字符本身,反斜
杠也可以将特殊字符的特殊含义屏蔽掉,使特殊字符失去特殊含义。
二、\ 反斜杠
反斜杠的作用是将特殊符号字符的特殊含义屏蔽掉,使其还是原字符
A=1234
echo \$A 显示为$A 如果不加\将显示为1234
echo \` 显示为`
echo \" 显示为双引号
echo \\ 显示为\
三、` 反引号
反引号的功能是命令替换,将反引号中的字符串做为命令来执行,我们在用shell编程时经常用的到 将系统命令的执行结果赋给一个变量
A=`date`
echo $A 显示的不是date而是当时的时间串
比如有一文件A的内容如下
ABCDEFG
1234456
abcdefg
B=`cat A|grep 234` # 检索文件A中含有字符串234的行
echo $B 将显示为1234456
echo "$B" 将显示为什么?
echo "\$B" 将显示为什么?读者自己试试
四、" 双引号
在系统中有些特殊字符,为避免引用这些特殊字符 往往用双引号或单引号将这些特殊字符引起来,使其不具有特殊含义。
但有一部分特殊字符在引号中还是具有特殊含义,用双引号引起来是不起作用的。本文中所列的前四个特殊字符在双引号中还是特殊字符。为了使其不具有特殊含义一是用单引号引进来二是用\反斜线使其失去作用。
比如我们想原样输出这些特殊字符
echo """
echo "$"
echo "\"
echo "`"
以上不是你所期望的结果,因为双引号对它们不起作用,你只能这样才能输出这些特殊字符的原形
echo '"'
echo '$'
echo '\'
echo '`'
或
echo "\""
echo "\$"
echo "\\"
echo "\`"
将分别显示为 " $ \ `
五、其它特殊字符
大家注意到 除了前四个特殊字符外 我将其它的特殊字符都放在一块,这是因为前四个特殊字符在双引号中还是具有特殊含义,所以单独拿出来讲,除此以外的特殊字符如果你要输出这些特殊字符的原形,你就可以用双引号或单引号引起来使其失去特殊含义。
< ,>,*,?,[,]对shell有特殊含义 但你可以用双引号引起来输入这些原形
讲了这么多大家是不是已经注意到所有的特殊字符在单引号中失去特殊含义,如果你要输出特殊字符原形但又记不清那些特殊字符在双引号中不能输出原形,建议你干脆用单引号引起来。
今天介绍条件测试语句
一、if 条件语句
格式:
if 条件表达式
then #当条件为真时执行以下语句
命令列表
else #为假时执行以下语句
命令列表
fi
if 语句也可以嵌套使用
if 条件表达式1
then
if 条件表达式2
then
命令列表
else
if 条件表达式3
then
命令列表
else
命令列表
fi
fi
else
命令列表
fi
你可以进行多层嵌套 一个if语句一定要跟一个fi 表示该层条件结束 否则会造成语法错误
结合前面讲的 举例如下:
这里先讲一个条件语句中用到的命令test 表示测试test后面的条件是否为真
if test -f "$1"
then
lpr $1
else
if test -d "$1"
then
cd $1
lpr $1
else
echo "$1不是文件或目录"
fi
fi
以上的例子还可以改成如下所示
if test -f "$1"
then
lpr $1
elif test -d "$1" #elif 同else if
then
(cd $1;lpr $1)
else
echo "$1不是文件或目录"
fi
以上的例子不知您是否看懂是什么意思吗?
假如我们现在将这个例子保存为prfile
chmod +x prfile
执行刚才的程序
./prfile aaa
这个例子是检查你的输入的参数是否是一个文件 如果是就打印 如果是一个目录 先转目录再打印 如果即不是文件也不是目录给出提示
二、多重条件测试语句case
格式:
case 字串 in
模式) 命令列表;;
模式) 命令列表;;
....
esac
多重条件语句是以case 开始以esac结束 中间可以有多个条件列表 功能是测试字串和和里面的模式有没有匹配的,有就执行里面的命令列表 模式也可以是*号 表示任意字串,每个模式里面的最后要心;;双引号结束,否则会发生语法错误。
现举例如下:
case $1 in
*.c)
cc $1
;;
*.txt)
lpr $1
;;
*)
echo "未知的类型"
esac
假如将以上内容保存在文件abc中
chmod +x abc
执行 ./abc a.c 将会对文件a.c进行编译
执行 ./abc readme.txt 将会把文件通过打印机
假如我将以上内容改一下,你是否会知道它的执行结果?
case $1 in
*)
cc $1
;;
*.txt)
lpr $1
;;
*.c)
echo "未知的类型"
esac
今天介绍循环语句
一. while 循环
while 命令格式
while 条件表
do
命令表
done
执行过程
shell首先执行条件表,如果条件表的最后一条语句的退出状态为零,则执行盾环体内的命令
表,执行完后,再检查条件表,如果退出状态为零将继续执行,如此循环往复直到条件表的
最后一条语句的退出状态非零. 退出状态为零就是条件为真True.
举例说明 假如shell文件的内容如下:
Sum=0
i=0
while true #true是系统的关键词 表示真
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
if [ $i = "100" ]
then
break;
fi
done
echo $i $Sum
最后这个程序显示的是 100 5050
这个程序的运算就是将1到100加起来
下面将这个程序再改动一下
Sum=0
i=0
while [ $i != "100" ]
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
done
echo $i $Sum
改动后的程序运算结果和上面是一样 但程序比上面的要简练
在这个循环中还可以以until做为测试条件它正好与while测试的条件相反,也就是当条件为假时将继续执行循环体内的语句,否则就退出循环体,下面还用这个例子.
Sum=0
i=0
until [ $i = "100" ]
do
i=`expr $i + 1`
Sum=`expr $Sum + $i`
done
echo $i $Sum
当i不等于100时循环就是当条件为假时循环,否则就退出,而第一个例子是当i不等于100
时循环,也就是测试条件为真时循环.
二.for 循环
命令格式:
for 变量 in 名字列表
do
命令列表
done
这里的名字列表是一个由空格分隔的字符串列表,shell在执行for循环时每次依次从名字表
中取出一个字符串赋给循环变量作为变量的值.
在写for语句时,也可以省略in 名字列表部分,这表示用当前的位置参数来代替这时的名
字列表.
下面举个例子
比如在你的电脑中有两个目录,一个是aa,一个是bb在这两个目录中有5个相同的文件,但其
中一个目录中的一个或多个文件刚刚修改过,现在我忘记刚才改的是那几个文件了,那么我靠梢员冉弦幌抡饬礁瞿柯嫉奈募就知道?程序如下:
for File in a1 a2 a3 a4 a5
do
diff aa/$File bb/$File
done
下面再举一个不带名字列表的例子
for File
do
echo $Filw
done
文件内容保存在a.sh中 并可执行
我们在执行这个shell程序时命令行如下:
a.sh a1 a2 a3 a4 a5
执行结果如下:
a1
a2
a3
a4
a5
大家从这个例子中可以看到命令行的参数被逐一读入一次
三.循环控制语句
break 命令不执行当前循环体内break下面的语句从当前循环退出.
continue 命令是程序在本循体内忽略下面的语句,从循环头开始执行.
一,命令组合:圆括号和花括号
shell中有两种方法将命令组合在一起:圆括号和花括号.圆括号使shell创建一个子shell
来读取并执行括起来的名命令.左括号和右括号不论出现在命令行中的什么位置,shell都会
认为它们具有特殊的组合意义的.只有用双引号将它们括起来引用,才表示圆括号或花括号
的原义.例如:
echo a(b)
将出现语法上的错误,要想输出a(b)字符串 只能括起来
echo "a(b)"
或echo a"("b")"
这样才能被shell正确解释.
利用组合命令有什么作用呢?
一,用圆括号组合命令
圆括号的组合命令可以创建子进程运行组合程序,建立子进程的功能是很有用的,因为
子shell在组合命令中的种种操作都不会影响到当前shell的各变量的值.
例如:
子进程在执行组合命令时改变了工作目录,并在新的工作目录下执行一系例命令,执行
完后它可以不必返回原工作目录,因为子进程工作目录的改变不会影响到当前工作目录.
创建子进程后将当前的环境也同样传给子shell,当前shell中用export输出到环境中的
各变量在子shell中同样有效.
花括号也可以将命令组合在一起.左右花括号只有作为一条命令的第一个字出现时,
shell才它们含有特殊含义.
与圆括号不同的是花括号并不创建子shell,只是由当前的shell来读取并执行括起来的
命令.有时用户希望使用一组命令的顺序输出作为另一组命令的输入,此时用花括号是很方
便的.
不论是用圆括号不是花括号,退出状态都是等于最后一条括起来的命令的退出状态.
二,可以在当前shell中执行的命令
用户在使用shell时一定要了解那些是可以在当前shell中执行的命令那些不可以
可以在当前shell中执行的命令有:
break case cd continue
echo eval exec exit
export for if read
readonly return set shift
test times trap umask
until wait while
: {}
=============================================================
.chmod
chmod [who] operator [permission] filename
who
u 所有者
g 组
o 其他用户
a 所有用户
operator
+ 增加权限
- 取消权限
= 设定权限
permission
r 读权限
w 写权限
x 执行权限
s 文件属主和组set-ID
l 给文件加锁,使其他用户无法访问
t 粘性位 (如果一个目录上出现粘着位,这就意味着目录中的文件只有其属主才可以删除,即使某个同组用户具有和属主同等的权限。不过有的系统上,这个规则并不严格;如果在文件列表时看到t,则意味着脚本或程序在执行时会被放在交换区(虚存),不过由于当今的内存价格如此这低,大可不必理会文件的t应用)
示例:
chmod a+x file
chmod og-w file
chmod g+w file
chmod u+x file
chmod go+x file
.suid/guid
chmod u+s file
# ls -l a.html
-rw-r--r-- a.html
# chmod u+s a.html
# ls -l a.html
-rwSr--r-- a.html
.find
find path -options [-print -exec -ok]
-exec find命令将匹配的文件执行exec所给出的shell命令 -exec cmd {} \; (注意: {} 和 \;间的空格)
-ok 同-exec只不过要求对命令的执行进行确认
-name 匹配文件名
find . -name "*.txt" -print
find . -name "[a-z]*" -print
find . -name * -print 或 find . -print
find . -name "[a-z][a-z][0--9][0--9].txt" -print
-perm 匹配权限
find . -perm 755 -print
find . -perm -007 -print
-prune 忽略目录 (如果使用了-depth选项,则忽略-prune选项) [没有试用成功]
find /apps -name "/apps/bin" -prune -o -print
-user 匹配用户
find . -user cnscn -print
-nouser 帐户属主已被删除的文件
find . -nouser -print
-group
-nogroup
-mtime +-n 按更改时间查找文件
find . -mtime -5 -print 查找5日内更改过的文件
find . -mtime +5 -print 查找5日前更改过的文件
-newer file 查找比file更改时间新的文件
! -newerfile2 查找比file更改时间旧的文件
find . -newer a.txt
find . ! -newer a.txt
# date
2006年 09月 13日 星期三 11:16:07 CST
# touch -t 0609120000 a.txt 创建文件a.txt并指定它的更改时间
# ls -l a.txt
-rw-r--r-- 1 root root 0 09-12 00:00 a.txt
-type
f
b
d
c
p
l
-maxdepth 指定递归操作的最在目录深度
# find . -maxdepth 1 -type d
.
./tmp
./mrtg-2.14.5
./piano
./tidy4aug00
-mount 仅在当前的文件系统内进行操作
-fstype
-cpio 将查询结果进行cpio命令进行操作
find etc home apps -depth -print | cpio -ivcdC65536 -o /dev/rmt0
-exec cmd {} \;
-ok cmd {} \;
find . -type f -exec ls -l {} \;
find . -type f -exec rm -f {} \;
find . -type f -ok ls -l {} \;
find . -type f -ok rm -f {} \;
-size n[c]
-depth
-follow
.xargs
功能类似 -exec , 但和-exec不同的是它一次只是取得部分find查询到的文件结果,而-exec是一次性获取全部文件结果,容易导致参数太多
# find . -type f -name "*.mp3" -print | xargs ls -l
-rw-r--r-- 1 root root 3985349 09-07 16:05 ./piano/025.mp3
-rw-r--r-- 1 root root 3936256 09-07 16:06 ./piano/duni.mp3
-rw-r--r-- 1 root root 28 09-07 16:23 ./piano/丝丝记忆.mp3
-rw-r--r-- 1 root root 2423745 09-07 16:24 ./piano/异国情.mp3
-rw-r--r-- 1 root root 2967512 09-07 16:20 ./piano/思乡曲.mp3
-rw-r--r-- 1 root root 4567040 09-07 16:20 ./piano/星空.mp3
.echo
echo -n "string..." 不换行输出
echo -e "string...\c" 同上
.read variable1 variable2 从标准输入读内容到变量
# read name
sssssssssss
# echo $name
sssssssssss
.文件重定向
cmd > file 结果输出到文件file
cmd >> file 结果附加到文件file
cmd 1> file 结果输出到文件file
cmd 2> file 标准错误输出到文件file
cmd > file 2>&1 结果和标准错误输出到文件file
cmd < file1 > file2 命令以file1为输入, 然后以file2为输出
cmd <
>ssssssss
>ssaaaaaaa
>HERE
cmd <&m 把文件描述符m作为标准输入
cmd >&m 把标准输出重定向到文件描述符m中
cmd <&_ 关闭标准输入
.&&
cmd1 && cmd2 顺序执行命令cmd1和cmd2 (只有cmd1执行成功才会执行cmd2)
.||
cmd1 && cmd2 选择执行cmd1和cmd2(如果cmd1执行失败,则执行cmd2)
awk '{print $1,$2}' acc.qtr >qtr.tmp || echo "Error" #如果前面执行错误,则报告错误
.() 一般很少单独执行而是和&& || 组合运行
(cmd1;cmd2;cmd3...) 在当前shell中执行一组命令
mail ... || (echo "Error" | mail; exit)
.{} 一般很少单独执行而是和&& || 组合运行
{cmd1;cmd2;cmd3...} 在子shell中执行一组命令
.正则表达式
^ 匹配行首
$ 匹配行尾
* 匹配0个或多个前面的单个字符
[] 匹配[]内的任意一个字符
\ 转义
pattern\{n\} 匹配n次pattern
pattern\{n,\} 匹配最少n次pattern
pattern\{n,m\} 匹配n到m次pattern
经常使用的正则表达式
^
$
^[the]
[Ss]igna[lL]
[mayMAY]
^User$
[tty]$ 以tty结尾的行
\.
^d..x..x..x
^[^l]
[.*0]
[000*] 000或更多个
[iI] i或I
[iI][nN] in或iN或In或IN
[^$] 空行
^......$ 含6个字符的行
[a-zA-Z] 任意单个字符
[a-z][a-z]* 到少一个小写字母
[^0-9\$] 非数字或$
[^0-0A-Za-z] 非数字或字母
[123] 1或2或3
[Dd]evice device或Device
De..ce 以De开始中间两个任意字符最后是ce
\^q 以^q开始的行
^.$ 只有一个字符的行
^\.[0-9][0-9] 一个句点和两个数字开始的行
'"Device"' Device
De[Vv]ice\. Device 或 device
[0-9]\{2\}-[0-9]\{2\}-[0-9]\{4\} dd-mm-yyyy
[0-9]\{3\}\.[0-9]\{3\}\.[0-9]\{3\}\.[0-9]\{3\} 匹配IP地址
[^.*$] 匹配任意行
.grep
-E 匹配正则表达式
使用例:
MESSAGE_TYPE="AA BB CC"
RECOVERY_FILE=my.log
BACKUP_FILE_COUNT=3
BACKUP_DIR=/rtt/its/data/recovery/backup/
CP="/bin/cp -p"
RM="/bin/rm -f"
#get parameter from the command line
while getopts D:h options
do
case $options in
D) date=$OPTARG;;
h) echo 'USAG: recovery_alert -D date'
exit 0;;
esac
done
#parameter check: the business date(format:yyyy/mm/dd) is expected
if [ $date = "" ]; then
#parameter not be inputted
exit 1
elif [ `expr "$date" : "[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]$"` -lt 10 ]; then
#parameter format is not yyyy/mm/dd
echo 'business date(format:yyyy/mm/dd) error!'
echo 'USAG: recovery_alert -D date'
exit 1
fi
#get the year, month, date from the business date
year=`echo $date | cut -c 1-4`
month=`echo $date | cut -c 6-7`
day=`echo $date | cut -c 9-10`
#get the recovery file name for the business date
Filename=${RECOVERY_FILE}.${year}${month}${day}
if [ ! -f $Filename ]; then
#the recovery file is not exists
exit 0
fi
#check the recovery file for message leak
for CurType in $MESSAGE_TYPE
do
#count the be leaked messages for 4P, QM, Q1, CP, BP
DataCount=`grep $CurType $Filename | wc -l | awk '{print $1}'`
if [ $DataCount -ne 0 ]; then
TotalDataCount="${TotalDataCount}[${CurType}:${DataCount}]"
fi
done
if [ ! $TotalDataCount = "" ]; then
#if the message leakage is detected, write it into the system log
Message="[$HOSTNAME][$date] Feed data leakage be detected! "${TotalDataCount}
logger -p daemon.notice $Message
#backup the file
$CP $Filename $BACKUP_DIR
if [ $? -ne 0 ]; then
echo "failed to copy the recovery file($Filename) into the backup directory($BACKUP_DIR)"
exit 1
fi
fi
#delete the old files in the backup directory
StartIndex=`expr $BACKUP_FILE_COUNT + 1`
FileList=`ls $BACKUP_DIR | sort -rn | sed -n "$StartIndex,$ p"`
for CurFile in $FileList
do
$RM ${BACKUP_DIR}${CurFile}
if [ $? -ne 0 ]; then
echo "failed to delete the recovery file(${BACKUP_DIR}${CurFile})"
exit 1
fi
done
exit 0
关于 >/dev/null 2>&1的问题
shell中可能经常能看到:>/dev/null 2>&1
命令的结果可以通过%>的形式来定义输出
分解这个组合:“>/dev/null 2>&1” 为五部分。
1:> 代表重定向到哪里,例如:echo "123" > /home/123.txt
2:/dev/null 代表空设备文件
3:2> 表示stderr标准错误
4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于1
5:1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 "1>/dev/null"
因此,>/dev/null 2>&1也可以写成“1> /dev/null 2> &1”
那么本文标题的语句执行过程为:
1>/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。
2>&1 :接着,标准错误输出重定向 到 标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。
说清楚了吗,大家理解下吧!
顺便对比述说下这么用的好处!
最常用的方式有:
command > file 2>file 与command > file 2>&1
它们 有什么不同的地方吗?
首先command > file 2>file 的意思是将命令所产生的标准输出信息,和错误的输出信息送到file 中.command > file 2>file 这样的写法,stdout和stderr都直接送到file中, file会被打开两次,这样stdout和stderr会互相覆盖,这样写相当使用了FD1和FD2两个同时去抢占file 的管道。
而command >file 2>&1 这条命令就将stdout直接送向file, stderr 继承了FD1管道后,再被送往file,此时,file 只被打开了一次,也只使用了一个管道FD1,它包括了stdout和stderr的内容。
从IO效率上,前一条命令的效率要比后面一条的命令效率要低,所以在编写shell脚本的时候,较多的时候我们会command > file 2>&1 这样的写法。