我有一組txt
文件,其名稱可能包含空格或特殊字符,例如#
.
我有一個grep
解決方案grep -L "cannot have" $(grep -l "must have" *.txt)
來列出所有具有must have
但不具有cannot have
.
例如,有一個文件abc defg.txt
僅包含 1 行:must have
.
所以通常 grep 解決方案應該找出abc defg.txt
,但它會返回:
grep: abc: No such file or directory
grep: defg.txt: No such file or directory
我認為對於包含 的檔名#
,grep 解決方案也是無效的。
誰能幫我修改 grep 解決方案嗎?
答案1
如果如果你願意走得更遠,awk 可以一次完成:
awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0}
/must have/{a=1} /cannot have/{b=1} END{s()}' filepattern
對於最近的 gawk,您可以使用 BEGINFILE 和 ENDFILE 來簡化。 (像所有 awk 答案一樣,您可以使用 -f 將 awk 命令放入檔案中,並且像大多數一樣,如果您願意,您可以輕鬆轉換為 perl。)
答案2
由於您已經在使用 GNU 特定選項 ( -L
),您可以這樣做:
grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"
這個想法是用於-Z
列印 NUL 分隔的文件名列表,並用於xargs -r0
將該列表作為參數傳遞給第二個grep
.
預設情況下,命令替換按空格、製表符和換行符(以及 中的 NUL zsh
)進行分割。類似於 Bourne 的 shellzsh
也對分裂產生的每個單字執行通配符操作。
你可以這樣做:
IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
set +f # we need globbing for *.txt in this subshell though
grep -l -- "must have" *.txt
)
但這仍然會破壞包含換行符的檔案名稱。
在zsh
(且zsh
僅),您可以執行以下操作:
IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)
或者:
grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}
答案3
考慮使用shell 指令來find
代替:grep
find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'