如何将空参数传递给命令

如何将空参数传递给命令

所以我有这个:

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,未设置的变量和设置为空字符串之间的区别就像

  1. char *var = NULL; /* unset case. var is 4 or 8 bytes all of which are 0 */
  2. 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"

相关内容