首页 > 解决方案 > 为什么 xargs 会在空格上拆分输入以及如何解决它?

问题描述

在以下bash脚本中,我将文件列表从路径捕获到变量中,然后将其传递到xargs以进行进一步操作。

我发现只需echo对变量进行 ing,即可为每一行适当地添加空格,并为每一行添加一个换行符终止符。但是,当我printfecho此结束时xargs,我发现xargs似乎也将每行的输入都用空格分隔。我将通过以下示例进行说明,其中包含我看到的结果的注释:

# Using GNU find:
list="$( find '$SOME_PATH' -type f )"

excluded_list="$( egrep -v -f FILE_WITH_PATTERNS_OF_FOLDERS_TO_EXCLUDE <<< $list )"

# This prints out just fine with lines such as "/some/path/here with spaces" on their own line, eg:
#   /some/path/here with spaces
#   /another/path/here with spaces
#   /and yet another/path/here with spaces
echo "$excluded_list"

# But this prints out a line such as the above example "/some/path/here with spaces" broken up like this instead:
#   /some/path/here 
#   with 
#   spaces
#   /another/path/here 
#   with 
#   spaces
#   /and 
#   yet
#   another/path/here 
#   with 
#   spaces
printf "%s" "$excluded_list" | xargs -n 1 -P 1 sh -c 'echo "$0"'
# And the same result as `printf` above:
echo "$excluded_list" | xargs -n 1 -P 1 sh -c 'echo "$0"'

标签: bashgrepfindechoxargs

解决方案


将多个文件名分配给单个变量是一种反模式,因为除了空字节之外的任何特殊字符都可能出现在文件名中,并且您不能再次将变量拆分为原始文件名。

在您的示例echo "$excluded_list" 中可能看起来像保留原始文件名,但不幸的是它不是。尝试在路径名中插入两个或更多连续的空格,看看会发生什么。
作为急救,您可以$list用双引号括起来,<<< "$list"但这只是一种临时补救措施。

解决方法是:

  1. 将文件名存储在数组中
  2. 使用空字节作为分隔符并通过管道处理结果

例如,您可以这样说:

while IFS= read -r -d "" f; do
    excluded_list+=("$f")
done < <(find "$SOME_PATH" -type f -print0 | egrep -v -z -Z -f FILE_WITH_PATTERNS_OF_FOLDERS_TO_EXCLUDE -)
for f in "${excluded_list[@]}"; do
    echo "$f"
done

或者

find "$SOME_PATH" -type f -print0 | egrep -v -z -Z -f FILE_WITH_PATTERNS_OF_FOLDERS_TO_EXCLUDE - | xargs -0 -n 1 -P 1 sh -c 'echo "$0"'

请注意,-z-Z选项是 GNU grep 的扩展,可能不适用于其他平台。


推荐阅读