Bash 脚本实用操作与格式化指南
1. 测试运算符
测试运算符常用于test以及[...]和[[...]]结构中。这些运算符可以通过-a(“and”)和-o(“or”)进行逻辑组合,还能使用转义括号\(...)进行分组。需要注意的是,字符串比较运算符<和>以及[[...]]结构在 bash 2.0 之前的版本中不可用,而=~仅在 bash 3.0 及更高版本中可用。
以下是部分常见的测试运算符及其含义:
| 运算符 | 为真的条件 |
| — | — |
|-a file| 文件存在(已弃用,等同于-e) |
|-b file| 文件存在且为块设备文件 |
|-c file| 文件存在且为字符设备文件 |
|-d file| 文件存在且为目录 |
|-e file| 文件存在(等同于-a) |
|-f file| 文件存在且为普通文件 |
|-g file| 文件存在且设置了 setgid 位 |
|-G file| 文件存在且归有效组 ID 所有 |
|-h file| 文件存在且为符号链接(等同于-L) |
|-k file| 文件存在且设置了粘滞位 |
|-L file| 文件存在且为符号链接(等同于-h) |
2. I/O 重定向
I/O 重定向提供了多种方式来控制命令的输入和输出。有两种指定标准输出(STDOUT)和标准错误(STDERR)重定向的格式,分别是&>file和>&file,其中>&file是更推荐的方式。
以下是完整的 I/O 重定向操作符列表:
| 重定向符 | 功能 |
| — | — |
|cmd1 | cmd2| 管道,将cmd1的标准输出作为cmd2的标准输入 |
|> file| 将标准输出定向到文件 |
|< file| 从文件获取标准输入 |
|>> file| 将标准输出定向到文件,若文件已存在则追加内容 |
|>| file| 即使设置了noclobber,也强制将标准输出定向到文件 |
|n>| file| 即使设置了noclobber,也强制将文件描述符n的输出定向到文件 |
|<> file| 将文件同时用作标准输入和标准输出 |
|n<> file| 将文件同时用作文件描述符n的输入和输出 |
|<< label| 此处文档 |
|n> file| 将文件描述符n的输出定向到文件 |
|n< file| 从文件获取文件描述符n的输入 |
|>> file| 将文件描述符n的输出定向到文件,若文件已存在则追加内容 |
|n>&| 将标准输出复制到文件描述符n|
|n<&| 从文件描述符n复制标准输入 |
|n>&m| 使文件描述符n成为输出文件描述符m的副本 |
|n<&m| 使文件描述符n成为输入文件描述符m的副本 |
|&>file| 将标准输出和标准错误定向到文件 |
|<&-| 关闭标准输入 |
|>&-| 关闭标准输出 |
|n>&-| 关闭文件描述符n的输出 |
|n<&-| 关闭文件描述符n的输入 |
|n>&word| 若未指定n,则使用标准输出(文件描述符 1);若word中的数字未指定一个打开用于输出的文件描述符,则会发生重定向错误;特殊情况下,若省略n且word未扩展为一个或多个数字,则按上述方式重定向标准输出和标准错误 |
|n<&word| 若word扩展为一个或多个数字,则使文件描述符n成为该文件描述符的副本;若word中的数字未指定一个打开用于输入的文件描述符,则会发生重定向错误;若word求值为-,则关闭文件描述符n;若未指定n,则使用标准输入(文件描述符 0) |
|n>&digit-| 将文件描述符digit移动到文件描述符n,若未指定n则移动到标准输出(文件描述符 1) |
|n<&digit-| 将文件描述符digit移动到文件描述符n,若未指定n则移动到标准输入(文件描述符 0);digit复制到n后关闭 |
3. echo 选项和转义序列
echo命令接受多个参数和以反斜杠开头的转义序列。部分转义序列的行为比较可预测,但也有一些存在差异,如\f在某些显示器上会清屏,在其他显示器上会换行,在大多数打印机上会换页;\v有些过时,通常会换行。\n、\0和\x序列更依赖设备,可用于复杂的 I/O 操作。
以下是echo命令的选项和转义序列:
| 选项 | 功能 |
| — | — |
|-e| 开启对反斜杠转义字符的解释 |
|-E| 在默认开启转义字符解释模式的系统上,关闭该功能 |
|-n| 省略最后的换行符(等同于\c转义序列) |
| 转义序列 | 打印字符 |
|---|---|
\a | 警报或 Ctrl - G(响铃) |
\b | 退格或 Ctrl - H |
\c | 省略最后的换行符 |
\e | 转义字符(等同于\E) |
\E | 转义字符 |
\f | 换页或 Ctrl - L |
\n | 换行(不在命令末尾)或 Ctrl - J |
\r | 回车(Enter)或 Ctrl - M |
\t | 制表符或 Ctrl - I |
\v | 垂直制表符或 Ctrl - K |
\nnnn | 其值为八进制(基数 8)值nnn(nnn为 1 到 3 位数字)的八位字符 |
\0nnn | 其值为八进制(基数 8)值nnn(nnn为 0 到 3 位数字)的八位字符 |
\xHH | 其值为十六进制(基数 16)值HH(一或两位数字)的八位字符 |
\\ | 单个反斜杠 |
4. printf 命令
printf命令自 bash 2.02 版本起可用,由格式字符串和可变数量的参数两部分组成:
printf format-string [arguments]格式字符串描述格式规范,最好以带引号的字符串常量形式提供。参数是一个列表,如字符串列表或变量值列表,需与格式规范对应。格式会根据需要重复使用,以消耗所有参数。若格式需要的参数数量超过提供的数量,多余的格式规范会被视为提供了零值或空字符串。
格式规范以百分号%开头,主要的格式说明符有%s(用于字符串)和%d(用于十进制整数)等。printf命令可指定输出字段的宽度和对齐方式,格式表达式可在%后、格式说明符前使用三个可选修饰符:
%flags width.precision format-specifier输出字段的宽度是一个数值,指定字段宽度时,字段内容默认右对齐,需使用-标志实现左对齐。
以下是printf命令的格式说明符:
| 格式字符 | 含义 |
| — | — |
|%c| ASCII 字符(打印对应参数的第一个字符) |
|%d, %i| 十进制(基数 10)整数 |
|%e| 浮点格式[-]d.precisione[+-]dd(精度含义见表格后文本) |
|%E| 浮点格式[-]d.precisionE[+-]dd|
|%f| 浮点格式[-]ddd.precision|
|%g|%e或%f转换,取较短者,去除尾部零 |
|%G|%E或%f转换,取最短者,去除尾部零 |
|%o| 无符号八进制值 |
|%s| 字符串 |
|%u| 无符号十进制值 |
|%x| 无符号十六进制数,使用a - f表示 10 到 15 |
|%X| 无符号十六进制数,使用A - F表示 10 到 15 |
|%%| 字面%|
5. printf 示例
以下通过一些示例展示printf命令的使用:
|printf语句 | 结果 | 注释 |
| — | — | — |
|printf '%f\n' $PI|3.141593| 注意默认的四舍五入 |
|printf '%f.5\n' $PI|3.14.5| 常见错误,格式说明符应在%f另一侧,此处.5只是像普通文本一样追加 |
|printf '%.5f\n' $PI|3.14159| 保留小数点后五位 |
|printf '%+.2f\n' $PI|+3.14| 带前导+号,小数点后保留两位 |
|printf '[%.4s]\n' s string|[s][stri]| 截断为四个字符,只有一个字符时,输出宽度为一个字符,不重复使用格式字符串 |
|printf '[%4s]\n' s string|[ s][string]| 确保最小字段宽度为四个字符,右对齐,不截断 |
|printf '[%-4.4s]\n' s string|[s ][stri]| 最小宽度为四,最大宽度为四,必要时截断,短于四个字符时左对齐 |
6. 日期和时间字符串格式化
使用strftime可以对日期和时间字符串进行格式化。不同系统中,strftime的选项和含义可能有所不同,可查阅系统的date和strftime(3)手册页。
以下是常见的strftime格式代码:
| 格式 | 描述 |
| — | — |
|%%| 字面%|
|%a| 本地化的缩写星期名称(Sun..Sat) |
|%A| 本地化的完整星期名称(Sunday..Saturday) |
|%B| 本地化的完整月份名称(January..December) |
|%b或%h| 本地化的缩写月份名称(Jan..Dec) |
|%c| 本地化的默认/首选日期和时间表示 |
|%C| 世纪(年份除以 100 并截断为整数),以十进制数表示(00..99) |
|%d| 月份中的日期,以十进制数表示(01..31) |
|%D| 日期格式%m/%d/%y(MM/DD/YY),该格式有歧义,建议使用%F|
|%e| 月份中的日期,以空格填充的十进制数表示( 1..31) |
|%F| 日期格式%Y-%m-%d(ISO 8601 日期格式:CCYY - MM - DD) |
|%g| 与%V周数对应的两位年份(YY) |
|%G| 与%V周数对应的四位年份(CCYY) |
|%H| 小时(24 小时制),以十进制数表示(00..23) |
|%h或%b| 本地化的缩写月份名称(Jan..Dec) |
|%I| 小时(12 小时制),以十进制数表示(01..12) |
|%j| 一年中的日期,以十进制数表示(001..366) |
|%k| 小时(24 小时制),以空格填充的十进制数表示( 0..23) |
|%l| 小时(12 小时制),以空格填充的十进制数表示( 1,12) |
|%m| 月份,以十进制数表示(01..12) |
|%M| 分钟,以十进制数表示(00..59) |
|%n| 字面换行符 |
|%N| 纳秒(000000000..999999999)(GNU) |
|%p| 本地化的 “AM” 或 “PM” 等效表示 |
|%P| 本地化的 “am” 或 “pm” 等效表示(GNU) |
|%r| 本地化的 12 小时制时间表示,使用 AM/PM 符号(HH:MM:SS AM/PM) |
|%R| 时间格式%H:%M(HH:MM) |
|%s| 自纪元(UTC,1970 年 1 月 1 日 00:00:00)以来的秒数 |
|%S| 秒,以十进制数表示(00..61),范围为 00 - 61 以允许闰秒和双闰秒的出现 |
|%t| 字面制表符 |
|%T| 时间格式%H:%M:%S(HH:MM:SS) |
|%u| 星期(星期一为一周的第一天),以十进制数表示(1..7) |
|%U| 一年中的周数(星期日为一周的第一天),以十进制数表示(00..53) |
|%v| 日期格式%e-%b-%Y(D - MMM - CCYY)(非标准) |
|%V| 一年中的周数(星期一为一周的第一天),以十进制数表示(01..53),根据 ISO 8601 标准,包含 1 月 1 日的周若在新年中有四天或更多天则为第 1 周,否则为前一年的第 53 周,下一周为第 1 周,年份由%G转换规范给出 |
|%w| 星期(星期日为一周的第一天),以十进制数表示(0..6) |
|%W| 一年中的周数(星期一为一周的第一天),以十进制数表示(00..53) |
|%x| 本地化的合适日期表示 |
|%X| 本地化的合适时间表示 |
|%y| 无世纪的年份,以十进制数表示(00..99) |
|%Y| 带世纪的年份,以十进制数表示 |
|%z| 与 UTC 的偏移量,采用 ISO 8601 格式[-]hhmm|
|%Z| 时区名称 |
7. 模式匹配字符
以下是常见的模式匹配字符及其含义:
| 字符 | 含义 |
| — | — |
|*| 匹配任何字符串,包括空字符串 |
|?| 匹配任何单个字符 |
|[ ... ]| 匹配方括号内的任何一个字符 |
|[! ... ]或[^ ... ]| 匹配不在方括号内的任何字符 |
通过合理运用这些测试运算符、I/O 重定向、echo、printf以及日期时间格式化等功能,可以让我们在编写脚本时更加高效地处理各种输入输出和数据展示。同时,模式匹配字符也为文件查找和字符串处理提供了强大的工具。希望这些内容能帮助你更好地掌握 Bash 脚本的使用。
Bash 脚本实用操作与格式化指南(续)
8. printf 命令的高级用法
printf命令除了基本的格式输出外,还有一些高级用法,能让输出更加灵活和精确。
8.1 动态指定宽度和精度
可以通过在格式表达式中使用星号*来动态指定宽度和精度,具体值从printf的参数列表中获取。例如:
myvar=42.123456 mysig=6 printf "|%*.*G|\n" 5 $mysig $myvar在这个例子中,宽度是 5,精度是 6,要打印的值来自$myvar,输出结果为|42.1235|。
8.2 精度的具体含义
不同的格式说明符下,“精度” 的含义有所不同:
| 格式 | “精度” 的含义 |
| — | — |
|%d, %I, %o, %u, %x, %X| 打印的最小数字位数。当值的位数较少时,前面用零填充。默认精度是 1。 |
|%e, %E| 打印的最小数字位数。当值的位数较少时,小数点后用零填充。默认精度是 10。精度为 0 时,不打印小数点。 |
|%f| 小数点后的数字位数。 |
|%g, %G| 最大有效数字位数。 |
|%s| 打印的最大字符数。 |
|%b| [POSIX Shell—可能在其他版本的printf中不兼容] 代替%s使用时,会展开参数字符串中的echo风格转义序列。 |
|%q| [POSIX Shell—可能在其他版本的printf中不兼容] 代替%s使用时,会以适合作为 shell 输入的方式打印字符串参数。 |
8.3 %b 和 %q 的示例
%q用于 shell 引号:
printf "%q\n" "greetings to the world"输出结果为greetings\ to\ the\ world。
-%b用于echo风格的转义:
printf "%s\n" 'hello\nworld' printf "%b\n" 'hello\nworld'第一个printf输出hello\nworld,第二个输出:
hello world8.4 printf 的标志
在printf格式说明符中,字段宽度和精度之前可以有一个或多个标志:
| 字符 | 描述 |
| — | — |
|-| 在字段内左对齐格式化的值。 |
| (空格) | 正数前加空格,负数前加负号。 |
|+| 总是在数值前加符号,即使值为正。 |
|#| 使用备用形式:%o前加0;%x和%X分别前缀0x和0X;%e、%E和%f结果中总是有小数点;%g和%G不去除尾部零。 |
|0| 用零而不是空格填充输出。只有当字段宽度大于转换结果时才会发生。在 C 语言中,此标志适用于所有输出格式,在 bash 中仅适用于数字格式。 |
|'| 如果是%i、%d、%u、%f、%F、%g或%G,则用千位分隔符格式化(虽然这是 POSIX 标准,但并非总是实现)。 |
9. 综合运用示例
下面通过一个流程图展示如何综合运用上述知识来完成一个简单的文件处理脚本:
graph TD; A[开始] --> B[检查文件是否存在]; B -- 存在 --> C[读取文件内容]; B -- 不存在 --> D[输出错误信息并退出]; C --> E[格式化输出文件内容]; E --> F[记录处理时间]; F --> G[结束]; D --> G;以下是实现该流程的示例脚本:
#!/bin/bash # 检查文件是否存在 file="test.txt" if [ -e "$file" ]; then # 读取文件内容 content=$(cat "$file") # 格式化输出文件内容 printf "文件内容如下:\n%s\n" "$content" # 记录处理时间 current_time=$(date +"%Y-%m-%d %H:%M:%S") printf "处理时间:%s\n" "$current_time" else printf "错误:文件 %s 不存在。\n" "$file" fi10. 总结与注意事项
在使用 Bash 脚本的这些功能时,需要注意以下几点:
-版本兼容性:部分功能如[[...]]结构和=~运算符在较老的 bash 版本中可能不可用,使用前要确保脚本运行的环境支持。
-转义字符:echo和printf中的转义字符在不同系统和设备上可能有不同的表现,特别是\f、\v等。
-精度和宽度:在使用printf动态指定精度和宽度时,要确保参数列表中的值合理,避免出现意外结果。
-文件操作:进行文件重定向和检查文件状态时,要注意文件的权限和路径是否正确,避免因权限问题导致操作失败。
通过深入理解和熟练运用这些测试运算符、I/O 重定向、echo、printf以及日期时间格式化和模式匹配等功能,可以大大提高 Bash 脚本的编写效率和质量,让脚本更加灵活、强大。希望大家在实际应用中不断探索和实践,将这些知识运用得更加得心应手。