如果我理解您的规范,您希望:
grep --null -l '<pattern>' directory/*.extension1 | \
xargs -n 1 -0 -I{} bash -c 'rm "$1" "${1%.*}.extension2"' -- {}
这与@triplee 的评论所描述的基本相同,只是它是换行安全的。
这是怎么回事?
grep 和 --null 将返回以空值而不是换行符分隔的输出。由于文件名中可以有换行符,用换行符分隔使得无法安全地解析 grep 的输出,但 null 不是文件名中的有效字符,因此是一个很好的分隔符。
xargs 将采用一系列以换行符分隔的项目并执行给定命令,将尽可能多的这些项目(每个参数一个)传递给给定命令(如果没有给出命令,则传递给echo)。因此,如果你说:
printf 'one\ntwo three \nfour\n' | xargs echo
xargs 将执行 echo one 'two three' four。这对于文件名是不安全的,因为文件名可能包含嵌入的换行符。
-0 切换到 xargs 将其从寻找换行符分隔符更改为空分隔符。这使它与我们从grep --null 得到的输出相匹配,并且可以安全地处理文件名列表。
通常xargs 只是将输入附加到命令的末尾。 -I 切换到xargs 将其更改为用输入替换指定的替换字符串。要了解这个想法,请尝试以下实验:
printf 'one\ntwo three \nfour\n' | xargs -I{} echo foo {} bar
并注意与之前的printf | xargs 命令的区别。
在我的解决方案中,我执行的命令是bash,我将-c 传递给它。 -c 开关使 bash 执行以下参数中的命令(然后终止),而不是启动交互式 shell。下一个块'rm "$1" "${1%.*}.extension2"' 是-c 的第一个参数,是bash 将执行的脚本。 -c 的脚本参数后面的任何参数都被指定为脚本的参数。这个,如果我要说:
bash -c 'echo $0' "Hello, world"
然后Hello, world 将被分配给$0(脚本的第一个参数),并且在脚本中我可以将echo 返回。
因为$0 通常是为脚本名称保留的,所以我传递了一个虚拟值(在本例中为--)作为第一个参数,然后,我写了{} 来代替第二个参数,这是我为xargs 指定的替换字符串。这将被 xargs 替换,每个文件名在执行 bash 之前从 grep 的输出中解析。
迷你 shell 脚本可能看起来很复杂,但其实很简单。首先,整个脚本是单引号的,以防止调用 shell 解释它。在脚本中,我调用rm 并将两个文件名传递给它以删除:$1 参数,这是在上面替换替换字符串时传递的文件名,以及${1%.*}.extension2。后者是$1 变量的参数替换。重要的部分是%.*,上面写着
-
% "从变量末尾开始匹配,去掉匹配模式的最短字符串。
-
.* 模式是一个句点,后面跟着任何东西。
这有效地从文件名中删除扩展名(如果有)。你可以自己观察效果:
foo='my file.txt'
bar='this.is.a.file.txt'
baz='no extension'
printf '%s\n'"${foo%.*}" "${bar%.*}" "${baz%.*}"
由于扩展名已被剥离,我将所需的备用扩展名.extension2 连接到剥离的文件名以获得备用文件名。