对于字符串替换,为什么 bash shebang 有效,但 sh shebang 无效

对于字符串替换,为什么 bash shebang 有效,但 sh shebang 无效

我一直在追踪 buildkite 脚本中遇到的问题,这就是我得到的:

首先,我进入 docker 镜像的 shell:

docker run --rm -it --entrypoint bash node:12.21.0

此 docker 映像没有任何文本编辑器,因此我通过连接到文件来创建 shell 脚本:

touch a.sh
chmod +x a.sh
printf '#!/bin/sh\necho ${1:0:1}' >> a.sh

touch b.sh
chmod +x b.sh
printf '#!/bin/bash\necho ${1:0:1}' >> b.sh

我现在运行我的脚本:

./a.sh hello
>./a.sh: 2: ./a.sh: Bad substitution 
./b.sh hello 
>h

有人可以简单地告诉我这里的问题是什么吗?

这个AskUbuntu问题说 bash 和 sh 是不同的 shell,并且在许多系统中 sh 将符号链接到 bash。

这个 docker 镜像具体发生了什么?我怎么会知道?

答案1

/bin/sh预计只是一个 POSIX shell,并且 POSIX shell不知道参数扩展中的子字符串

POSIX“定义了标准操作系统界面和环境,包括命令解释器(或“shell”)”,并且是传统 Unix 风格环境中广泛遵循的标准。看POSIX 到底是什么?以获得更广泛的描述。在受 POSIX 启发的环境中,/bin/sh应该提供 POSIX 风格的 shell,并且在用作/bin/sh其 shebang 的脚本中,您只能依赖 POSIX 功能(尽管大多数实际实现/bin/sh提供了更多功能)。依赖更高级的 shell 是完全可以的,但是 shebang 需要进行相应的调整。

由于您的脚本依赖于 bash 功能,因此正确的 shebang 是#!/bin/bash(或者可能是#!/usr/bin/env bash),无论它最终运行在什么环境中。在某些情况下,它可能恰好可以与 一起工作#!/bin/sh,但这只是一个愉快的意外。

答案2

您的脚本依赖于参数扩展的非 POSIX 扩展,因此它无法在大多数 shell 上运行。

这可以通过强制脚本由类似 shell 执行bash或通过编写 POSIX shell 脚本,在很多情况下这是一个好习惯。

不幸的是,没有像zshwith那样优雅的方法来获取参数中的第一个字符$1[1]。所以使用可以使用

  • "$(echo "$1" | cut -c1-1)"或者
  • "$(printf %.1s "$1")"或者
  • 奇怪的是,它从字符串中"${1%"${1#?}"}"删除没有第一个字符 ( ) 的字符串${1#?}

相关内容