Xargs во вторую сторону трубы?

Xargs во вторую сторону трубы?

Я пытаюсь сделать следующее:

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и поэтому достаточно портативен.

Связанный контент