[英]perform an operation for *each* item listed by grep
如何分別對grep列出的每個項目執行操作?
背景:
我使用grep列出了包含特定模式的所有文件:
grep -l '<pattern>' directory/*.extension1
我想刪除所有列出的文件,還要刪除所有具有相同文件名但擴展名不同的文件: .extension2
。
我嘗試使用管道,但它似乎將grep的輸出作為一個整體。
在查找中有-exec
選項,但是grep沒有這樣的東西。
如果我了解您的規格,則需要:
grep --null -l '<pattern>' directory/*.extension1 | \
xargs -n 1 -0 -I{} bash -c 'rm "$1" "${1%.*}.extension2"' -- {}
這與@triplee的注釋所描述的基本相同,只是它是換行安全的。
帶--null
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
printf | xargs
命令。
對於我的解決方案,我執行的命令是bash
,我將-c
傳遞給該命令。 -c
開關使bash在以下參數中執行命令(然后終止),而不是啟動交互式shell。 下一個塊'rm "$1" "${1%.*}.extension2"'
是-c
的第一個參數,它是將由bash
執行的腳本。 -c
腳本參數之后的所有參數都將分配為腳本參數。 如果我要說的話:
bash -c 'echo $0' "Hello, world"
然后, Hello, world
將Hello, world
分配給$0
(腳本的第一個參數),然后在腳本中echo
它。
由於通常為腳本名稱保留$0
,因此我將一個虛擬值(在本例中為--
)作為第一個參數傳遞,然后代替第二個參數,我寫了{}
,這是我為xargs
指定的替換字符串。 在執行bash
之前,此文件將由xargs
替換,每個文件名均由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
連接到剝離的文件名,以獲得替代文件名。
如果這樣做符合您的要求,則通過/ bin / sh傳遞輸出。
grep -l 'RE' folder/*.ext1 | sed 's/\(.*\).ext1/rm "&" "\1.ext2"/'
或者,如果sed讓您發癢:
grep -l 'RE' folder/*.ext1 | while read file; do
echo rm "$file" "${file%.ext1}.ext2"
done
如果輸出看起來像您要運行的命令,請刪除echo
。
但是您也可以使用find
來做到這一點:
find /path/to/start -name \*.ext1 -exec grep -q 'RE' {} \; -print | ...
其中...
是sed腳本或從while
到done
三行。
這里的想法是, find
會...根據給定的限定符“查找”事物,即,事物與文件glob“ * .ext”匹配,並且“ exec”的結果成功。 -q
告訴grep在{}
(由find
提供的文件)中查找RE,然后以TRUE或FALSE退出而不生成其自身的任何輸出。
在find和使用grep進行搜索之間唯一真正的區別是,可以根據需要使用find的出色條件集合進一步縮小搜索范圍。 man find
詳細信息。 默認情況下,find將遞歸到子目錄中。
您可以將列表通過管道傳遞給xargs:
grep -l '<pattern>' directory/*.extension1 | xargs rm
至於第二組具有不同擴展名的文件,我會這樣做(通常在進行xargs echo rm
運行測試時使用xargs echo rm
;我尚未對其進行測試,它可能不適用於其中包含空格的文件名):
filelist=$(grep -l '<pattern>' directory/*.extension1)
echo $filelist | xargs rm
echo ${filelist//.extension1/.extension2} | xargs rm
將結果通過管道傳遞給xargs
,它將允許您為每個匹配項運行命令。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.