Bash: 查找 |标准 | xargs rm 不工作,但 rm 可以

Bash: find | sed | xargs rm not working, but rm does

我正在尝试从 src 的任何名为 __tests__.

的子目录中删除所有 .js.js.map 文件
$ find . -path './src/**' -name __tests__ | # find subdirectories
> sed -E 's/([^ ]+__tests__)/\/*.js \/*.js.map/g' | # for each subdirectory, concat *.js and *.js.map
> xargs rm # remove files

失败并出现以下错误:

rm: cannot remove './src/game/__tests__/*.js': No such file or directory
rm: cannot remove './src/game/__tests__/*.js.map': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js.map': No such file or directory

但是,如果我将我的 xargs rm 更改为 xargs echo rm,复制并粘贴输出,然后 运行 它就可以了。

$ find . -path './src/**' -name __tests__ | sed -E 's/([^ ]+__tests__)/\/*.js \/*.js.map/g' |
> xargs echo rm # echo command to remove files
rm ./src/game/__tests__/*.js ./src/game/__tests__/*.js.map ./src/helpers/__tests__/*.js ./src/helpers/__tests__/*.js.map

$ rm ./src/game/__tests__/*.js ./src/game/__tests__/*.js.map ./src/helpers/__tests__/*.js ./src/helpers/__tests__/*.js.map

$(...) 中包装我的 echo 的输出并在前面添加 rm 导致与以前相同的错误。

$ rm $(find . -path './src/**' -name __tests__ | sed -E 's/([^ ]+__tests__)/\/*.js \/*.js.map/g' | xargs echo rm
rm: cannot remove './src/game/__tests__/*.js': No such file or directory
rm: cannot remove './src/game/__tests__/*.js.map': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js': No such file or directory
rm: cannot remove './src/helpers/__tests__/*.js.map': No such file or directory

我做错了什么?

我怀疑这是否重要,但我在 Windows.

上使用 GitBash

您需要展开 glob (*)。文件名扩展由 UNIX 上的 shell 执行,而不是由 rm 或其他程序执行。尝试:

.... | xargs -d $'\n' sh -c 'IFS=; for f; do rm -- $f; done' sh

...解释一下:

  • -d $'\n' 确保 xargs 仅在换行符(而不是空格!)上拆分,并阻止它把反斜杠和引号视为特殊字符。
  • sh -c '...' sh 运行s ...作为脚本,sh作为[=18=],后续参数在</code>等; <code>for f; 将因此迭代这些参数。
  • IFS= 清除 IFS 可防止 string-splitting 在 $f 未加引号使用时发生,因此只会发生 glob 扩展。
  • -- 参数用于 rm 确保它将后续参数视为文件名,而不是选项,即使它们以破折号开头。

就是说,如果每个模式确实有很多文件,即使您使用的是 xargs,也可能 运行 到 "argument list too long"。


另一个警告是包含换行符的文件名可能会被拆分成多个名称(取决于您使用的 find 版本的详细信息)。一种适用于所有 POSIX-compliant 版本 find 的解决方法可能是:

find ./src -type d -name __tests__ -exec sh -c '
  for d; do
    rm -- "$d"/*.js{,.map}
  done
' sh {} +

首先,解释一下这个问题:在find | sed | xargs rm中,shell只是建立了那些程序之间的通信,但实际上并不以任何方式处理结果。这是一个问题,因为 *.js 需要通过 shell 扩展 以用文件名列表替换它; rm 将给定的每个参数都视为文字名称。 (这与 Windows 不同,其中程序自己进行 command-line 解析和 glob 扩展。

可以说,您根本不需要 find。考虑:

shopt -s globstar                 # enable ** as a recursion operator
rm ./src/**/__tests__/*.js{,.map} # delete *.js and *.js.map in any __tests__ directory under src

...或者,如果您 想使用 find,让它完成列出与 [=13 匹配的单个文件的工作=],而不是让这项工作稍后发生:

find src -regextype posix-egrep -regex '.*/__tests__/[^/]*[.]js([.]map)?' -delete