我一直在追踪我在 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
,但这只是一个快乐的意外。
您的脚本依赖于非 POSIX 扩展来进行参数扩展,因此它不适用于大多数 shell。
这可以通过强制脚本由类似 shell 执行bash
或编写 POSIX shell script 来解决,这在许多情况下是一个好习惯。
Unfortunally,没有优雅的方式来获得一个参数的第一个字符像zsh
用$1[1]
。所以使用可以使用
"$(echo "$1" | cut -c1-1)"
或者"$(printf %.1s "$1")"
或者"${1%"${1#?}"}"
其去除而不字符串第一个字符(${1#?}
从字符串)