我一直在追踪 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 脚本,在很多情况下这是一个好习惯。
不幸的是,没有像zsh
with那样优雅的方法来获取参数中的第一个字符$1[1]
。所以使用可以使用
"$(echo "$1" | cut -c1-1)"
或者"$(printf %.1s "$1")"
或者- 奇怪的是,它从字符串中
"${1%"${1#?}"}"
删除没有第一个字符 ( ) 的字符串${1#?}