有时,在系统上不可能拥有 bash 的奢侈,但与 sh 或 ash 相比,在 bash 上创建条件更容易,在重写时应该验证什么以确保条件不会因典型的“意外操作符”而中断bash 到 sh 还是 ash ?
答案1
我不同意使用((...))
and更容易制定条件[[...]]
(假设这就是您所指的;请注意,这些运算符不是 bash 特有的,而是来自 ksh)比标准[
或test
命令更容易。[[ ... ]]
并(( ... ))
有自己的几个问题,而且比 的问题严重得多[
。
如果你[
失败了意外的操作员错误,很可能你没有使用壳正确地(通常,您忘记引用扩展)而不是命令[
。
对于如何正确和便携地使用[
/test
最好是参考它的 POSIX 规范。
确保安全的几个基本规则是:
引用其参数中的所有单词扩展 (
$var
,$(cmd)
, )。$((arithmetic))
这适用于每个命令,而[
不仅仅是dash
.[[ ... ]]
和(( ... ))
它们本身是特殊的构造,具有自己的特定语法(因 shell 而异),何时可以或不可以引用事物并不总是显而易见的。[
除了和之外,不要传递超过 4 个参数]
。也就是说,不要使用已弃用的-o
and-a
运算符,也不要使用(
or)
进行分组。所以通常你的[
表达应该是:- 中的单个参数
[ "$string" ]
,尽管我更喜欢使用[ -n "$string" ]
变体来明确您检查$string
是否为非空。 - 一元运算符 (
-f
,-r
,-n
...) 及其操作数,可以选择在其前面加上!
for 否定。 - 二元运算符 (
=
,-gt
...) 及其 2 个操作数,前面可选!
- 算术运算符的操作数必须是带有可选符号的十进制整数常量。
dash
,就像bash
接受这些操作数中的前导和尾随空格,但并非所有[
实现都这样做。
- 中的单个参数
检查 POSIX 标准以获取可移植操作符列表。请注意,它还dash
对标准进行了一些扩展(-nt
, -ef
, -k
, -O
, <
, >
² ...)
对于模式匹配,请使用case
构造 ( case $var in ($pattern)...
) 而不是if [[ $var = $pattern ]]...
。
对于扩展正则表达式匹配,您可以使用awk
:
ere_match() { awk -- 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' "$1" "$2"; }
if ere_match "$string" "$regex"...
代替:
if [[ $string =~ $regex ]]...
对于 AND/OR,使用或shell 运算符(具有相同的优先级)链接多个[
调用,并使用命令组进行分组,这与您用于任何其他命令(而不仅仅是.&&
||
[
if
[ "$mode" = "$mode1" ] || {
[ -f "$file" ] && [ -r "$file" ]
}
then...
代替:
if [[ $mode = "$mode1" || ( -f $file && -r $file ) ]]; then...
一些bash
's/ dash
's/ zsh
's/ ksh
's/ yash
's test
/[
非 POSIX 运算符的标准等效项:
-a file
答案2
斯蒂芬·查泽拉斯在指出严格兼容 POSIX 的 shell 和其他 shell 之间的语法差异以及其他陷阱方面有一个很好的答案。这个答案将采取不同的方法。
作为人类,很难将为bash
、ksh
或其他带有非 POSIX 扩展的 shell 编写的脚本安全地将整个脚本转换为仅使用 POSIX 语法的脚本。很容易意外地错过某些内容,然后脚本可能会失败,而且可能是在非常糟糕的时间。
有一个很棒的开源工具我已经使用了一段时间了,叫做外壳检查。 shellcheck 的作用是充当 shell 脚本的 linter。如果您指定类似的 shebang#!/usr/bin/env sh
或使用该--shell sh
选项,shellcheck 将自动提醒您任何对指定 shell 无效的语法。
Shellcheck 还有一个非常好的 wiki,其中每个 linting 错误或警告都记录在其自己的页面上,并解释错误或警告的含义以及正确/更安全代码的建议。
如果您选择安装 shellcheck,我建议您从以下位置安装最新的稳定版本:GitHub 上的发布页面而不是使用包管理器,因为根据我的经验,包管理器有旧版本的 shellcheck。
答案3
我想说最重要的是要避免[[ ... ]]
。两者之间的算术(( ... ))
可能也不起作用。
养成将变量放在引号中的习惯,以避免在未设置变量时出现错误,例如"$var"
.
所有算术或逻辑运算符都是带有破折号的单词,例如-eq
or-a
代替=
or &&
。
还使用shellcheck.net用于语法检查。#!/bin/sh
告诉 shellcheck 您想要使用 POSIX shell,我认为它不完全是 Bourne shell,但很接近。也应该有一种方法来测试 Bourne shell 脚本。