斑竹网络专注为中小企业客户提供以管理服务为核心的IT全方位服务 https://www.sysadm.cn
分类: 系统运维
2013-11-21 15:51:10
Shell 编程语法
(整理时间:2006-10-12)
l 三种指定解释执行脚本的Shell类型的方法
1. 如果Script的第一个非空白字元不是"#",则它会使用Bourne Shell。
2. 如果Script的第一个非空白字元是"#"时,但不以"#!"开头时,则它会使用C Shell。
3. 如果Script以"#!"开头,则"#!"後面所写的就是所使用的Shell,而且要将整个路径名称指出来
注:
Bourne Shell的路径名称是:/bin/sh;
而 C Shell的路径名称是: /bin/csh
同时还可以用perl来解释执行,如 /etc/perl等;
l 常用的系统变量
$HOME 使用者自己的目录
$PATH 执行命令时所搜寻的目录
$TZ 时区
$MAILCHECK 每隔多少秒检查是否有新的信件
$PS1 在命令列时的提示号
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号
$MANPATH man 指令的搜寻路径
$BASH 其值指向bash的路径位置
$BASH_VERSION :当前所使用的bash的版本号
$DIRSTACK:当前目录的上一层目录名
$EDITOR:脚本调用的默认编辑器,一般是vi 或者是emacs
$FUNCNAME : 当前的函数名
$GROUPS :当前用户属于的组的ID
$HOSTNAME :系统的名称
$HOSTTYPE :主机类型,如I386 I686等
$IFS:内部域分隔符。这个变量用来决定Bash在解释字符串时如果字符串的分隔。默认是空格
$LINENO:这个变量记录它所在的shell脚本中它所在行的行号。一般用于调试目的。
$MACHTYPE:系统类型,如I386等
$OLDPWD:老的目录,即你切换到当前目录之前的工作目录;
$OSTYPE:操作系统类型
$PPID:一个进程的PPID,也就是它的父进程的进程ID
$PWD:工作上当(即当前所在的目录)
$SECONDS:这个脚本已经运行的时间(单位为秒)
$SHLVL:Shell层次,就是shell层叠的层次,如果是在命令行,则$SHLVL是1,如果
是在脚本是,则为2,如在脚本里调用了第二个调本文件,则在第二个脚本文
件里的这个变量为:3,依次类推;
$TMOUT:shell的超时时间,如果这个变量不为零,则当时间到了以后就退出shell,
在命令行的情况下,如果这个值不为零,则时间到了之后,就会引起一个logout
$UID:当前用户的ID号
${#string}: 字符串string的长度,也可以用expr length $string 来获得字符串的长度
${string:position} 表示截取从position位置之后的子字符串
${string: position:length} 表示从position开始截取长度为length长度的子字符串;
${string : -position } 用负值表示从字符串的后面的位置开始截取;
注:
上述的截取字符串也可以使用下列的命令:
expr substr $string $position $length
${string#$substr} 表示将字符串string中的substr子字符截去掉;
${string%$substr} 表示从右开始将字符串string中的substr子字符串截去掉;
${string/substr/replacestr} :将string中的substr用replacestr替换,只替换第一个子字符串
${string//substr/replacestr} 与上要类似,但是替换所有的substr子字符串;
${string/#substr/replacestr} 如果substr在字符串string的开头部分匹配到,则用replacestr来替换,否则不替换;如:
若string: abcABCDEF123abc,而substr是abc,则string中的第一个abc可以被替换,但是如果string是tabcABCDEF123abc ,则一个都不会被替换;
${string/%substr/replacestr} 与上面类似,但是替换最后一个子字符串;
变量的间接引用
当一个变量的值是另一个变量的名字,那可以用eval var=\$$var获得第二个变量的值,如:
var1=”abc”
abc=”12345678”
eval aa=\$$var1 则变量aa的值就是变量abc的值,也就是12345678
l 常用的特殊变量
$0 这个程式的执行名字
$n 这个程式的第n个参数值,n=1..9
$* 这个程式的所有参数
$# 这个程式的参数个数
$$ 这个程式的PID
$! 执行上一个背景指令的PID
$? 执行上一个指令的返回值
l 在Bourne Shell 中执行命令的五种方法
1. 直接下命令
这个方式和在命令列中直接下命令的效果一样。
2. 使用sh命令
sh command 这个档案必须是Bourne Shell的Script,但这个档案并不一定要设成可执行。 除此之外和直接下命令的方式一样。
3. 使用"."命令 . command
这时和使用sh命令相似,只不过它不像sh一般会产生新的process ,相反地, 它会在原有的process 下完成工作。
4. 使用exec命令 exec command
此时这个Script将会被所执行的命令所取代。当这个命令执行完毕之後,这个 Script也会随之结束。
5. 使用命令替换
这是一个相当有用的方法。如果想要使某个命令的输出成为另一个命令的参数 时,就一定要使用这个方法。我们将命令列於两个"`" 号之间,而Shell 会以 这个命令执行後的输出结果代替这个命令以及两个"`" 符号。
str='Current directory is '`pwd`
echo $str
结果如下:
Current directory is /users/cc/mgtsai
这个意思是pwd 这个命令输出"/users/cc/mgtsai",而後整个字串代替原 来的`pwd` 设定str 变数,所以str 变数的内容则会有pwd 命令的输出。
number=`expr $number + 1`
这就是先前所提要作数值运算的方法,基本上expr命令只将运算式解,而 後输出到标准输出上。如果要将某变数设定成其值,非得靠命令替换的方 式不可。这个例子是将number变数的值加1 後再存回number变数。
l 使用cut命令可以对字符串按照指定的分隔符分开,如
test=`echo $1|cut -d "@" -f1`
就是将变量1的值按照@字符分开,并将它付给test变量
l 数值测试运算符
-eq:等于则为真
-ne:不等于则为真
-gt:大于则为真
-ge:大于等于则为真
-lt:小于则为真
-le:小于等于则为真
l 字符串测试运算符
=:等于则为真
!=:不相等则为真
-z字符串:字符串长度为零则为真
-n字符串:字符串长度不为零则为真
l 文件测试运算符
-e文件名:如果文件存在则为真
-r文件名:如果文件存在且可读则为真
-w文件名:如果文件存在且可写则为真
-x文件名:如果文件存在且可执行则为真
-s文件名:如果文件存在且至少有一个字符则为真
-d文件名:如果文件存在且为目录则为真
-f文件名:如果文件存在且为普通文件则为真
-c文件名:如果文件存在且为字符型特殊文件则为真
-b文件名:如果文件存在且为块特殊文件则为真
l Test 指令的用法
test 是用来检测文件类型和比较数值大小的,具体用法如下:
用法:test <表达式> [表达式] [选项]
返回结果:根据检测的状态返回结果
各种选项返回的结果:
EXPRESSION1 –a EXPRESSION2 当EXPRESSION1和EXPRESSION2都为真
的时候,返回真结果
EXPRESSION1 -o EXPRESSION2 当EXPRESSION1和EXPRESSION2有一个
为真时返回真结果
-n STRING 当字符串长度不为零是,返回真
-z STRING 当字符串长度为零时,返回真
STRING1 = STRING2 当两个字符串相等时,返回真
STRING1 != STRING2 当两个字符串不相等时,返回真
其它的看man 说明文档
STRING1 != STRING2
the strings are not equal
INTEGER1 -eq INTEGER2
INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2
INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2
INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2
INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2
INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2
INTEGER1 is not equal to INTEGER2
FILE1 -ef FILE2
FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2
FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2
FILE1 is older than FILE2
-b FILE
FILE exists and is block special
-c FILE
FILE exists and is character special
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
-g FILE
FILE exists and is set-group-ID
-h FILE
FILE exists and is a symbolic link (same as -L)
-G FILE
FILE exists and is owned by the effective group ID
-k FILE
FILE exists and has its sticky bit set
-L FILE
FILE exists and is a symbolic link (same as -h)
-O FILE
FILE exists and is owned by the effective user ID
-p FILE
FILE exists and is a named pipe
-r FILE
FILE exists and is readable
-s FILE
FILE exists and has a size greater than zero
-S FILE
FILE exists and is a socket
file descriptor FD (stdout by default) is opened on a terminal
-u FILE
FILE exists and its set-user-ID bit is set
-w FILE
FILE exists and is writable
-x FILE
FILE exists and is executable
一、 关于变量
1、$是变量替换操作符
2、变量只有在被声明、赋值、unset或者export及代表一个信号时,才能本来面目出现,即以不带$形式出现,否则都是以$变量名的方式出现;
3、””,即一对双引号里的变量是会被替换的,即以变量的值来替换变量的名字。这叫
做部分引用,或叫弱引用。
而在’ ‘中的变量是不会发生替换的,这叫强引用。见面下面的例子
例1:example1
#! /bin/bash
a="11111111"
echo "this is double qouta:$a"
echo 'this is single qouta:$a'
上面的例子的输出结果如下,注意看两行的输出结果的不一样性。
this is double qouta:11111111
this is single qouta:$a
4、定义一个变量时,使用$变量名和${变量名}在绝部份情况时是一样,但是在某些情况下是不一样的。如在下列这种情况下:
parameter=”aaaaaa”
echo “$parameterabc” 这一句输出的结果是空值
echo “${parameter}abc” 这一句则可以输出aaaaaaabc这个字符串
5、关于${parameter:-default}和 ${parameter:=default}的用法,见下面的例程:
例2:example2
#! /bin/bash
1. username1=${testa:-`whoami`} #因为testa没有定义,所以username1就whoami
的结果,但因为使用的是“-”号,所以testa值为空
2. username2=${testa-`whoami`} # 结果与上面类似
3. username1=${testa:=`whoami`} # 结果与上面类似,但是testa有值了,也是
whoami的结果,注意与第一句相比较
4. username2=${testa=`whoami`} #与上面类似
5. testa=""
6. username1=${testa:=`whoami`} # 与4句类似,且testa有值
7. username2=${testa=`whoami`} # 与6句不同的是testa值为空,因为testa已经
设置了,虽然为空
运行后输出的结果如下所示:
The $username1 value is:root
The $username2 value is:root
The $testa value is:
The $username1 value is:root
The $username2 value is:root
The $testa value is:root
The $username1 value is:root
The $username2 value is:root
The $testa value is:root
6、${#var} ${#} ${a}
${#var}: 表示变量var 的值的字符长度;如果变量var是数组的话,则是第一个元
素的字符长度;
${#}: 表示调用脚本时,输入的参数的个数;
${@}: 表示调用脚本时,输入的参数的内容,如参数1,参数2等等
${#var[@]} :如果var变量是数组的话,则表示数组元素的个数;
${#var[*]}: :如果var变量是数组的话,则表示数组元素的个数;
7、echo $hello 和echo “$hello”在输出结果上不一样的
a. 当直接用echo $hello时,系统会跟变量中的连续的空格删除,只留一个,同时回
车换行符也删除了;
b. 当使用echo “$hello” 时,则输出时,变量中的连续空格不会被删除,还是保留;
8、关于变量的变量值的引用,即变量的间接引用
变量可以通过间接的方式来引用,如:
message=Hello
Hello=Goodbye
则 echo “${!message}”出来的值就是 Goodbye。因为,message的值是Hello,而
Hello正好又是一个变量,这样相当引用了变量Hello的值
二、 循环
1. for arg in [list]
do
command(s)
done
注意:
a..在循环每次执行中,arg将顺序的存取list中列出的变量;
b. list中可以使用 * 和?等通配符,表示当前目录下的文件,如
for a in *
或 for a in *.sh等等
c. 如果list 里列出来的不是变量,而是值,则各个值之间用空格分开,如:
for a in 12 34 45 67 89
d. 可以在list位置放一个变量,如
b=”aaaa bbbbbbbb cccccccc ddddddddd eeee”
for a in $b
则a 在循环时分别为aaaa bbbbbbbb等等
e. 如果list 省略的话,则使用$@(命令行中传过来的参数列表)替代list 如
for a
do
echo $a
done
f. 可以使用一个命令来替代list 如:
for a in `du -h`
g. for 循环的结果可以通过管道传递给其它命令,如:
for a in “$(find ./ -type f )”
do
echo $a
done |sort
2. while [condition]
do
commands
done
注意:
a. while循环可以有多个判断,但是只有最后一个才能决定是否退出循环。
b. while 循环的输入支持管道输入,如:
while read line
do
echo $line
done
这样可以直接从test.txt文件里读出数据
3. until [ conditions ]
do
command
done
注意:
a. until 是当条件为假的时候循环的,如果条件为真,则退出循环;
b.
三、 条件语句
1. case in $var
“value1”)
Command;
;;
“value2”)
Command;
;;
Esac
注意:
a. 对变量使用”” 并不是强制的,因为不会发生单词分离;
b. 每句测试行,都以右小括号)结尾;
c. Case块的结束以esac(case的反向拼写)结尾
2.Select variable [in list]
Do
Command …..
Break
done
四、 关于受限shell
作用:为了限制某些用户或脚本的权限,可以让shell受限模式。
进入退出方法:可以用 set –r 或/bin/bash –r 进入受限模式了。当要退出受限模式,
只需要执行 bash就可以了。
权限限制:在受限shell如下的操作将会被限制
1、 使用cd 更改工作路径;
2、 更改$PATH $SHELL $BASH_ENV, $ENV等的值;
3、 读取或更改$SHELLOPTS的值;
4、 输出重定向;
5、 调用的命令路径中包括有一个或多个/字符;
6、 在脚本中可能存在的其它脱离受限shell的操作;
实例:
#!/bin/bash -r
echo "Current PATH is:`pwd`"
cd /usr/local
echo "Current PATH is:`pwd`"
echo "Try to change PATH value:"
export PATH=$PATH:"/var"
export SHELL=/sbin/sh
echo $SHELLOPTS
echo "Try to redirect content into:"
echo "aaaaa" >a.txt
执行后的提示是:
Current PATH is:/root/test
./test.sh: line 3: cd: restricted
Current PATH is:/root/test
Try to change PATH value:
./test.sh: line 6: PATH: readonly variable
./test.sh: line 7: SHELL: readonly variable
braceexpand:hashall:interactive-comments
Try to redirect content into:
./test.sh: line 10: a.txt: restricted: cannot redirect output
五、 进程替换
进程替换是把一个进程的输出回馈给另一个进程。换句话说,它把一个进程的结果发送给另一个进程。
进程替换的一般形式是: >(command) 和<(command)形式。
利用进程替换可以:
1、 比较两个不同目录之间的内容,如:diff <(ls -l aaa1) <(ls -l aaa2),得出结果:
1,2c1
< ×????? 88
< -rw-r--r-- 1 root root 29 1?? 13 17:34 test1.sh
---
> ×????? 72
4d2
< -rw-r--r-- 1 root root 40 1?? 13 17:34 test2.txt
2、
注意,参考文档