Shell脚本是Debian操作系统中常用的一种脚本语言,用于执行系统管理和自动化任务。本教程将介绍Debian Shell脚本的各个方面,包括POSIX shell兼容性、Shell参数、条件语句、循环、环境变量、命令行处理顺序以及用于Shell脚本的应用程序等内容。
一、POSIX shell兼容性
系统中的许多脚本都可以通过任意 POSIX shell来执行。
默认的非交互 POSIX shell “/usr/bin/sh” 是一个指向到 /usr/bin/dash 的符号链接,并被许多系统程序使用。
默认的交互式 POSIX shell 是 /usr/bin/bash。
避免编写具有 bashisms(bash 化)或者 zshisms(zsh 化)语法的 shell 脚本,确保脚本在所有 POSIX shell 之间具有可移植性。你可以使用 checkbashisms(1) 对其进行检查。
典型 bashism 语法列表:
好的:POSIX | 应该避免的:BASHISM |
---|---|
if [ "$foo" = "$bar" ] ; then … | if [ "$foo" == "$bar" ] ; then … |
diff -u file.c.orig file.c | diff -u file.c{.orig,} |
mkdir /foobar /foobaz | mkdir /foo{bar,baz} |
funcname() { … } | function funcname() { … } |
八进制格式:”\377 “ | 十六进制格式:”\xff “ |
使用 “echo” 命令的时候需要注意以下几个方面,因为根据内置 shell 和外部命令的不同,它的实现也有差别。
注意:
避免使用除“-n”以外的任何命令行选项;
避免在字符串中使用转义序列,因为根据 shell 不同,计算后的结果也不一样;
尽管“-n”选项并不是 POSIX 语法,但它已被广泛接受;
如果你想要在输出字符串中嵌入转义序列,用 “printf” 命令替代 “echo” 命令。
二、Shell参数
特殊的 shell 参数经常在 shell 脚本里面被用到。
shell 参数列表:
SHELL 参数 | 值 |
---|---|
$0 | shell 或 shell 脚本的名称 |
$1 | 第一个 shell 参数 |
$9 | 第 9 个 shell 参数 |
$# | 位置参数数量 |
"$*" | "$1 $2 $3 $4 … " |
"$@" | "$1" "$2" "$3" "$4" … |
$? | 最近一次命令的退出状态码 |
$$ | 这个 shell 脚本的 PID |
$! | 最近开始的后台任务 PID |
如下所示是需要记忆的基本的参数展开。
shell 参数展开列表:
参数表达式形式 | 如果 VAR 变量已设置那么值为 | 如果 VAR 变量没有被设置那么值为 |
---|---|---|
${var:-string} | “$var “ | “string “ |
${var:+string} | “string “ | “null “ |
${var:=string} | “$var “ | “string ” (并运行 “var=string “) |
${var:?string} | “$var “ | 在 stderr 中显示 “string ” (出错退出) |
以上这些操作中 “:” 实际上都是可选的。
有 “:” 等于测试的 var 值是存在且非空;
没有 “:” 等于测试的 var 值只是存在的,可以为空。
重要的 shell 参数替换列表:
参数替换形式 | 结果 |
---|---|
${var%suffix} | 删除位于 var 结尾的 suffix 最小匹配模式 |
${var%%suffix} | 删除位于 var 结尾的 suffix 最大匹配模式 |
${var#prefix} | 删除位于 var 开头的 prefix 最小匹配模式 |
${var##prefix} | 删除位于 var 开头的 prefix 最大匹配模式 |
三、Shell条件语句
每个命令都会返回退出状态,这可以被条件语句使用。
成功:0 (“True”);
失败:非 0 (“False”)。
注意:
“0” 在 shell 条件语句中的意思是 “True”,然而 “0” 在 C 条件语句中的含义为 “False”;
“[” 跟 test 命令是等价的,它评估到 “]” 之间的参数来作为一个条件表达式。
如下所示是需要记忆的基础 条件语法:
“command && if_success_run_this_command_too || true”
“command || if_not_success_run_this_command_too || true”
如下所示是多行脚本片段:
这里末尾的“|| true”是需要的,它可以保证这个 shell 脚本在不小心使用了“-e”选项而被调用时不会在该行意外地退出。
在条件表达式中进行文件比较:
表达式 | 返回逻辑真所需的条件 |
---|---|
-e file | file 存在 |
-d file | file 存在并且是一个目录 |
-f file | file 存在并且是一个普通文件 |
-w file | file 存在并且可写 |
-x file | file 存在并且可执行 |
file1 -nt file2 | file1 是否比 file2 新 |
file1 -ot file2 | file1 是否比 file2 旧 |
file1 -ef file2 | file1 和 file2 位于相同的设备上并且有相同的 inode 编号 |
在条件表达式中进行字符串比较:
表达式 | 返回逻辑真所需的条件 |
---|---|
-z str | str 的长度为零 |
-n str | str 的长度不为零 |
str1 = str2 | str1 和 str2 相等 |
str1 != str2 | str1 和 str2 不相等 |
str1 < str2 | str1 排列在 str2 之前(取决于语言环境) |
str1 > str2 | str1 排列在 str2 之后(取决于语言环境) |
算术整数的比较在条件表达式中为 “-eq”,”-ne”,”-lt”,”-le”,”-gt” 和 “-ge”。
四、shell循环
这里有几种可用于 POSIX shell 的循环形式。
“for x in foo1 foo2 … ; do command ; done”,该循环会将 “foo1 foo2 …” 赋予变量 “x” 并执行 “command”;
“while condition ; do command ; done”,当 “condition” 为真时,会重复执行 “command”;
“until condition ; do command ; done”,当 “condition” 为假时,会重复执行 “command”;
“break” 可以用来退出循环;
“continue” 可以用来重新开始下一次循环。
注意:C 语言中的数值迭代可以用 seq(1) 实现来生成 “foo1 foo2 …”;
五、Shell环境变量
普通的 shell 命令行提示下的一些常见的环境变量,可能在你的脚本的执行环境中不存在。
对于 “$USER”, 使用 “$(id -un)”;
对于 “$UID”, 使用 “$(id -u)”;
对于 “$HOME”,使用”$(getent passwd “$(id -u)”|cut -d “:” -f 6)” 。
六、shell命令行处理顺序
shell 大致以下列的顺序来处理一个脚本。
1、shell 读取一行。
2、如果该行包含有”…” 或 ‘…’,shell 对该行各部分进行分组作为 一个标识(one token) (译注:one token 是指 shell 识别的一个结构单元).
3、shell 通过下列方式将行中的其它部分分隔进 标识(tokens)。
空白字符:空格 tab 换行符;
元字符: < > | ; & ( )。
4、shell 会检查每一个不位于 “…” 或 ‘…’ 的 token 中的 保留字 来调整它的行为。
保留字:if then elif else fi for in while unless do done case esac
5、shell 展开不位于 “…” 或 ‘…’ 中的 别名。
6、shell 展开不位于 “…” 或 ‘…’ 中的 波浪线。
“~” → 当前用户的家目录;
“~user” → user 的家目录。
7、shell 将不位于 ‘…’ 中的 变量 展开为它的值。
变量:”$PARAMETER” 或 “${PARAMETER}”
8、shell 展开不位于 ‘…’ 中的 命令替换。
“$( command )” → “command” 的输出;
“` command `” → “command” 的输出。
9、shell 将不位于 “…” 或 ‘…’ 中的 glob 路径 展开为匹配的文件名。
* → 任何字符;
? → 一个字符;
[…] → 任何位于 “…” 中的字符。
10、shell 从下列几方面查找 命令 并执行。
函数定义;
内建命令;
“$PATH” 中的可执行文件。
shell 前往下一行,并按照这个顺序从头再次进行处理。
双引号中的单引号是没有效果的。在 shell 中执行 “set -x” 或使用 “-x” 选项启动 shell 可以让 shell 显示出所有执行的命令。
七、shell脚本应用程序
为了使 shell 程序在 Debian 系统上尽可能地具有可移植性,应该只使用 必要的软件包所提供的应用程序。
“aptitude search ~E”,列出 必要的软件包;
“dpkg -L package_name |grep ‘/man/man.*/'”,列出 package_name 软件包所提供的 man 手册。
包含用于 shell 脚本的小型应用程序的软件包:
软件包 | 流行度 | 大小 | 说明 |
dash | V:883, I:997 | 191 | 小和快的 POSIX 兼容 shell,用于 sh |
coreutils | V:879, I:999 | 18307 | GNU 核心工具 |
grep | V:781, I:999 | 1266 | GNU grep、egrep 和 fgrep |
sed | V:787, I:999 | 987 | GNU sed |
mawk | V:437, I:997 | 285 | 小和快的 awk |
debianutils | V:907, I:999 | 223 | 用于 Debian 的各种工具 |
bsdutils | V:519, I:999 | 356 | 来自 4.4BSD-Lite 的基础工具 |
bsdextrautils | V:582, I:698 | 339 | 来自 4.4BSD-Lite 的额外的工具 |
moreutils | V:15, I:38 | 244 | 额外的 Unix 工具 |
虽然 moreutils 这套工具集可能专属于 Debian 系统,它却包含了一些颇为实用的小工具。在这些工具中,尤其值得一提的是 sponge(8);当你需要覆盖原文件时,这个工具会显得特别方便。