spe*_*ndo 8 bash shell grep list exec
如何单独为grep列出的每个项目执行操作?
背景:
我使用grep列出包含特定模式的所有文件:
grep -l '<pattern>' directory/*.extension1
Run Code Online (Sandbox Code Playgroud)
我想删除所有列出的文件,但也删除所有具有相同文件名但扩展名不同的文件:.extension2.
我尝试使用管道,但它似乎将grep的输出作为一个整体.
在找到有-exec选项,但grep没有这样的.
Sor*_*gal 14
如果我理解您的规范,您需要:
grep --null -l '<pattern>' directory/*.extension1 | \
xargs -n 1 -0 -I{} bash -c 'rm "$1" "${1%.*}.extension2"' -- {}
Run Code Online (Sandbox Code Playgroud)
这与@ triplee的评论所描述的基本相同,只是它是换行安全的.
grepwith --null将返回以null而不是换行符分隔的输出.由于文件名可以在其中使用换行符分隔,因此无法grep安全地解析输出,但null不是文件名中的有效字符,因此是一个很好的分隔符.
xargs将获取换行符分隔项并执行给定命令,将尽可能多的项(每个参数一个)传递给给定命令(或者echo如果没有给出命令则传递).因此如果你说:
printf 'one\ntwo three \nfour\n' | xargs echo
Run Code Online (Sandbox Code Playgroud)
xargs会执行echo one 'two three' four.这对于文件名是不安全的,因为文件名可能同样包含嵌入的换行符.
-0用于xargs将其从查找换行符分隔符更改为空分隔符的开关.这使它与我们得到的输出相匹配,grep --null并使其可以安全地处理文件名列表.
通常xargs只需将输入附加到命令的末尾即可.的-I开关来xargs改变这对代换指定替换字符串与输入.为了实现这个想法尝试这个实验:
printf 'one\ntwo three \nfour\n' | xargs -I{} echo foo {} bar
Run Code Online (Sandbox Code Playgroud)
并注意与早期printf | xargs命令的区别.
在我的解决方案的情况下,我执行的命令是bash我传递的-c.该-c开关使bash执行以下参数中的命令(然后终止),而不是启动交互式shell.下一个块'rm "$1" "${1%.*}.extension2"'是第一个参数,-c并且是将由其执行的脚本bash.脚本参数后面的任何参数-c都被指定为脚本的参数.如果我这样说:
bash -c 'echo $0' "Hello, world"
Run Code Online (Sandbox Code Playgroud)
然后Hello, world将被分配给$0(脚本的第一个参数)并在脚本中我可以echo回来.
因为$0通常是为脚本名称保留的,所以我传递一个虚拟值(在本例中--)作为第一个参数,然后代替我写的第二个参数{},这是我指定的替换字符串xargs.这将被替换为执行前xargs从grep输出解析的每个文件名bash.
迷你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%.*}"
Run Code Online (Sandbox Code Playgroud)
由于扩展已被剥离,我将所需的备用扩展连接.extension2到剥离的文件名以获取备用文件名.