Debian Shell脚本详解

2024-11-09 2

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.cdiff -u file.c{.orig,}
mkdir /foobar /foobazmkdir /foo{bar,baz}
funcname() { … }function funcname() { … }
八进制格式:”\377十六进制格式:”\xff

使用 “echo” 命令的时候需要注意以下几个方面,因为根据内置 shell 和外部命令的不同,它的实现也有差别。

注意:

  • 避免使用除“-n”以外的任何命令行选项;

  • 避免在字符串中使用转义序列,因为根据 shell 不同,计算后的结果也不一样;

  • 尽管“-n”选项并不是 POSIX 语法,但它已被广泛接受;

  • 如果你想要在输出字符串中嵌入转义序列,用 “printf” 命令替代 “echo” 命令。

二、Shell参数

特殊的 shell 参数经常在 shell 脚本里面被用到。

shell 参数列表:

SHELL 参数
$0shell 或 shell 脚本的名称
$1第一个 shell 参数
$9第 9 个 shell 参数
$#位置参数数量
"$*""$1 $2 $3 $4 … "
"$@""$1" "$2" "$3" "$4" …
$?最近一次命令的退出状态码
$$这个 shell 脚本的 PID
$!最近开始的后台任务 PID

如下所示是需要记忆的基本的参数展开。

shell 参数展开列表:

参数表达式形式如果 VAR 变量已设置那么值为如果 VAR 变量没有被设置那么值为
${var:-string}$varstring
${var:+string}stringnull
${var:=string}$varstring” (并运行 “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”

如下所示是多行脚本片段:

if [ conditional_expression ]; then
if_success_run_this_command
else
if_not_success_run_this_command
fi

这里末尾的“|| true”是需要的,它可以保证这个 shell 脚本在不小心使用了“-e”选项而被调用时不会在该行意外地退出。

在条件表达式中进行文件比较:

表达式返回逻辑真所需的条件
-e filefile 存在
-d filefile 存在并且是一个目录
-f filefile 存在并且是一个普通文件
-w filefile 存在并且可写
-x filefile 存在并且可执行
file1 -nt file2file1 是否比 file2 新
file1 -ot file2file1 是否比 file2 旧
file1 -ef file2file1 和 file2 位于相同的设备上并且有相同的 inode 编号

在条件表达式中进行字符串比较:

表达式返回逻辑真所需的条件
-z strstr 的长度为零
-n strstr 的长度不为零
str1 = str2str1 和 str2 相等
str1 != str2str1 和 str2 不相等
str1 < str2str1 排列在 str2 之前(取决于语言环境)
str1 > str2str1 排列在 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 脚本的小型应用程序的软件包:

软件包流行度大小说明
dashV:883, I:997191小和快的 POSIX 兼容 shell,用于 sh
coreutilsV:879, I:99918307GNU 核心工具
grepV:781, I:9991266GNU grep、egrep 和 fgrep
sedV:787, I:999987GNU sed
mawkV:437, I:997285小和快的 awk
debianutilsV:907, I:999223用于 Debian 的各种工具
bsdutilsV:519, I:999356来自 4.4BSD-Lite 的基础工具
bsdextrautilsV:582, I:698339来自 4.4BSD-Lite 的额外的工具
moreutilsV:15, I:38244额外的 Unix 工具

虽然 moreutils 这套工具集可能专属于 Debian 系统,它却包含了一些颇为实用的小工具。在这些工具中,尤其值得一提的是 sponge(8);当你需要覆盖原文件时,这个工具会显得特别方便。