省略传递空引号参数

9

我有一些变量在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省略带引号的空变量吗?

4个回答

10

如果变量已设置,您可以使用 备用值参数扩展 (${var+altvalue}) 来包含引用的变量:

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

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


我思考了参数扩展的精妙之处,但花括号内的引用并没有浮现在我的脑海中。确实非常优雅,正是我所寻找的。 - XZS
1
太棒了!以及 posix - user1593842

2

可以使用数组进行纯bash解决方案。虽然"$empty"将评估为空参数,但"${empty[@]}"将展开所有引用的数组字段,在本例中为零。

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

在不能使用数组的情况下,请参考pasaba por aqui更为灵活的答案。

1

试一下:

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*//' |可以解决此问题。添加后,我会接受这个答案作为正确答案。 - XZS
此外,tr 部分可以被移除。通过在格式字符串中用 \0 替换 \nprintf 可以直接打印空值。 - XZS
@XZS:使用“grep”而不是“xargs -r”进行了更新。关于你使用sed的解决方案,我建议你将其作为独立答案发布(stackoverflow会推广答案,无论作者是否是问题的提问者)。 - pasaba por aqui
我实际上更喜欢你的 grep 方法,因为它更紧凑。不过,可以通过直接编写 nulls 来保存 tr 步骤来进一步简化它。grep 可以使用 -zZ 选项处理 null 分隔符。 - XZS

1
在像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
}

eg:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接