【问题标题】:Weird difference in while statement (Bash vs sh)while 语句的奇怪差异(Bash 与 sh)
【发布时间】:2018-08-28 10:39:25
【问题描述】:

我正在学习 shell 脚本(更专注于 sh shell 而不是 Bash shell),我编写了一个简单的脚本来测试 sh shell 中的 while 语句。

但是当我运行它时,我得到了这个错误......

test.sh: 8: test.sh: 10: not found
10

但是当我使用 bash 执行脚本时,它运行时没有问题(即使脚本上有 #!/bin/sh)。有人可以解释为什么吗?两个 shell 上的 while 语句是否不同?

几点说明:

  1. 通过调试脚本发现了运行脚本的“解决方案”:

    bash -x test.sh
    
  2. 我的用户的权限是 rwx,所有其他用户的权限是 r

  3. #!/bin/sh 更改为 /bash,但没有真正改变(仍然使用 sh 运行它,我知道这很愚蠢,但有时愚蠢的事情会起作用)。

  4. 代码非常简单明了,它只是一个变量和一个带有变量回显的 while 语句,所以我猜没有语法错误(因为它在 Bash shell 中运行)。

  5. 我使用的是 Ubuntu 18.04.1 LTS (x86_64)。

  6. 脚本所在目录包含在$PATH中。

  7. 尝试使用 sudo,但同样的事情,在 Bash 上有效,在 sh 上失败。

编辑:

#!/bin/sh

var=10
while [ $var -gt 0 ]
do
        echo $var
        var=$[ $var - 1 ]
done

【问题讨论】:

  • @IMN01 $[ 是 Bash 的东西(如果我错了,请纠正我)。
  • @Biffen 无法真正纠正你,因为我知道的不多,但看起来是这样:D
  • 我建议您在脚本中使用shellcheck:它会报告52055888.sh:7:13: warning: In POSIX sh, $[..] in place of $((..)) is undefined. [SC2039]

标签: linux bash sh


【解决方案1】:

bash 的维护者在$[...] 语法上引用这个bug-bash discussion

它可以追溯到 1990 年左右的 Posix(1003.2d9,其中我的论文丢失了 复制)。我是在伯克利的人之后实施的,主要是 Marc Teitelbaum,把它放到 Posix 中。它最终被放弃了 ksh $((...)) 扩展,此时每个人都弃用 老$[...]。后来我把它从手册中删除了,但它仍然存在 一如既往地工作。


$[...] 是一种已弃用的语法,在 bash 中仍然有效,但在 dash 中无效(根据 cmets,/bin/sh 在您的系统上指向该位置)。

dash(符合 POSIX)支持 $((...)) 语法:

var=10
while [ "$var" -gt 0 ]; do
   echo "$var"
   var=$((var-1))
done

【讨论】:

  • 刚刚修改了我的代码并按预期工作。毕竟,这是一个语法错误。感谢您澄清这个烂摊子:D
  • 很棒的发现!我曾经试图弄清楚 $[...] 的来源以及为什么它仍然有效,但在手册页中没有提到 - 现在我知道了 :)
【解决方案2】:

请注意,#!/bin/sh 仅在您使用./test.sh 执行脚本且您拥有x(执行)脚本的权限时才有效。

另外,bash test.sh 表示你调用了 bash,bash 读取 test.sh 的行,然后在 bash 的子进程中执行,顶部的#!/bin/sh 作为注释被忽略。

因此您应该使用./test.sh 执行,在此之前您应该使用chmod 755 test.sh 以确保您有权限执行这样的操作。

那么,请给我们看代码...

更新 (请阅读 cmets)/bin/sh 可能只是链接您正在使用的实际外壳。调用readlink -f /bin/sh 找出它(当前)指向的shell。

【讨论】:

  • 还要注意/bin/sh 通常是指向bash 或其他shell 的链接。
  • 我只是想提醒他或她执行方式很重要,它将决定哪个shell真正在执行。 T_T 然后我得到一个“-1”
  • 好的,现在我明白为什么使用 bash 或 sh 运行会产生不同的输出。并且已经更改了权限,但同样的事情发生在 sh test.sh 或 ./test.sh
  • @IMN01 嘿,正如我所说,sh 可能链接到另一个 shell,例如 bashdash。尝试使用此命令readlink -f /bin/sh
  • @PesaThe 我得到以下输出:/bin/dash
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-07
  • 1970-01-01
  • 2021-06-08
  • 1970-01-01
  • 1970-01-01
  • 2017-07-28
  • 1970-01-01
相关资源
最近更新 更多