grep dla wielu ciągów w pliku w różnych wierszach (tj. cały plik, a nie wyszukiwanie oparte na wierszach)?


85

Chcę grep dla plików zawierających słowa Dansk, Svenskaczy Norskna każdej linii, z użytkowym returnCode (jak ja naprawdę tylko jak mieć informacje, które zawarte są struny, mój jeden-liner idzie trochę dalej niż teraz).

Mam wiele plików z takimi liniami:

Disc Title: unknown
Title: 01, Length: 01:33:37.000 Chapters: 33, Cells: 31, Audio streams: 04, Subpictures: 20
        Subtitle: 01, Language: ar - Arabic, Content: Undefined, Stream id: 0x20, 
        Subtitle: 02, Language: bg - Bulgarian, Content: Undefined, Stream id: 0x21, 
        Subtitle: 03, Language: cs - Czech, Content: Undefined, Stream id: 0x22, 
        Subtitle: 04, Language: da - Dansk, Content: Undefined, Stream id: 0x23, 
        Subtitle: 05, Language: de - Deutsch, Content: Undefined, Stream id: 0x24, 
(...)

Oto pseudokod tego, czego chcę:

for all files in directory;
 if file contains "Dansk" AND "Norsk" AND "Svenska" then
 then echo the filename
end

Jaki jest najlepszy sposób, aby to zrobić? Czy można to zrobić w jednej linii?

Odpowiedzi:


89

Możesz użyć:

grep -l Dansk * | xargs grep -l Norsk | xargs grep -l Svenska

Jeśli chcesz znaleźć również w ukrytych plikach:

grep -l Dansk .* | xargs grep -l Norsk | xargs grep -l Svenska

Sprytne rozwiązanie; jedną rzeczą, na którą należy zwrócić uwagę (ogólnie rzecz biorąc; nie dotyczy to tego, o co prosił PO) jest to, że ogólny kod zakończenia będzie wynosił 0 nawet w przypadku (koncepcyjnego) niepowodzenia. Tak więc, jeśli byłbyś zainteresowany określeniem niepowodzenia w porównaniu z sukcesem, musiałbyś albo zbadać, czy wyjście standardowego wyjścia jest puste, czy też nie, albo zastosować podejście @ EddSteel.
mklement0

@mklement: W Bash PIPESTATUStablica zawiera wartości wyjściowe elementów potoku.
Wstrzymano do odwołania.

@DennisWilliamson Dobrze wiedzieć, dziękuję. Inną opcją jest włączenie pipefailopcji powłoki (tymczasowo):shopt -so pipefail
mklement0

4
Możesz chcieć użyć grep -Zi xargs -0jeśli twoje nazwy plików mogą zawierać spacje.
Ben Challenor,

1
Może to powodować błędy typu „Lista argumentów za długa”, jeśli masz wiele plików.
AnnanFay

23

Jeszcze inny sposób używania tylko bash i grep:

Dla pojedynczego pliku „test.txt”:

  grep -q Dansk test.txt && grep -q Norsk test.txt && grep -l Svenska test.txt

Zostanie wydrukowane, test.txtjeśli plik zawiera wszystkie trzy (w dowolnej kombinacji). Pierwsze dwa polecenia greps niczego nie drukują ( -q), a ostatni drukuje plik tylko wtedy, gdy pozostałe dwa przeszły pomyślnie.

Jeśli chcesz to zrobić dla każdego pliku w katalogu:

   dla f in *; do grep -q Dansk $ f && grep -q Norsk $ f && grep -l Svenska $ f; Gotowe

ale wtedy nie ma potrzeby wykonywania grep 3 razy.
kurumi,

1
Wiem, że możesz łączyć wzory za pomocą -e, ale nie widziałem sposobu na wykonanie koniunkcji w samym grep.
Edd Steel

1
Świetny; re for f ...: use "$f"(podwójne cudzysłowy), a nie tylko $fpo to, aby upewnić się, że nazwy plików z osadzonymi spacjami itp. są poprawnie obsługiwane.
mklement0

