在为 sh 或 ash 重写 bash 条件时应该检查什么?

在为 sh 或 ash 重写 bash 条件时应该检查什么?

有时,在系统上不可能拥有 bash 的奢侈,但与 sh 或 ash 相比,在 bash 上创建条件更容易,在重写时应该验证什么以确保条件不会因典型的“意外操作符”而中断bash 到 sh 还是 ash ?

答案1

我不同意使用((...))and更容易制定条件[[...]](假设这就是您所指的;请注意,这些运算符不是 bash 特有的,而是来自 ksh)比标准[test命令更容易。[[ ... ]](( ... ))有自己的几个问题,而且比 的问题严重得多[

如果你[失败了意外的操作员错误,很可能你没有使用正确地(通常,您忘记引用扩展)而不是命令[

对于如何正确和便携地使用[/test最好是参考它的 POSIX 规范

确保安全的几个基本规则是:

  • 引用其参数中的所有单词扩展 ( $var, $(cmd), )。$((arithmetic))这适用于每个命令,而[不仅仅是dash.[[ ... ]](( ... ))它们本身是特殊的构造,具有自己的特定语法(因 shell 而异),何时可以或不可以引用事物并不总是显而易见的。

  • [除了和之外,不要传递超过 4 个参数]。也就是说,不要使用已弃用的-oand-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 之间的语法差异以及其他陷阱方面有一个很好的答案。这个答案将采取不同的方法。

作为人类,很难将为bashksh或其他带有非 POSIX 扩展的 shell 编写的脚本安全地将整个脚本转换为仅使用 POSIX 语法的脚本。很容易意外地错过某些内容,然后脚本可能会失败,而且可能是在非常糟糕的时间。

有一个很棒的开源工具我已经使用了一段时间了,叫做外壳检查。 shellcheck 的作用是充当 shell 脚本的 linter。如果您指定类似的 shebang#!/usr/bin/env sh或使用该--shell sh选项,shellcheck 将自动提醒您任何对指定 shell 无效的语法。

Shellcheck 还有一个非常好的 wiki,其中每个 linting 错误或警告都记录在其自己的页面上,并解释错误或警告的含义以及正确/更安全代码的建议。

如果您选择安装 shellcheck,我建议您从以下位置安装最新的稳定版本:GitHub 上的发布页面而不是使用包管理器,因为根据我的经验,包管理器有旧版本的 shellcheck。

答案3

我想说最重要的是要避免[[ ... ]]。两者之间的算术(( ... ))可能也不起作用。

养成将变量放在引号中的习惯,以避免在未设置变量时出现错误,例如"$var".

所有算术或逻辑运算符都是带有破折号的单词,例如-eqor-a代替=or &&

还使用shellcheck.net用于语法检查。#!/bin/sh告诉 shellcheck 您想要使用 POSIX shell,我认为它不完全是 Bourne shell,但很接近。也应该有一种方法来测试 Bourne shell 脚本。

相关内容