【问题标题】:Bash: find | sed | xargs rm not working, but rm does重击:查找 | sed | xargs rm 不起作用,但 rm 可以
【发布时间】:2019-11-20 12:37:53
【问题描述】:

我正在尝试从名为__tests__src 的任何子目录中删除所有.js.js.map 文件。

$ find . -path './src/**' -name __tests__ | # find subdirectories
> sed -E 's/([^ ]+__tests__)/\1\/*.js \1\/*.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__)/\1\/*.js \1\/*.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__)/\1\/*.js \1\/*.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。

【问题讨论】:

  • 您告诉rm 删除一个字面上称为*.js 的文件。
  • 哦,如何去掉它周围的''
  • 周围没有'';没有什么可以删除的。
  • rm 总是按字面意思解释它的参数。 * 扩展是由 shell 执行的,但你在这里直接从 sedrm
  • 你想要的是rm ./src/**/__tests__/*.js{,.map}吗? (当然,需要启用globstar 选项)。

标签: bash sed scripting xargs


【解决方案1】:

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

可以说,这里根本不需要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,让它来完成与*.js匹配的单个文件列表的工作,而不是将这项工作留给稍后发生:

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

【讨论】:

    【解决方案2】:

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

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

    ...解释一下:

    • -d $'\n' 确保 xargs 仅在换行符处拆分(不是空格!),并阻止它将反斜杠和引号视为特殊。
    • sh -c '...' sh 作为脚本运行 ...sh 作为 $0,后续参数在 $1 等; for f; 将因此迭代这些参数。
    • IFS= 清除IFS 可防止在使用不带引号的$f 时发生字符串拆分,因此只会发生全局扩展。
    • rm 使用-- 参数可确保它将后续参数视为文件名,而不是选项,即使它们以破折号开头。

    也就是说,如果每个模式都有很多文件,即使您使用的是 xargs,您也可能会遇到“参数列表太长”。


    另一个需要注意的是,包含换行符的文件名可能会拆分为多个名称(取决于您使用的 find 版本的详细信息)。一种适用于所有符合 POSIX 标准的 find 版本的解决方法可能是:

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

    【讨论】:

    • xargs sh -c rm 实际上根本不会将参数传递给rmsh 的后续参数在您的脚本中变为 $0$1 等;但脚本只是rm;它根本不读 $0$1
    • 你需要一个 shell 来扩展 glob,但你不需要 eval 来做。任何未加引号的参数扩展都会进行通配(当set -f 不活动时)。例如,xargs -d $'\n' sh -c 'IFS=; for item; do rm -- $item; done' _ 应该安全地完成这项工作。
    • 正确,清除 IFS 可防止在未引用的扩展上发生字符串拆分,因此唯一剩下的行为是通配符。下划线填充$0,因此其他项目变为$1 及以后,因此for item 迭代它们。我使用换行符是因为 OP 的 find | sed 管道会为每个要评估的 glob 表达式发出一行;而在没有-0-d 的情况下,xargs 进行了一堆解析,基本上使它像一个稍微有问题的外壳(试图尊重引号,除非它们前面有反斜杠等)。
    • 也就是说,切换到使用 NUL 分隔符(find -print0sed -zxargs -0)肯定会更安全,因为这样你就不需要担心文件了使用d=$'src/evil/__tests__/\n/etc/paswd\n/' && mkdir -p "$d" && touch "$d/hi.js" 创建的,如果您安装的find-print 版本在文件名包含的情况下发出文字换行符,则会导致您删除/etc/passwd
    • (现代 GNU find 不会通过带有 -print 的文字换行符,但它带有 -printf '%s\n';POSIX 规范没有定义行为,因此不能相信不同的版本会以任何一致的方式运行——而-print0 不容易受到这类问题的影响,因为 NUL 在 UNIX 领域的任何文件名中都无效,因此在将名称传入和传出内核空间、传入/传出 libc 等时使用的 NUL 分隔字符串。
    猜你喜欢
    • 2018-01-17
    • 1970-01-01
    • 2012-07-19
    • 2012-09-06
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    • 2017-08-07
    相关资源
    最近更新 更多