跳转至内容

Ict-创新/LPI/105.2

来自维基教科书,开放的书籍,为开放的世界

105.2 自定义或编写简单的脚本

[编辑 | 编辑源代码]

考生应该能够自定义现有脚本,或编写简单的新的 BASH 脚本。


关键知识领域

  • 使用标准 sh 语法(循环、测试)。
  • 使用命令替换。
  • 测试返回值以判断命令成功或失败或其他信息。
  • 执行有条件的邮件发送给超级用户。
  • 通过 shebang(#!)行正确选择脚本解释器。
  • 管理脚本的位置、所有权、执行和 suid 权限。

什么是 shell 脚本?

[编辑 | 编辑源代码]

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 函数

[编辑 | 编辑源代码]

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


上一章 | 下一章

华夏公益教科书