Ict-创新/LPI/105.2
考生应该能够自定义现有脚本,或编写简单的新的 BASH 脚本。
关键知识领域
- 使用标准 sh 语法(循环、测试)。
- 使用命令替换。
- 测试返回值以判断命令成功或失败或其他信息。
- 执行有条件的邮件发送给超级用户。
- 通过 shebang(#!)行正确选择脚本解释器。
- 管理脚本的位置、所有权、执行和 suid 权限。
shell 脚本是一个文本文件,它告诉 shell 该做什么。
它包含用作脚本其余内容解释器的程序的名称。以 #!ProgramPath+Name 开头的行(通常是第一行)指定要使用的解释器
#!/bin/bash 或 #!/bin/sh 或 #!/usr/bin/perl -w
实际上,当系统被要求启动一个脚本时,它会读取以 #! 开头的行,启动相应的脚本解释器,该解释器依次读取脚本并执行其中包含的命令。
运行脚本的条件
脚本文件必须可由运行它的用户执行(chmod ....)。解释器必须位于脚本指定的位置:默认情况下会调用 bash。
shell 脚本中使用的语言
脚本语言取决于使用的脚本解释器。bash 有自己的语法,可以在交互模式或脚本中使用。
向脚本传递参数
脚本可以被赋予最多 9 个位置参数(适用于所有解释器)或最多 99 个参数(适用于 bash)。
在脚本内部,每个参数将被标识为 $1 到 $9 或 ${10} 到 ${99}
scriptname param1 param2 param3 param4..... param47.....$0 $1 $2 $3 $4 ${47} .....
一些特殊参数由 Bourne shell 自动设置,通常不能直接设置或修改。
参数 $n 可以通过脚本内部的 set 命令修改。(其中 n 是 1-99 适用于 bash)
set aaa bbb ccc ... $1 $2 $3
$n - 位置参数 n,最大 n=9($0 是 shell 脚本的名称)
${nn} - 位置参数 nn(对于 nn>9)
$# - 位置参数的数量(不包括脚本程序)
$@, $* - 所有位置参数
"$@" - 与 "$1" "$2" . . . "$n" 相同
"$*" - 与 "$1c$2c . . . $n" 相同,c = $IFS 的内容(默认是空格)
$? - 上一个命令的退出状态
$$ - 当前 shell 的进程 ID
$- - 当前生效的选项
$! - 上一个后台命令的进程 ID
$- 当前 shell 的名称(在本例中为 'bash')
shift 命令
shift 命令将位置参数的赋值向左移动。如果一个
脚本被这样调用
script1 aaa bbb ccc ddd
并且以下命令在脚本内部运行
# echo $1 $2 $3 # shift # echo $1 $2 $3 |
第一个 echo 命令的结果是
aaa bbb ccc
第二个 echo 命令的结果是
bbb ccc
set 和 unset 命令
unset 命令通常用于取消设置变量的值,set 命令用于从脚本内部为位置参数赋值。如果一个脚本在没有位置参数的情况下启动,并且在验证这一点后,脚本为它们分配默认值,则非常有用。
set aa bb cc dd $1 $2 $3 $4 - 将 aa 赋值给 $1,bb 赋值给 $2,cc 赋值给 $3,dd 赋值给 $4
set 命令也用于改变 bash 行为的属性。
set 的一个重要选项是
# set -o noclobber |
该命令会导致重定向符号 (>) 无法覆盖现有文件的内容。
以下列出了最常用的条件指令
if 条件分支指令
if - 允许只有在满足特定条件时才执行某些命令。
语法:(另请参阅本主题后面的“条件表达式”部分)
if <condition_is_true> ; then
run_these_commands
.................
elseif <condition_is_true> ; then
如果第一个条件不满足而此条件满足,则
run_these_commands
.................
else
如果以上所有条件都不满足,则
run_these_commands_instead
.................
fi
if 指令块的结束
<condition_is_true> 可以是以下类型
(1) 测试文件或目录的状态。
if test -e /etc/fstab ; then
或
if [ -e /etc/fstab ] ; then
(2) 命令或脚本退出代码。
if (ifconfig | grep 'ppp0') ; then
(3) 变量的内容与某个值相对应
if $1 ; then - 如果 $1 包含值,则为真
if [ "$net" = "eth0" ] ; then - 测试字符串
if test ["$#" -eq 5 ] ; then
(整数测试)
case 条件分支指令
case 通常用于根据
变量的内容,有条件地分支到多个选项之一。
语法
case <Variable> in
<choice1>)
要运行的命令
;;
choice2)
要运行的命令
;;
choice3)
要运行的命令
;;
*)
如果以上所有条件都不满足,则要运行的命令。
;;
case
case 指令块的结束
脚本中的循环
在需要多次执行一系列命令时使用。
while 条件循环指令
while 指令只要其条件(在 while 语句中定义)满足,就会一直循环并运行其块中的命令。
语法:
while <condition_is_true> ; do
run_these_commands
done
while 指令块的结束
注意:while 通常用于询问用户输入某种键盘输入,如果响应不合适,则会重复请求,直到输入正确的信息。然后退出 while 循环,程序继续执行。
until 条件循环指令
until 循环的工作方式与 while 循环完全相同,只是逻辑相反。
循环一直持续到条件满足。
语法:
until <condition_is_true> ; do
run_these_commands
done
until 指令块的结束
for 循环指令
for 指令允许在一系列命令中执行的次数与给定列表中的项目数量相同。每次循环运行时,特定变量的内容都会变成给定列表中当前项目的 value。
语法:
for variable in list ; do
run_these_commands
done
for 指令块的结束
variable = 将内容变成给定列表中每次循环的当前项目的变量名。列表也可以是一个包含项目列表的变量。
for item in ~/file1 ~/file2 ~/file3 ; do
echo "------------ Content of $item -----------"
cat $item >> ~/allfiles
done
Shell 函数是一系列存储在某个地方的命令,可以在脚本中的多个位置使用。可以通过位置参数向函数传递参数。
位置参数($1、$2、$3 ...)将成为函数的局部变量。它们使用与脚本相同的语法,但第一个($0)保持全局变量。
变量 FUNCNAME 用于与 $0 相似,用于相同的目的。
像 $#、$*、$@ 这样的特殊变量在函数中也是局部的。
所有其他变量对于脚本来说是全局的,并且可以被函数修改。
命令 return x(x=返回值)可以用作函数退出命令,并用于分配函数返回值。
语法:
FunctionName () {
command ;
command ;
}
或
function FunctionName () {
command ;
command ;
}
(有关 Shell 函数的更多详细信息,请参见上一节“自定义和使用 Shell 环境”中的函数。)
退出代码和变量 $?
所有程序(包括脚本)在进程结束时都会返回一个退出代码。退出代码有助于确定程序或脚本的成功或失败。可以通过特殊变量 $? 读取此退出代码,并用于在调用脚本中做出进一步的决策。
通常,“0”的退出代码表示成功,任何其他代码(1-255)表示某种失败。它也常被称为错误代码。
&& 和 || 条件分支
退出代码可用于根据其成功或失败执行另一个命令(仅一个)。双安培符号“&&”用于指定在退出代码成功(0)时运行的命令。双管道“||”用于指定在退出代码不成功(1-255)时运行的命令。
示例
# ifconfig ppp0 && echo "pppd running" || echo "pppd not running" |
如果命令 ifconfig ppp0 成功,则将执行命令 echo "pppd running"(&&),否则将执行命令 echo "pppd not running"。
从脚本向 root 发送邮件
有时,向 root 或其他用户发送邮件,以宣布自动脚本运行中的某些异常或成功是很有用的。通常使用的程序是“mail”。有关它使用的所有选项,请参阅 man mail。
语法1:
# mail -s "subject" destination_mail_address "message.." |
语法2:
# program | mail -s "subject" destination_mail_address |
语法3:
# mail -s "subject" destination_mail_address < message body....... EOM |
示例:
# df | mail -s "HD Space on $(date)" root |
将命令 df 的结果发送到本地 root 用户。
Bash 脚本的位置和安全性
管理脚本通常存储在 PATH 中,该路径是 /usr/local/bin 或 /root/bin。正常的访问权限为 755(rwx r-x r-x) 或为了通过防止除 root 之外的任何其他用户运行它来获得更多保护:700(rwx --- ---)。
虽然 SUID 对脚本没有任何影响,但非常旧版本的 Linux 可能会受到 SUID 设置的影响。
条件表达式
[edit | edit source]test 和 [...] 命令用于使用文件属性、字符串和整数评估条件表达式。基本格式为:test expression 或 [ expression ],其中 expression 是您正在评估的条件。在左括号之后和右括号之前必须有空格。空格还必须分隔表达式参数和运算符。如果表达式计算结果为真,则返回零退出状态,否则表达式计算结果为假并返回非零退出状态。
测试文件运算符
-a <file> 如果文件存在,则为真。
-b <file> 如果文件存在且是块特殊文件,则为真。
-c <file> 如果文件存在且是字符特殊文件,则为真。
-d <file> 如果文件存在且是目录,则为真。
-e <file> 如果文件存在,则为真。
-f <file> 如果文件存在且是普通文件,则为真。
-g <file> 如果文件存在且设置了组 ID,则为真。
-h <file> 如果文件存在且是符号链接,则为真。
-k <file> 如果文件存在且其“粘性”位已设置,则为真。
-p <file> 如果文件存在且是命名管道 (FIFO),则为真。
-r <file> 如果文件存在且可读,则为真。
-s <file> 如果文件存在且大小大于零,则为真。
-t <fd> 如果文件描述符 fd 已打开并引用终端,则为真。
-u <file>如果文件存在且其 SUID 位已设置,则为真。
-w <file>如果文件存在且可写,则为真。
-x <file> 如果文件存在且可执行,则为真。
-O <file> 如果文件存在且由有效 UID 拥有,则为真。
-G <file> 如果文件存在且由有效 GID 拥有,则为真。
-L <file> 如果文件存在且是符号链接,则为真。
-S <file> 如果文件存在且是套接字,则为真。
-N <file> 如果文件存在且自上次读取后已修改,则为真。
file1 -nt file2 如果 file1 比 file2 更新(根据修改日期),或者 file1 存在而 file2 不存在,则为真。
file1 -ot file2 如果 file1 比 file2 旧,或者 file2 存在而 file1 不存在,则为真。
file1 -ef file2 如果 file1 和 file2 引用同一个设备和 inode 号,则为真。
测试字符串运算符
-n string 如果字符串的长度不为零,则为真
-z string 如果字符串的长度为零,则为真
string 如果字符串未设置为 null,则为真
string1 = string2 如果 string1 等于 string2,则为真
string1 = string2 如果 string1 等于 string2,则为真
string1 != string2 如果 string1 不等于 string2,则为真
string1 < string2 如果 string1 在当前语言环境中按字典顺序排在 string2 之前,则为真。
string1 > string2 如果 string1 在当前语言环境中按字典顺序排在 string2 之后,则为真。
string = pattern 如果字符串与模式匹配,则为真
string != pattern 如果字符串与模式不匹配,则为真
测试整数运算符
exp1 -eq exp2 如果 exp1 等于 exp2,则为真,例如 [ "$#" -eq 4 ]
exp1 -ne exp2 如果 exp1 不等于 exp2,则为真,例如 test "$#" -ne 3
exp1 -le exp2 如果 exp1 小于或等于 exp2,则为真
exp1 -lt exp2 如果 exp1 小于 exp2,则为真
exp1 -ge exp2 如果 exp1 大于或等于 exp2,则为真
exp1 -gt exp2 如果 exp1 大于 exp2,则为真
其他测试运算符
! exp 如果给定表达式为假,则为真,例如 [ ! -r /etc/motd ]
exp1 -a exp2 如果 exp1 和 exp2 都计算结果为真,则为真(见下面的示例)
exp1 -o exp2如果 exp1 或 exp2 至少有一个计算结果为真,则为真
\( exp \) 如果 exp 为真,则为真;用于对表达式进行分组
\ 用于转义括号。在此字符前后使用空格
[ "$A" = "$B" -a \( "$C" = "$D" -a "$E" = "$F" \) ]
以下是使用过的文件、术语和实用程序的部分列表。* 用于
- while
- test
- if
- read
- seq