1. 变量与算术
POSIX Shell 为内嵌(inline) 算术提供T 一种标记法,称为算术展开(arithmetic expansion) 。Shell 会对$(( …)) 里的算术表达式进行计算,再将计算后的结果放回到命令的文本内容。
1.1. 变量与环境
有两个相似的命令提供变量的管理,一个是readonly ,它可以使变量成为只读模式; 而赋值给它们是被禁止的。一个是export ,将变量放进环境中。
1.1.1 . export, readonly 命令
语法
export name[=word] …
export -p
readonly name[=word]…
readonly -p
用途
export 用于修改或打印环境变童,readonly 则使得变量不得修改。
主要选项
-p 打印命令的名称以及所有被导出( 只读) 变量的名称与值,这种方式可使
得Shell 重新读取输出以便重新建立环境( 只读设置) 。
例如:
[root@local~]#hours_per_day=24 # 赋值
[root@local~]#readonly hours_per_day # 设为只读模式
[root@local~]# PATH=$PATH:/usr/local/bin # 更新PATH
[root@local~]#export PATH # 导出它
[root@local~]#export –p # 显示当前的环境
export CDPATH=":/home/tolstoy"
export DISPLAY=":0.0"
export ENV="/home/tolstoy/.kshrc"
export EXINIT="set ai sm"
export FCEDIT="vi”
…
1.1.2 .env 命令
语法
env [-i 」[var=value …][command_name[arguments …]]
用途
当command_name 被env 执行时,可针时被command_name 继承而来的环境
有更细致的控制。
主要选项
-i 忽略继承的环境,仅使用命令行上所给定的变量与值。
例如:
Env 临时改变变量
[root@local~]#env -i PATH=$PATH HOME=$HOME
#-i 选项是用来初始化(initializes) 环境变量的,也就是丢弃任何的继承值,仅传递命令行上指定的变量给程序使用。
1.1.3 . unset 命令
语法
unset[-v 」variable
unset -f function
用途
从当前Shell 删除变量与函数。
主要选项
-f 解除( 删除) 指定的函数。
-v 解除( 删除) 指定的变量。没有任何选项时,这是默认行为模式。
例如:
unset 命令解除变量设置
[root@local~]#unset full_name
[root@local~]#unset -v first middle last
使用unset -f 删除函数
[root@local~]#
who_is_on(){
who I awk ‘{print $1 }’| sort –u
}
unset -f who_is_on
2. 参数展开
参数展开(parameter expansion) 是Shell 提供变量值在程序中使用的过程。最简单的形式如下所示:
reminder="Time to go tv the dentist!” # 将值存储在reminder 中
sleep 120 # 等待两分钟
echo $reminder # 显示信息
在Shell 下,有更复杂的形式可用于更特殊的情况。这些形式都是将变量名称括在花括号里(${variable}}, 然后再增加额外的语法以告诉3he11 该做些什么。如下:
reminder="Time to go to the dentist!“
sleep 120
echo _$(reminder)_
2.1. 展开运算符
表 1 :替换运算符
运算符 |
替换 |
${varname:-word} |
如果 varname 存在且非 null ,则返回其值;否则,返回 word, 用途 : 如果变量未定义,则返回默认值。 范例 : 如果 count 未定义,则 ${count:-0} 的值为 0 。
|
${varname:=word} |
如果 varname 存在且不是 null ,则返回它的值 ; 否则,设置它为 word ,并返回其值。 用途 : 如果变量未定义,则设置变量为默认值。 . 范例 : 如果 count 未被定义,则 ${count:=0} 设置 count 为 0 。 |
${varname:?message} |
如果 varname 存在且非 null ,则返回它的值 ; 否则,显示 varname:message ,并退出当前的命令或脚本。省略 message 会出现默认信息 parameter null or not set 。注意,在交互式 Shell 下不需要退出。 用途 : 为了捕捉由于变量未定义所导致的错误。 范例 :${count:?"undefined"} 将显示 count: undefined! ,且如果 count 未定义,则退出。 |
${varname:+word} |
如果 varname 存在且非 null ,则返回 word; 否则,返回 null 。 用途 :. 为测试变量的存在。 范例 : 如果 count 已定义,则 ${count:+1} 返回 1( 也就是“真” ) 。 |
注意:表 1 里每个运算符内的冒号 (:) 都是可选的。如果省略冒号,则将每个定义中的“存在且非 null ”部分改为“存在”,也就是说,运算符仅用于测试变量是否存在。
表 2 :替换运算符
运算符 |
替换 |
${variable#pattern} |
如果模式匹配于变量值的开头处,则删除匹配的最短部分,并返回剩下的部分。 例如: ${path#/*/} 结果: tolstoy/mem/long.file.name |
${variable##pattern} |
如果模式匹配于变量值的开头处,则删除匹配的最长部分,并返回剩下的部分。 例如: ${path##/*/} 结果: long.file.name |
${variable%pattern} |
如果模式匹配于变量值的结尾处,则删除匹配的最短部分,并返回剩下的部分。 例如: ${path%.*} 结果: /home/tolstoy/mem/long.file |
${variable%%pattern} |
如果模式匹配于变量值的结尾处,则删除匹配的最长部分,并返回剩下的部分。 例如: ${path%%.*} 结果: /home/tolstoy/mem/long |
注:假设变量 path 的值为 /home/tolstoy/mem/long.file.name 。
2.2. 位置参数
所谓位置参数(positional parameter) ,指的是Shell 脚本的命令行参数(argument); 同时也表示在Shell 函数内的函数参数。它们的名称是以单个的整数来命名。出于历史的
原因,当这个整数大于9 时,就应该以花括号({}) 括起来:
echo first arg is $1
echo tenth arg is is${10}
下面介绍的特殊“变量”提供了对传递的参数的总数的访问,以及一次对所有参数的访问:
$#
提供传递到She11 脚本或函数的参数总数。举例如下:
while [$#'!= 0 ] # 以shift 逐渐减少$# ,循环将会终止
do
case $1 in $*
。。。 # 处理第一个参数
esac
shift # 移开第一个参数
done
shift
$*,$@
一次表示所有的命令行参数。这两个参数可用来把命令行参数传递给脚本或函数所
执行的程序。
$*”
将所有命令行参数视为单个字符串。等同于”$1 $2. ,. " 。 $IFS 的第一个字符用来作为分隔字符,以分隔不同的值来建立字符串。举例如下:
printf "The arguments were %s/n". ,“$*”
$@
“将所有命令行参数视为单独的个体,也就是单独字符串。等同于,"$1” ,”$2”… 。这是将参数传递给其他程序的最佳方式,因为它会保留所有内嵌在每个参数里的任何空白。举例如下:
lpr "$@" # 显示每一个文件
2.3. 算术展开
表fi-4; 算术运算数
运算符 |
意义 |
顺序 |
++ -- |
增加及减少,可前置也可放在结尾 |
由左至右 |
+ - ! ~ |
一元(unary) 的正号与负号; 逻辑与位的(bitwise) 取反 |
由右至左 |
* / % |
乘法、除法,与余数 |
由左至右 |
+ - |
加法,减法 |
由左至右 |
<< >> |
向左移位、向右移位 |
由左至右 |
< <= > >= |
比较 |
由左至右 |
== != |
相等与不等 |
由左至右 |
& |
位的AND |
由左至右 |
^ |
位的Exclusive OR |
由左至右 |
| |
位的OR |
由左至右 |
&& || |
逻辑AND 和OR |
由左至右 |
?: |
条件表达式 |
由右至左 |
= += -= 等 |
赋值运算 |
由右至左 |
可利用圆括号将子表达式语句块括起来。例如:
$((3>2)) 的值为 1
$(((3>2) || ((4<=1))) 也为 1
[root@local~]#i=5
[root@local~]# echo $((i++)) $i
5 6
[root@local~]# echo $((++i)) $i
7 7
3. 退出状态
每一条命令,不管是内置的、 Shell 函数,还是外部的,当它退出时,都会返回一个小的整数值给引用它的程序,这就是大家所熟知的程序的退出状态 (exit status) 。
3.1. exit 命令
语法
exit [exit-value]
用途
目的是从 Shell 脚本返回一个退出状态给脚本的调用者 .
主要选项
无
3.2. 退出状态值
表 5: POSIX 的结束状态
值 |
意义 |
0 |
命令成功地退出 |
>0 |
在重定向或单词展开期间 (~ ,变量,命令,算术展开,以及单词切割 ) 失败 |
1-125 |
命令不成功地退出。特定的退出值的含义,是由各个单独的命令定义的 |
126 |
命令找到了,但文件无法执行 |
127 |
命令找不到 |
>128 |
命令因收到信号而死亡 |
3.3. 逻辑的NOT ,AND 与OR
示例 1:
if ! grep pattern myfile > /dev/null
then
…# 模式不在这里
fi
示例 2:
if grep patternl myfile && grep pattern2 myfile
then
…#myfile 包含两种模式
Fi
示例 3:
if grep patternl myfile || grep pattern2 myfile
then
…#myfile 一个或另一个模式出现
fi
注意:这两种都是快捷(short-circuit) 运算符,即当判断出整个语句块的真伪时,Shell 会立即停止执行命令。举例来说,在commandl&&command2 下,如果commandl 失败,则整个结果不可能为真,所以command2 也不会被执行,以此类推,commandl || command2 指的就是: 如果commandl 成功,那么也没有理由执行command2 。
3.4. test 命令
test 命令有另一种形式 :[…] ,这种用法的作用完全与 test 命令一样。因此,下面
是测试两个字符串是否相等的两个语句 :
if test "$strl”= ”$str2”
then
…
fi
第二种:
if [ “$strl ” = “ $str2” ]
then
…
Fi
表6 : test 表达式
运算符 |
如果… 一则为真 |
string |
string 不是null |
-b file |
file 是块设备文件 |
-c file |
file 是字符设备文件 |
-d file |
file 是目录 |
-e file |
file 存在 |
-f file |
file 为一般文件 |
-g file |
file 有设置它的setgid 位 |
-h file |
file 是一符号链接 |
-L file |
file 是一符号链接( 等同于-h) |
-n string |
string 是非空 |
-p file |
file 是命令管道文件 |
-r file |
file 是可读的 |
-S file |
file 是socket |
-s file |
file 不是空的 |
-t n |
文件描述符指向一终端 |
-u file |
File 有设置它的setuid 位 |
-w file |
File 是可写入的 |
-x file |
File 是可执行的 |
-z string |
字符串为null |
s1 = s2 |
字符串1 与字符串2 相同 |
s1 != s2 |
字符串1 与字符串2 不同 |
n1 –eq n2 |
整数1 等于整数2 |
n1 –ne n2 |
整数1 不等于整数2 |
n1 –lt n2 |
整数1 小于整数2 |
n1 –gt n2 |
整数1 大于整数2 |
n1 –le n2 |
整数1 小于或等于整数2 |
n1 –ge n2 |
整数1 大于或等于整数2 |
4. 控制结构
4.1. if-elif-else-fi 语句
一般语法如下 :
If pipeline
[pipeline…]
then
statements-if-ture-1
[elif pipeline
[pipeline … ]
then
statements-if-true-2
…]
[else
statements-if-all-else-fails]
fi
4.2. ca se 语句
一般语法如下 :
case $1 in
-f)
…# 针对-f 选项的程序代码
;;
-d|-directory) # 尤许长选项
… 针对-d 选项的程序代码
;;
*)
echo $1:unknown option
exit 1
;;# 在"esac" 之前的;; 形式是一个好习惯,不过这里并非必要
esac
4.3. for 语句
第一种语法如下 :
for var in con1 con2 con3
do
程序段
done
解释如下:
第一次循环时,$var 的内容为con1
第一次循环时,$var 的内容为con2
第一次循环时,$var 的内容为con3
。。。
第二种语法如下 :
for (( 初始值; 限制值; 执行步长))
do
程序段
done
例如:
s=0
for((i=0;i<100;i++))
do
s=s$(($s+$i))
done
echo “the result of ‘1+2+3+…+100’is $s”
4.4. while 与until 语句
第一种语法如下 :
while condition
do
statements
done
第二种语法如下 :
until condition
do
statements
done
5. 函数
就像其他的程序语言一样,函数 (function) 是指一段单独的程序代码,定义完整的单项工作。函数在使用之前必须先定义。这可通过在脚本的起始处,或是将它们放在另一个独立件里且以点号 (.) 命令来取用 (source) 它们。
定义方式如下所示。
#wait_for_user--- 等待用户登录
#wait_for_user user [sleeptime]
wait_for_user
until who grep "$1”>/dev/null
do
sleep {$2:-30}
done
}
使用Shell 的函数架构取代test 所执行的两个字符串的比较:
# equal- 一比较两个字符串
equal(){
case "$1" in
$2) # 两字符串匹配
return 0
;;
esac
return 1# 不匹配
}
if equal “$a””$b”…
在Shell 函数里,return 命令的功能与工作方式都与exit 相同
answer_the_question()
{
return 42
}
调用如下
x=$(answer_the_question, $@)
6. Shell 的追踪与调试
[root@local~]#sh [-nvx] scripts.sh
参数:
-n: 不要执行脚本,仅检查语法错误
-v: 在执行脚本时,先将脚本的内容输出到屏幕上
-x: 将使用的脚本内容显示到屏幕上