używając xargs do grepowania wielu wzorów

12

Mam plik, który zawiera warunki, dla których chcę grepować, przy czym każdy termin jest jedną linią w pliku. Myślałem, że mogę to zrobić za pomocą xargs. To, co jestem w stanie czerpać z przykładów ze strony podręcznika tego typu

find ./work -print0 | xargs -0 rm

jest to, że xargs dołącza wynik polecenia poprzedzającego potok na końcu swoich argumentów. Jeśli więc znalezisko zwróci report.doc, xargs zbuduje rm report.doc. Czy to zrozumienie jest prawidłowe?

Ponieważ chcę, aby wartości w moim pliku znajdowały się w środku polecenia grep, muszę określić symbol zastępczy. Podczas zabawy próbowałem {}, ale to nie działało:

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

Czy xargs jest właściwym narzędziem? Jeśli tak, jaka jest składnia?

użytkownik394
źródło

Odpowiedzi:

17

Tak, find ./work -print0 | xargs -0 rmwykona coś takiego rm ./work/a "work/b c" .... Możesz sprawdzić za pomocą echo, find ./work -print0 | xargs -0 echo rmwydrukuje polecenie, które zostanie wykonane (z wyjątkiem tego, że białe znaki zostaną odpowiednio zmienione, chociaż echonie będzie tego wyświetlać).

Aby xargsumieścić nazwy na środku, musisz dodać -I[string], gdzie [string]jest to, co chcesz zastąpić argumentem, w tym przypadku użyłbyś -I{}np <strings.txt xargs -I{} grep {} directory/*.

W rzeczywistości chcesz użyć 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.)

Tak więc grep -Ff strings.txt subdirectory/*znajdzie wszystkie wystąpienia dowolnego łańcucha strings.txtjako literał, jeśli upuścisz -Fopcję, możesz użyć wyrażeń regularnych w pliku. Możesz też użyć grep -F "$(<strings.txt)" directory/*. Jeśli chcesz ćwiczyć find, możesz użyć dwóch ostatnich przykładów w podsumowaniu. Jeśli chcesz przeprowadzić wyszukiwanie rekurencyjne zamiast tylko pierwszego poziomu, masz kilka opcji, również w podsumowaniu.

Podsumowanie:

# 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

Możesz użyć tej -lopcji, aby uzyskać tylko nazwę każdego pasującego pliku, jeśli nie potrzebujesz widzieć rzeczywistej linii:

-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
źródło
4

xargsmoże nie być najlepszym narzędziem, które zasugerowałbym, fgrepjeśli plik dopasowanych ciągów zawiera tylko ciągi, a nie wyrażenia regularne.

fgrep -f strings.txt subdirectory/*

Sugeruję fgrepjako tradycyjny Unix grepi egrepnie miałem opcji „-f”. Wierzę, że GNU grepi egrepmam opcję „-f”, więc jeśli twój plik zawiera wyrażenia regularne, powinieneś użyć wersji GNU.

Bruce Ediger
źródło