
Я пытаюсь сделать следующее:
cat file1.txt | xargs -I{} "cat file2.txt | grep {}"
Я ожидаю, что каждая строка из file1 будет значением для grep в конце третьего канала. Это не работает так, как ожидалось.
Это потому что -I{}
прекращает искать вещи для замены, как только попадает в трубу? Есть ли способ обойти это?
решение1
Это потому, что вам нужна оболочка для создания канала или выполнения перенаправления. Обратите внимание, что cat
это команда для конкатенации, нет смысла использовать ее только для одного файла.
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e "$1"' sh {}
Делатьнетделать:
cat file1.txt | xargs -I{} sh -c 'cat file2.txt | grep -e {}'
поскольку это будет равносильно уязвимости инъекции команды. Будет {}
расширен в аргументе кода, чтобы sh
интерпретироваться как шелл-код. Например, если одна строка 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{}
довольно бесполезно.
Если вы хотите, чтобы каждая строка передавалась дословно как аргумент команды, вы можете использовать xargs
-d '\n'
расширение GNU:
< file1.txt xargs -d '\n' -n 1 grep file2.txt -e
(здесь полагаемся на другое расширение GNU grep
, которое позволяет передавать параметры после аргументов (при условии, что в среде нет POSIXly correct) или переносимо:
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
Это отличается от вашей исходной команды.(как только мы исправим это, как в ответе Стефана Шазеласа) следующим образом:
- Строки печатаются в том порядке, в котором они появляются,
file2.txt
независимо от того, каким шаблонам они соответствуют. В вашей команде печатаются все строки, соответствующие первому шаблону, затем все строки, соответствующие второму, и так далее. - Строки, соответствующие более чем одному шаблону, печатаются ровно один раз. В вашей команде они печатаются один раз для каждого шаблона, которому они соответствуют.
- Несколько флагов можно использовать проще, включая
-v
и-c
.
Флаг -f
- этоспецифицировано POSIXи поэтому достаточно портативен.