基本上,我正在使用find命令搜索存在于许多目录中的多字文件,并且输出存储在变量vari中
vari = `find -name "multi word file.xml"
当我尝试使用for循环来删除文件时,
for file in ${vari[@]}
执行失败说。
rm: cannot remove `/abc/xyz/multi':: No such file or directory
你们能帮我解决这种情况吗?
最佳答案
如果您确实需要预先捕获数组中的所有文件路径(假设bash
,主要是由于使用了数组和process substitution (<(...)
) [1];那么符合POSIX的解决方案会比较麻烦[2];还请注意,这是基于行的解决方案,因此它无法正确处理带有嵌入式换行符的文件名,但这在实践中非常罕见):
# Read matches into array `vari` - safely: no word splitting, no
# globbing. The only caveat is that filenames with *embedded* newlines
# won't be handled correctly, but that's rarely a concern.
# bash 4+:
readarray -t vari < <(find . -name "multi word file.xml")
# bash 3:
IFS=$'\n' read -r -d '' -a vari < <(find . -name "multi word file.xml")
# Invoke `rm` with all array elements:
rm "${vari[@]}" # !! The double quotes are crucial.
否则,让
find
直接执行删除操作(这些解决方案还可以正确处理带有嵌入式换行符的文件名):find . -name "multi word file.xml" -delete
# If your `find` implementation doesn't support `-delete`:
find . -name "multi word file.xml" -exec rm {} +
至于你尝试了什么:
vari=`find -name "multi word file.xml"`
(我删除了=
周围的空格,这将导致语法错误)不会创建数组;这样的command substitution会将随附命令中的stdout输出作为单个字符串返回(除去尾随的换行符)。通过将命令替换包含在
( ... )
中,可以创建一个数组:vari=( `find -name "multi word file.xml"` )
,但这会在
find
的输出上执行word splitting,并且不能正确保留带空格的文件名。尽管可以使用
IFS=$'\n'
解决此问题,以便仅在行边界处进行拆分,但是生成的令牌仍受pathname expansion (globbing)约束,这可能会无意中更改文件路径。尽管也可以使用shell选项解决此问题,但是您现在需要提前执行两项设置并恢复其原始值;因此,使用上面演示的
readarray
或read
是更简单的选择。即使您确实设法正确地将
$vari
中的文件路径收集为一个数组,也将破坏该数组为${vari[@]}
-不带双引号-因为生成的字符串将再次受到单词拆分和路径名扩展的影响(球)。要在不对其元素进行任何解释的情况下安全地将数组扩展为其元素,请双引号:
"${vari[@]}"
[1]
使用进程替代而不是管道,以确保
readarray
/ read
在当前外壳而不是子外壳中执行。正如eckes在注释中指出的那样,如果您尝试使用
find ... | IFS=$'\n' read ...
,则read
将在子shell中运行,这意味着它创建的变量将在命令返回时消失(超出范围),并且无法执行。稍后使用。[2]
POSIX Shell规范。不支持数组或进程替换(既不支持
readarray
,也不支持除read
以外的任何-r
选项);您必须按以下方式实施逐行处理:while IFS='
' read -r vari; do
pv vari
done <<EOF
$(find . -name "multi word file.xml")
EOF
注意,由于
IFS='
syntax不可用,因此在'
和$'\n'
之间需要实际换行符才能分配换行符。