【问题标题】:omit passing an empty quoted argument省略传递空的引用参数
【发布时间】:2015-10-07 23:35:55
【问题描述】:

我在 bash 脚本中有一些变量,这些变量可能包含文件名或未设置。它们的内容应该作为附加参数传递给程序。但是,当变量未设置时,这会留下一个空参数。

$ afile=/dev/null
$ anotherfile=/dev/null
$ unset empty
$ cat "$afile" "$empty" "$anotherfile"
cat: : No such file or directory

没有引号,它工作得很好,因为附加参数被简单地省略了。但由于变量可能包含空格,因此必须在此处引用。

我知道我可以简单地将整行包含在空性测试中。

if [ -z "$empty" ]; then
  cat "$afile" "$anotherfile"
else
  cat "$afile" "$empty" "$anotherfile"
fi

但是对每个变量进行一次测试会产生一个庞大而复杂的决策树。

有没有更紧凑的解决方案? bash 可以省略带引号的空变量吗?

【问题讨论】:

    标签: bash arguments argument-passing variable-expansion


    【解决方案1】:

    如果已设置,您可以使用 alternate value parameter expansion (${var+altvalue}) 来包含带引号的变量:

    cat ${afile+"$afile"} ${empty+"$empty"} ${anotherfile+"$anotherfile"}
    

    由于双引号在备用值字符串中(而不是围绕整个参数表达式),因此它们只有在设置变量时才生效。请注意,您可以使用+(如果设置了变量,则使用备用值)或:+(如果设置了变量且不为空,则使用备用值)。

    【讨论】:

    • 我想到了参数扩展的微妙之处,但没有想到在大括号内引用。确实非常优雅,因此正是我想要的。
    【解决方案2】:

    尝试:

    printf "%s\n%s\n%s\n" "$afile" "$empty" "$anotherfile" | egrep -v '^$' | tr '\n' '\0' | xargs -0 cat
    

    【讨论】:

    • 不幸的是,你的行让我遇到了同样的错误。仅使用 xargs 还没有删除空参数。在 xargs 之前插入 sed 's/\x0\x0/\x0/g;s/\x0*$//;s/^\x0*//' | 可以做到这一点。加上它,我会接受这是正确的答案。
    • 另外,tr 部分可以删除。 printf 可以通过将格式字符串中的\n 替换为\0 来直接打印空值。
    • @XZS:使用“grep”而不是“xargs -r”更新。关于您使用 sed 的解决方案,我建议您将其作为独立答案发布(无论作者是否是问题的作者,stackoverflow 都会推广答案)。
    • 我实际上更喜欢您的grep 方式,因为它更紧凑。尽管如此,通过直接写入空值并保存tr 步骤,可以进一步压缩它。 grep 可以使用 -zZ 选项处理空分隔符。
    【解决方案3】:

    使用数组可以实现纯 bash 解决方案。 "$empty" 将评估为空参数,"${empty[@]}" 将扩展到所有数组字段,引用,在本例中为无。

    $ afile=(/dev/null)
    $ unset empty
    $ alsoempty=()
    $ cat "${afile[@]}" "${empty[@]}" "${alsoempty[@]}"
    

    在不能选择数组的情况下,请参阅 pasaba por aqui 的更通用的答案。

    【讨论】:

      【解决方案4】:

      对于像 cat 这样的命令,您可以用空文件替换空参数,您可以使用标准的 shell 默认替换语法:

      cat "${file1:-/dev/null}" "${file2:-/dev/null}" "${file3:-/dev/null}"
      

      或者,您可以通过管道(如下所示)或通过进程替换从存在的参数创建连接的输出流:

      { [[ -n "$file1" ]] && cat "$file1";
        [[ -n "$file2" ]] && cat "$file2";
        [[ -n "$file3" ]] && cat "$file3"; } | awk ...
      

      这可以通过实用函数来简化:

      cat_if_named() { [[ -n "$1" ]] && cat "$1"; }
      

      cat 的特殊情况下构建一个新文件,您可以执行一系列附加操作:

      # Start by emptying or creating the output file.
      . > output_file
      cat_if_named "$file1" >> output_file 
      cat_if_named "$file2" >> output_file 
      cat_if_named "$file3" >> output_file 
      

      如果您需要保留单个参数——例如,如果你想将列表传递给grep,它将打印文件名和匹配项——你可以构建一个参数数组,只选择存在的论点:

      args=()
      [[ -n "$file1" ]] && args+=("$file1")
      [[ -n "$file2" ]] && args+=("$file2")
      [[ -n "$file3" ]] && args+=("$file3")
      

      使用 bash 4.3 或更高版本,您可以使用 nameref 制作实用函数来执行上述操作,这几乎可以肯定是该问题的最紧凑和通用的解决方案:

      non_empty() {
        declare -n _args="$1"
        _args=()
        shift
        for arg; do [[ -n "$arg" ]] && _args+=("$arg"); done
      }
      

      例如:

      non_empty my_args "$file1" "$file2" "$file3"
      grep "$pattern" "${my_args[@]}"
      

      【讨论】:

        猜你喜欢
        • 2023-04-01
        • 1970-01-01
        • 2019-02-01
        • 1970-01-01
        • 2020-12-19
        • 2015-01-26
        • 2011-03-09
        • 2019-09-10
        • 1970-01-01
        相关资源
        最近更新 更多