省略传递一个空的引用参数
omit passing an empty quoted argument
我在 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 可以省略带引号的空变量吗?
试试:
printf "%s\n%s\n%s\n" "$afile" "$empty" "$anotherfile" | egrep -v '^$' | tr '\n' '[=10=]' | xargs -0 cat
一个纯 bash 解决方案可以使用数组。 "$empty"
将计算为空参数,而 "${empty[@]}"
将扩展到所有引用的数组字段,在本例中为 none.
$ afile=(/dev/null)
$ unset empty
$ alsoempty=()
$ cat "${afile[@]}" "${empty[@]}" "${alsoempty[@]}"
在无法选择数组的情况下,请参阅 pasaba por aqui 的更通用的答案。
对于像 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 "" ]] && cat ""; }
在 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=""
_args=()
shift
for arg; do [[ -n "$arg" ]] && _args+=("$arg"); done
}
例如:
non_empty my_args "$file1" "$file2" "$file3"
grep "$pattern" "${my_args[@]}"
您可以使用 alternate value parameter expansion (${var+altvalue}
) 来包含引用的变量(如果已设置):
cat ${afile+"$afile"} ${empty+"$empty"} ${anotherfile+"$anotherfile"}
由于双引号位于替代值字符串中(而不是围绕整个参数表达式),因此它们仅在设置变量时生效。请注意,您可以使用 +
(如果设置了变量,则使用备用值)或 :+
(如果设置了变量且不为空,则使用备用值)。
我在 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 可以省略带引号的空变量吗?
试试:
printf "%s\n%s\n%s\n" "$afile" "$empty" "$anotherfile" | egrep -v '^$' | tr '\n' '[=10=]' | xargs -0 cat
一个纯 bash 解决方案可以使用数组。 "$empty"
将计算为空参数,而 "${empty[@]}"
将扩展到所有引用的数组字段,在本例中为 none.
$ afile=(/dev/null)
$ unset empty
$ alsoempty=()
$ cat "${afile[@]}" "${empty[@]}" "${alsoempty[@]}"
在无法选择数组的情况下,请参阅 pasaba por aqui 的更通用的答案。
对于像 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 "" ]] && cat ""; }
在 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=""
_args=()
shift
for arg; do [[ -n "$arg" ]] && _args+=("$arg"); done
}
例如:
non_empty my_args "$file1" "$file2" "$file3"
grep "$pattern" "${my_args[@]}"
您可以使用 alternate value parameter expansion (${var+altvalue}
) 来包含引用的变量(如果已设置):
cat ${afile+"$afile"} ${empty+"$empty"} ${anotherfile+"$anotherfile"}
由于双引号位于替代值字符串中(而不是围绕整个参数表达式),因此它们仅在设置变量时生效。请注意,您可以使用 +
(如果设置了变量,则使用备用值)或 :+
(如果设置了变量且不为空,则使用备用值)。