所以我有这个:
export ti_arg='';
if [[ -t 1 ]] ; then
# allow us to kill container if attached to terminal
export ti_arg='-ti'
fi
(
cd "$(dirname "$BASH_SOURCE")"
docker build <...> '.'
docker run "$ti_arg" --rm "test-$commit_id"
)
问题是,即使 $ti_arg 为空,shell 仍然将其视为参数,我得到:
docker:参考格式无效
我能想到的唯一解决方案是 if/else 运行基本相同的 docker 命令(除了那个 arg),或者找到一些可以与 docker 一起使用的传递参数,如下所示:
export ti_arg='--noop';
if [[ -t 1 ]] ; then
# allow us to kill container if attached to terminal
export ti_arg='-ti'
fi
有人有好的解决方案吗?我想 CLI 设计总是会--noop
为此用例提供一个标志
答案1
有多种方法可以处理它。基本方法是使用 shell 的功能来测试正在设置的参数。所以你可以有
docker run ${ti_arg:+"$ti_arg"} --rm "test-$commit_id"
这会测试 ti_arg 是否已设置,如果设置了,则将其放入"$ti_arg"
.
当然,启动脚本export ti_arg=''
并不是最好的,这明确表示参数的值为空字符串。使用unset ti_arg
说该值未设置会更好,尽管在这种情况下它不能解决问题。但是,它允许您使用${ti_arg+"$ti_arg"}
(注意缺少:
)来更好地表示您没有设置值的情况。
使用“$@”的技巧
值得庆幸的是,在很久以前,人们会使用
subprog ${1:"$@"}
在他们的脚本中处理参数的扩展来解决这个问题。 Bourne 的所有现代实现(如 shell)都修复了此错误,因此您可以直接说"$@"
(用双引号),如果没有设置值,它会正确扩展为空。因此,您可以使用而不是使用命名变量
set -- # Clear the $@ array
if [[ -t 1 ]] ; then
set -- "-ti"
fi
...
docker run "$@" --rm ....
C 程序员的旁注
对于熟悉的人来说C
,未设置的变量和设置为空字符串之间的区别就像
char *var = NULL; /* unset case. var is 4 or 8 bytes all of which are 0 */
char *var = strdup(""); /* set to the empty string. var is still 4 or 8 8 bytes, but now contains the address of an item in the heap, which could be as small as 1 byte. The first byte of this heap item will be 0, to indicate the end of string */
答案2
您可以将项目推入数组并使用数组替换。这样做有一个奇妙的优点,即没有元素的数组可以完全优化。这是您自己的代码的修改部分:
(
cd "$(dirname "$BASH_SOURCE")"
docker build --build-arg commit_id -t "test-$commit_id" .
set --
test -n "$ti_arg" && set -- "$ti_arg"
docker run "$@" --rm "test-$commit_id"
)
如果您正在使用,bash
您可以避免使用$@
并进一步简化事情
(
cd "${BASH_SOURCE%/*}"
docker build --build-arg commit_id -t "test-$commit_id" .
args=()
[[ -n "$ti_arg" ]] && args+=("$ti_arg")
docker run "${args[@]}" --rm "test-$commit_id"
)
这两个解决方案都使用双引号,"$ti_arg"
以防止任何无意的 shell 评估。在这种特殊情况下,您可能也可以简单地$ti_arg
在final中不加引号docker run
,以便shell对其进行插值-ti
或仅对其求值,但作为一般解决方案,它可能会破坏变量或其上下文(包括IFS
)的意外输入,即为什么我更喜欢上面更复杂和完整的解决方案。
docker run $ti_arg --rm "test-$commit_id"