簡體   English   中英

對grep列出的*每個*項目執行操作

[英]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, worldHello, 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腳本或從whiledone三行。

這里的想法是, 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM