为什么 bash shebang 工作,但 sh shebang 不起作用,用于字符串替换

dwj*_*ton 5 bash shell-script

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

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

docker run --rm -it --entrypoint bash node:12.21.0
Run Code Online (Sandbox Code Playgroud)

这个 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

Run Code Online (Sandbox Code Playgroud)

我现在运行我的脚本:

./a.sh hello
>./a.sh: 2: ./a.sh: Bad substitution 
./b.sh hello 
>h
Run Code Online (Sandbox Code Playgroud)

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

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

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

Ste*_*itt 25

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

POSIX “定义了一个标准的操作系统接口和环境,包括一个命令解释器(或“shell”)”,并且是传统 Unix 风格环境中主要遵循的标准。请参阅POSIX 究竟是什么?以获得更广泛的描述。在受 POSIX 启发的环境中,/bin/sh应该提供 POSIX 风格的 shell,而在/bin/sh用作它的 shebang的脚本中,您只能依赖 POSIX 功能(即使大多数实际实现/bin/sh提供更多)。依靠更高级的外壳是完全可以的,但需要相应地调整shebang。

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


Phi*_*pos 8

您的脚本依赖于非 POSIX 扩展来进行参数扩展,因此它不适用于大多数 shell。

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

Unfortunally,没有优雅的方式来获得一个参数的第一个字符像zsh$1[1]。所以使用可以使用

  • "$(echo "$1" | cut -c1-1)" 或者
  • "$(printf %.1s "$1")" 或者
  • 怪异"${1%"${1#?}"}"其去除而不字符串第一个字符(${1#?}从字符串)

  • 最后一个例子很有启发性。我从来不知道你可以在不调用命令的情况下在 POSIX shell 中做到这一点。此外,它可以说是有用的 shell 脚本的一个很好的例子,阅读起来很痛苦...... (5认同)