
我正在嘗試執行以下操作:
cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
我期望 file1 中的每一行都是第三個管道末端的 grep 的值。它沒有按預期工作。
這是因為-I{}
一旦它到達管道就停止尋找替換的東西嗎?有沒有解決的辦法?
答案1
這是因為您需要一個 shell 來建立管道或執行重新導向。請注意,這cat
是連接命令,僅對一個檔案使用它沒有什麼意義。
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
做不是做:
貓檔1.txt | xargs -I{} sh -c 'cat file2.txt | xargs -I{} sh -c 'cat file2.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'
因為這相當於命令注入漏洞。將{}
在程式碼參數中擴展sh
為 shell 程式碼。例如,如果 的 一行file1.txt
是$(reboot)
,則將呼叫reboot
.
(-e
或者您也可以使用--
)也很重要。如果沒有它,您就會遇到以 . 開頭的正規表示式問題-
。
您可以使用重定向來簡化上述操作,而不是cat
:
< file1.txt xargs -I{} sh -c '< file2.txt grep -e "$1"' sh {}
或者簡單地將檔案名稱作為參數傳遞給grep
而不是使用重定向,在這種情況下您甚至可以刪除sh
:
< file1.txt xargs -I{} grep -e {} file2.txt
您也可以告訴grep
在一次呼叫中立即尋找所有正規表示式:
grep -f file1.txt file2.txt
但請注意,在這種情況下, 的每一行都只有一個正規表示式file1.txt
, 沒有進行任何特殊的引號處理xargs
。
xargs
預設情況下,將其輸入視為空白清單(在某些實作中只有空格和製表符,在其他實作中只有[:blank:]
當前語言環境的字元類別中的任何字元)或換行符分隔的單詞,其中反斜槓以及單引號和雙引號可用於轉義分隔符號(換行符只能透過反斜線轉義)或彼此。
例如,對於這樣的輸入:
'a "b'\" "bar baz" x\
y
xargs
沒有-I{}
將通過a "b"
,bar baz
和x<newline>y
命令。
使用 時-I{}
,xargs
每行獲取一個單詞,但仍進行一些額外的處理。它忽略前導(但不忽略尾隨)空格。空格不再被視為分隔符,但報價處理仍在進行中。
上面的輸入將向命令xargs -I{}
傳遞一個參數。a "b" foo bar x<newline>y
另請注意,根據 POSIX 的要求,如果單字長度超過 255 個字符,許多系統將無法運作。總而言之,xargs -I{}
很沒用。
如果您希望將每一行逐字作為參數傳遞給命令,您可以使用 GNUxargs
-d '\n'
擴充:
< file1.txt xargs -d '\n' -n 1 grep file2.txt -e
(這裡依賴 GNU 的另一個擴展grep
,它允許在參數之後傳遞選項(前提是環境中不存在 POSIXly 正確的選項)或可移植:
sed "s/'/'\\\\\\''/g;s/.*/'&'/" file1.txt | xargs -n1 sh -c '
for line do
grep -e "$line" file2.txt
done' sh
如果你想要每一個單字在file1.txt
(引號仍然被識別)而不是每個線要查找(如果每行只有一個單詞,這也可以解決尾隨空格問題),您可以xargs -n1
單獨使用而不是使用-I
:
< file1.txt xargs -n1 sh -c '
for word do
grep -e "$word" file2.txt
done' sh
要移除前導和尾隨空白(但沒有引號處理xargs
),您還可以執行以下操作:
unset IFS # restore word splitting to its default
while read -r regexp; do
grep -e "$regexp" file2.txt
done < file1.txt
答案2
根據您想要執行的操作,您最好xargs
完全跳過並使用此解決方案:
grep -f file1.txt file2.txt
這與您原來的命令不同(一旦我們按照 Stéphane Chazelas 的回答修復它)如下:
- 線條按照它們出現的順序列印,
file2.txt
無論它們匹配哪種模式。在您的命令中,將列印與第一個模式相符的所有行,然後列印與第二個模式相符的所有行,依此類推。 - 與多個模式相符的行將只列印一次。在您的命令中,它們會針對每個匹配的模式列印一次。
- 可以更輕鬆地使用多個標誌,包括
-v
和-c
。
國旗-f
是由 POSIX 指定因此相當便攜。