Zaletą tego podejścia w stosunku do @ vmpstr jest to, że kod zakończenia poprawnie odzwierciedla, czy wszystkie wyszukiwane hasła zostały znalezione, czy nie.
mklement0

19
grep –irl word1 * | grep –il word2 `cat -` | grep –il word3 `cat -`
  • -i sprawia, że ​​wielkość liter w wyszukiwaniu jest niewrażliwa
  • -r powoduje, że wyszukiwanie plików jest cykliczne w folderach
  • -l potokuje listę plików ze znalezionym słowem
  • cat - powoduje, że następny grep przejrzy listę plików przekazanych do niego.

1
to najprostsza i najprostsza odpowiedź, bardzo pomocne dzięki!
majick

9

Jak grepować wiele ciągów w pliku w różnych liniach (użyj symbolu potoku):

for file in *;do 
   test $(grep -E 'Dansk|Norsk|Svenska' $file | wc -l) -ge 3 && echo $file
done

Uwagi:

  1. Jeśli użyjesz podwójnych cudzysłowów ""z grepem, będziesz musiał uciec od potoku w ten sposób: \|aby wyszukać Dansk, Norsk i Svenska.

  2. Zakłada, że ​​jedna linia ma tylko jeden język.

Przewodnik: http://www.cyberciti.biz/faq/howto-use-grep-command-in-linux-unix/


Czy to nie zawiedzie, jeśli Dansk Norsk i Svenska pojawią się na tej samej linii?
vmpstr

Tak, w takim przypadku by się nie udało. Założyłem, że języki pojawiają się po jednym w każdym wierszu.
Damodharan R

Byłoby to również składane, gdybym miał tylko Norsk, ale w trzech różnych wierszach.
Benjamin W.

6

Możesz to zrobić naprawdę łatwo za pomocą potwierdzenia :

ack -l 'cats' | ack -xl 'dogs'
  • -l: zwraca listę plików
  • -x: weź pliki z STDIN (poprzednie wyszukiwanie) i przeszukaj tylko te pliki

Możesz po prostu kontynuować pracę, aż uzyskasz tylko te pliki, które chcesz.


Kiedy tego próbuję, mówi Unknown option: x. Czy istnieje wersja Ack, która obsługuje tę flagę x?
Hassan

4
awk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print "0" }' 

możesz następnie złapać wartość zwracaną za pomocą powłoki

jeśli masz Ruby (1,9+)

ruby -0777 -ne 'print if /Dansk/ and /Norsk/ and /Svenka/' file

1
w klauzuli END awk, prawdopodobnie chcesz: if (a && b && c) {exit 0} else {exit 1}lub bardziej zwięźleexit !(a && b && c)
Glenn Jackman

Twoje rozwiązanie rubinowe nie wygląda dobrze. spowoduje to wydrukowanie tylko akapitów zawierających wszystkie wyszukiwane słowa. pytanie brzmi: czy plik (jako całość) zawiera wszystkie słowa, nawet jeśli nie wszystkie występują w tym samym akapicie.
glenn jackman

dzięki. zmieniony, jeśli potrzebny jest cały plik, to trzeba użyć -0777
kurumi

4

To wyszukuje wiele słów w wielu plikach:

egrep 'abc|xyz' file1 file2 ..filen 

2
Oprócz znalezienia plików, które mają oba ciągi, spowoduje to również znalezienie plików, które mają tylko „abc” LUB „xyz”. Myślę, że OP prosił o pliki zawierające „abc” i „xyz”.
Chris Warth,

3

Po prostu:

grep 'word1\|word2\|word3' *

zobacz ten post, aby uzyskać więcej informacji


Dodałbym -lflagę, ale poza tym ta odpowiedź wydaje mi się najprostsza, chyba że czegoś brakuje.
xdhmoore

Tak, jest również bardziej wydajny, ponieważ nie przetwarzasz wszystkich danych w wielu rurach i filtrach
moshe beeri

