используя xargs для grep нескольких шаблонов

12

У меня есть файл, в котором есть термины, к которым я хочу обратиться, причем каждый термин представляет собой одну строку в файле. Я думал, что смогу сделать это с помощью xargs. Что я могу почерпнуть из примеров со страницы руководства, подобной этой

find ./work -print0 | xargs -0 rm

в том, что xargs добавляет вывод команды pre-pipe к концу своих аргументов. Так что, если находка вернулась report.doc, тогда xargs построил бы rm report.doc. Это понимание правильно?

Поэтому, поскольку я хочу, чтобы значения в моем файле находились в середине команды grep, мне нужно указать заполнитель. Играя вокруг, я пытался {}, но это не сработало:

$> cat strings.txt | xargs grep {} subdirectory/*
grep: string1: No such file or directory
grep: string2: No such file or directory

Является ли Xargs правильным инструментом? Если так, то каков синтаксис?

user394
источник

Ответы:

17

Да, find ./work -print0 | xargs -0 rmвыполнит что-то вроде rm ./work/a "work/b c" .... Вы можете проверить echo, find ./work -print0 | xargs -0 echo rmнапечатает команду, которая будет выполнена (за исключением того, что пробел будет экранирован соответствующим образом, хотя это echoне будет отображаться).

Чтобы xargsпоместить имена в середину, вам нужно добавить -I[string], где [string]вы хотите заменить аргументом, в этом случае вы будете использовать -I{}, например <strings.txt xargs -I{} grep {} directory/*.

Что вы на самом деле хотите использовать grep -F -f strings.txt:

-F, --fixed-strings
  Interpret PATTERN as a  list  of  fixed  strings,  separated  by
  newlines,  any  of  which is to be matched.  (-F is specified by
  POSIX.)
-f FILE, --file=FILE
  Obtain  patterns  from  FILE,  one  per  line.   The  empty file
  contains zero patterns, and therefore matches nothing.   (-f  is
  specified by POSIX.)

Так grep -Ff strings.txt subdirectory/*что найдет все вхождения любой строки в strings.txtкачестве литерала, если вы опустите -Fопцию, вы можете использовать регулярные выражения в файле. Вы могли бы на самом деле использовать grep -F "$(<strings.txt)" directory/*тоже. Если вы хотите попрактиковаться find, вы можете использовать два последних примера в резюме. Если вы хотите выполнить рекурсивный поиск, а не только на первом уровне, у вас есть несколько вариантов, также в сводке.

Резюме:

# grep for each string individually.
<strings.txt xargs -I{} grep {} directory/*

# grep once for everything
grep -Ff strings.txt subdirectory/*
grep -F "$(<strings.txt)" directory/*

# Same, using file
find subdirectory -maxdepth 1 -type f -exec grep -Ff strings.txt {} +
find subdirectory -maxdepth 1 -type f -print0 | xargs -0 grep -Ff strings.txt

# Recursively
grep -rFf strings.txt subdirectory
find subdirectory -type f -exec grep -Ff strings.txt {} +
find subdirectory -type f -print0 | xargs -0 grep -Ff strings.txt

Вы можете использовать -lопцию, чтобы получить только имя каждого соответствующего файла, если вам не нужно видеть фактическую строку:

-l, --files-with-matches
  Suppress  normal  output;  instead  print the name of each input
  file from which output would normally have  been  printed.   The
  scanning  will  stop  on  the  first match.  (-l is specified by
  POSIX.)
Kevin
источник
4

xargsможет быть не лучшим инструментом, который я бы посоветовал, fgrepесли ваш файл сопоставляемых строк содержит только строки, а не регулярные выражения.

fgrep -f strings.txt subdirectory/*

Я предлагаю в fgrepкачестве традиционного Unix grepи egrepне имел опции "-f". Я считаю, что GNU grepи egrepесть опция "-f", поэтому, если в вашем файле есть регулярные выражения, вы можете использовать версию GNU.

Брюс Эдигер
источник