3
Pytanie dotyczy wyrażenia, które zwraca pliki zawierające wszystkie trzy terminy; zwraca wiersze (zamiast nazw plików) zawierające dowolne z trzech (zamiast wszystkich trzech).
Benjamin W.

2

Jest to połączenie odpowiedzi Glenna Jackmana i Kurumi, co pozwala na użycie dowolnej liczby wyrażeń regularnych zamiast dowolnej liczby ustalonych słów lub ustalonego zestawu wyrażeń regularnych.

#!/usr/bin/awk -f
# by Dennis Williamson - 2011-01-25

BEGIN {
    for (i=ARGC-2; i>=1; i--) {
        patterns[ARGV[i]] = 0;
        delete ARGV[i];
    }
}

{
    for (p in patterns)
        if ($0 ~ p)
            matches[p] = 1
            # print    # the matching line could be printed
}

END {
    for (p in patterns) {
        if (matches[p] != 1)
            exit 1
    }
}

Uruchom to w ten sposób:

./multigrep.awk Dansk Norsk Svenska 'Language: .. - A.*c' dvdfile.dat

2

Oto, co działało dobrze dla mnie:

find . -path '*/.svn' -prune -o -type f -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh
./another/path/to/file2.txt
./blah/foo.php

Gdybym tylko chciał znaleźć pliki .sh z tymi trzema, mógłbym użyć:

find . -path '*/.svn' -prune -o -type f -name "*.sh" -exec gawk '/Dansk/{a=1}/Norsk/{b=1}/Svenska/{c=1}END{ if (a && b && c) print FILENAME }' {} \;
./path/to/file1.sh

1

Rozwijając odpowiedź awk @ kurumi, oto funkcja bash:

all_word_search() {
    gawk '
        BEGIN {
            for (i=ARGC-2; i>=1; i--) {
                search_terms[ARGV[i]] = 0;
                ARGV[i] = ARGV[i+1];
                delete ARGV[i+1];
            }
        }
        {
            for (i=1;i<=NF; i++) 
                if ($i in search_terms) 
                    search_terms[$1] = 1
        }
        END {
            for (word in search_terms) 
                if (search_terms[word] == 0) 
                    exit 1
        }
    ' "$@"
    return $?
}

Stosowanie:

if all_word_search Dansk Norsk Svenska filename; then
    echo "all words found"
else
    echo "not all words found"
fi

1

Zrobiłem to w dwóch krokach. Zrób listę plików csv w jednym pliku Z pomocą komentarzy na tej stronie wykonałem dwa kroki bez użycia skryptów, aby uzyskać to, czego potrzebowałem. Po prostu wpisz w terminalu:

$ find /csv/file/dir -name '*.csv' > csv_list.txt
$ grep -q Svenska `cat csv_list.txt` && grep -q Norsk `cat csv_list.txt` && grep -l Dansk `cat csv_list.txt`

zrobił dokładnie to, czego potrzebowałem - wydrukował nazwy plików zawierające wszystkie trzy słowa.

Zwróć także uwagę na symbole, takie jak `' "


1

Jeśli potrzebujesz tylko dwóch wyszukiwanych haseł, prawdopodobnie najbardziej czytelnym podejściem jest przeprowadzenie każdego wyszukiwania i przecięcie wyników:

 comm -12 <(grep -rl word1 . | sort) <(grep -rl word2 . | sort)

1

Jeśli masz zainstalowany program git

git grep -l --all-match --no-index -e Dansk -e Norsk -e Svenska

--No-index przeszukuje pliki w bieżącym katalogu, który nie jest zarządzany przez Git. Więc to polecenie będzie działać w każdym katalogu, niezależnie od tego, czy jest to repozytorium git, czy nie.


0

Miałem dzisiaj ten problem, a wszystkie jednolinijkowe tutaj zawiodły mnie, ponieważ pliki zawierały spacje w nazwach.

Oto, co wymyśliłem, że zadziałało:

grep -ril <WORD1> | sed 's/.*/"&"/' | xargs grep -il <WORD2>
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.