Szukam sposobu, aby wyświetlić listę wszystkich plików w katalogu zawierającym pełny zestaw słów kluczowych, których szukam, w dowolnym miejscu pliku.
Tak więc słowa kluczowe nie muszą pojawiać się w tym samym wierszu.
Jednym ze sposobów na to byłoby:
grep -l one $(grep -l two $(grep -l three *))
Trzy słowa kluczowe to tylko przykład, równie dobrze mogą to być dwa lub cztery itd.
Drugi sposób, jaki mogę wymyślić, to:
grep -l one * | xargs grep -l two | xargs grep -l three
Trzecią metodą, która pojawiła się w innym pytaniu , byłoby:
find . -type f \
-exec grep -q one {} \; -a \
-exec grep -q two {} \; -a \
-exec grep -q three {} \; -a -print
Ale zdecydowanie nie w tym kierunku idę tutaj. Chcę coś, co wymaga mniej pisać, a może tylko jedno wywołanie grep
, awk
,perl
lub podobny.
Na przykład podoba mi się sposób awk
dopasowania linii zawierających wszystkie słowa kluczowe , takie jak:
awk '/one/ && /two/ && /three/' *
Lub wydrukuj tylko nazwy plików:
awk '/one/ && /two/ && /three/ { print FILENAME ; nextfile }' *
Ale chcę znaleźć pliki, w których słowa kluczowe mogą znajdować się w dowolnym miejscu pliku, niekoniecznie w tej samej linii.
Preferowane rozwiązania byłyby przyjazne dla gzip, na przykład grep
ma zgrep
wariant, który działa na skompresowanych plikach. Dlaczego wspominam o tym, że niektóre rozwiązania mogą nie działać dobrze, biorąc pod uwagę to ograniczenie. Na przykład w awk
przykładzie drukowania pasujących plików nie można po prostu:
zcat * | awk '/pattern/ {print FILENAME; nextfile}'
Musisz znacząco zmienić polecenie, na coś takiego:
for f in *; do zcat $f | awk -v F=$f '/pattern/ { print F; nextfile }'; done
Z powodu tego ograniczenia musisz dzwonić awk
wiele razy, nawet jeśli możesz to zrobić tylko raz z nieskompresowanymi plikami. I na pewno fajniej byłoby po prostu zrobić zawk '/pattern/ {print FILENAME; nextfile}' *
i uzyskać ten sam efekt, więc wolałbym rozwiązania, które na to pozwalają.
gzip
przyjaźni, tylkozcat
pliki.grep
rozwiązania można łatwo dostosować, poprzedzającgrep
połączenia znakiem „a”z
, nie muszę też obsługiwać nazw plików.grep
. AFAIK, tylkogrep
icat
mają standardowe „warianty Z”. Nie sądzę, że uzyskasz coś prostszego niż użyciefor f in *; do zcat -f $f ...
rozwiązania. Wszystko inne musiałoby być pełnym programem, który sprawdza formaty plików przed otwarciem lub korzysta z biblioteki, aby zrobić to samo.Odpowiedzi:
Jeśli chcesz automatycznie obsługiwać pliki spakowane gzip, uruchom to w pętli za pomocą
zcat
(powolne i nieefektywne, ponieważ będziesz rozwidlaćawk
wiele razy w pętli, raz dla każdej nazwy pliku) lub przepisz ten sam algorytmperl
i użyjIO::Uncompress::AnyUncompress
modułu biblioteki, który może rozpakuj kilka różnych rodzajów skompresowanych plików (gzip, zip, bzip2, lzop). lub w pythonie, który ma również moduły do obsługi skompresowanych plików.Oto
perl
wersja, która używaIO::Uncompress::AnyUncompress
pozwala na dowolną liczbę wzorców i dowolną liczbę nazw plików (zawierających zwykły tekst lub skompresowany tekst).Wszystkie argumenty wcześniej
--
są traktowane jako wzorce wyszukiwania. Wszystkie argumenty później--
są traktowane jak nazwy plików. Prymitywna, ale skuteczna obsługa opcji dla tego zadania. Lepszą obsługę opcji (np. W celu obsługi-i
opcji wyszukiwania bez rozróżniania wielkości liter) można uzyskać za pomocąGetopt::Std
lubGetopt::Long
modułów .Uruchom tak:
(Nie wymienię plików,
{1..6}.txt.gz
a{1..6}.txt
tutaj ... zawierają one tylko niektóre lub wszystkie słowa „jeden” „dwa” „trzy” „cztery” „pięć” i „sześć” do testowania. Pliki wymienione w wynikach powyżej Zawierają wszystkie trzy wzorce wyszukiwania. Sprawdź to sam na podstawie własnych danych)Hash
%patterns
zawiera pełny zestaw wzorców, które pliki muszą zawierać co najmniej jeden z każdego elementu,$_pstring
to ciąg zawierający posortowane klucze tego hasha. Ciąg$pattern
zawiera wstępnie skompilowane wyrażenie regularne również zbudowane z%patterns
skrótu.$pattern
jest porównywany z każdą linią każdego pliku wejściowego (przy użyciu/o
modyfikatora do kompilacji$pattern
tylko raz, ponieważ wiemy, że nigdy się nie zmieni podczas uruchamiania), imap()
jest używany do budowania skrótu (% s) zawierającego dopasowania dla każdego pliku.Ilekroć wszystkie wzory są widoczne w bieżącym pliku (przez porównanie, czy
$m_string
(posortowane klucze%s
są równe$p_string
), wydrukuj nazwę pliku i przejdź do następnego pliku.Nie jest to szczególnie szybkie rozwiązanie, ale nie jest nieuzasadnione powolne. Pierwsza wersja zajęła 4m58 sekund, aby wyszukać trzy słowa w skompresowanych plikach dziennika o wartości 74 MB (łącznie 937 MB bez kompresji). Ta aktualna wersja zajmuje 1m13s. Prawdopodobnie można dokonać dalszych optymalizacji.
Jednym z oczywistych optymalizacji jest do tego użyć w połączeniu z
xargs
„s-P
aka--max-procs
uruchamianie wielu wyszukiwań w podgrupach plików równolegle. Aby to zrobić, musisz policzyć liczbę plików i podzielić przez liczbę rdzeni / cpus / wątków, które ma Twój system (i zaokrąglić w górę, dodając 1). np. w moim zestawie próbek przeszukano 269 plików, a mój system ma 6 rdzeni (AMD 1090T), więc:Dzięki tej optymalizacji znalezienie wszystkich 18 pasujących plików zajęło tylko 23 sekundy. Oczywiście to samo można zrobić z dowolnym innym rozwiązaniem. UWAGA: Kolejność nazw plików wymienionych w danych wyjściowych będzie inna, więc może to wymagać późniejszego posortowania, jeśli to ma znaczenie.
Jak zauważył @arekolek, wiele
zgrep
s zfind -exec
lubxargs
może to zrobić znacznie szybciej, ale ten skrypt ma tę zaletę, że obsługuje dowolną liczbę wzorców do wyszukiwania i jest w stanie poradzić sobie z kilkoma różnymi typami kompresji.Jeśli skrypt ogranicza się do zbadania tylko pierwszych 100 wierszy każdego pliku, przechodzi przez wszystkie (w mojej 74 MB próbce 269 plików) w 0,6 sekundy. Jeśli jest to przydatne w niektórych przypadkach, można je przekształcić w opcję wiersza poleceń (np.
-l 100
), Ale istnieje ryzyko , że nie uda się znaleźć wszystkich pasujących plików.BTW, według strony
IO::Uncompress::AnyUncompress
podręcznika, obsługiwane formaty kompresji to:Ostatnia (mam nadzieję) optymalizacja. Korzystając z
PerlIO::gzip
modułu (spakowanego w debian aslibperlio-gzip-perl
) zamiast zużywaćIO::Uncompress::AnyUncompress
czas do około 3,1 sekundy na przetworzenie moich 74 MB plików dziennika. Wprowadzono również niewielkie ulepszenia, używając prostego skrótu zamiastSet::Scalar
(co również pozwoliło zaoszczędzić kilka sekund w przypadkuIO::Uncompress::AnyUncompress
wersji).PerlIO::gzip
był zalecany jako najszybszy perl gunzip w /programming//a/1539271/137158 (znaleziony przy wyszukiwaniu w Googleperl fast gzip decompress
)Używanie
xargs -P
z tym wcale go nie poprawiło. W rzeczywistości wydawało się, że nawet spowalnia to od 0,1 do 0,7 sekundy. (Próbowałem czterech uruchomień, a mój system wykonuje inne czynności w tle, które zmienią czas)Cena jest taka, że ta wersja skryptu obsługuje tylko pliki spakowane i nieskompresowane. Szybkość vs elastyczność: 3,1 sekundy dla tej wersji vs 23 sekund dla
IO::Uncompress::AnyUncompress
wersji zxargs -P
otoką (lub 1m13s bezxargs -P
).źródło
for f in *; do zcat $f | awk -v F=$f '/one/ {a++}; /two/ {b++}; /three/ {c++}; a&&b&&c { print F; nextfile }'; done
działa dobrze, ale w rzeczywistości zajmuje 3 razy więcej czasu niż mojegrep
rozwiązanie i jest bardziej skomplikowane.apt-get install libset-scalar-perl
użyć skryptu. Ale wydaje się, że nie kończy się w rozsądnym czasie.Ustaw separator rekordów na
.
tak,awk
aby traktował cały plik jako jedną linię:Podobnie z
perl
:źródło
for f in *; do zcat $f | awk -v RS='.' -v F=$f '/one/ && /two/ && /three/ { print F }'; done
nic nie produkuje.zcat -f "$f"
jeśli niektóre pliki nie są skompresowane.awk -v RS='.' '/bfs/&&/none/&&/rgg/{print FILENAME}' greptest/*.txt
nadal nie zwraca żadnych wyników, agrep -l rgg $(grep -l none $(grep -l bfs greptest/*.txt))
zwraca oczekiwane wyniki.W przypadku plików skompresowanych można zapętlić każdy plik i najpierw rozpakować. Następnie, używając nieco zmodyfikowanej wersji innych odpowiedzi, możesz:
Skrypt Perla zakończy działanie ze
0
statusem (sukces), jeśli wszystkie trzy ciągi zostaną znalezione. Jest}{
to skrót od PerlaEND{}
. Wszystko po nim zostanie wykonane po przetworzeniu wszystkich danych wejściowych. Skrypt zakończy działanie ze statusem wyjścia innym niż 0, jeśli nie zostaną znalezione wszystkie ciągi. Dlatego&& printf '%s\n' "$f"
wydrukuje nazwę pliku tylko wtedy, gdy wszystkie trzy zostaną znalezione.Lub, aby uniknąć ładowania pliku do pamięci:
Wreszcie, jeśli naprawdę chcesz zrobić wszystko w skrypcie, możesz:
Zapisz skrypt powyżej jako
foo.pl
gdzieś w swoim$PATH
, ustaw go jako wykonywalny i uruchom go w następujący sposób:źródło
Ze wszystkich proponowanych do tej pory rozwiązań moje oryginalne rozwiązanie wykorzystujące grep jest najszybsze i kończy się w 25 sekund. Wadą jest to, że żmudne jest dodawanie i usuwanie słów kluczowych. Wymyśliłem więc skrypt (dubbingowany
multi
), który symuluje zachowanie, ale pozwala zmienić składnię:Więc teraz pisanie
multi grep one two three -- *
jest równoważne mojej oryginalnej propozycji i działa w tym samym czasie. Mogę też łatwo użyć go w skompresowanych plikach, używajączgrep
jako pierwszego argumentu.Inne rozwiązania
Eksperymentowałem również ze skryptem Python, stosując dwie strategie: wyszukiwanie wszystkich słów kluczowych wiersz po wierszu i wyszukiwanie w całym pliku słów kluczowych według słowa kluczowego. Druga strategia była szybsza w moim przypadku. Ale było wolniejsze niż zwykłe używanie
grep
, kończąc w 33 sekundy. Dopasowywanie słów kluczowych wiersz po wierszu zakończone w 60 sekund.Skrypt podany przez terdon wykończone w 54 sekund. Właściwie zajęło to 39 sekund czasu na ścianie, ponieważ mój procesor jest dwurdzeniowy. Co jest interesujące, ponieważ mój skrypt w języku Python zajął 49 sekund czasu na ścianie (i
grep
wynosił 29 sekund).Scenariusz cas nie udało się zakończyć w rozsądnym czasie, nawet na mniejszą liczbę plików, które zostały przetworzone z
grep
poniżej 4 sekund, więc musiałem go zabić.Ale jego oryginalna
awk
propozycja, choć wolniejsza niżgrep
obecnie, ma potencjalną przewagę. W niektórych przypadkach, przynajmniej z mojego doświadczenia, można oczekiwać, że wszystkie słowa kluczowe powinny pojawić się gdzieś w nagłówku pliku, jeśli w ogóle są w pliku. Daje to rozwiązaniu znaczny wzrost wydajności:Kończy się za kwadrans, w przeciwieństwie do 25 sekund.
Oczywiście możemy nie mieć przewagi w wyszukiwaniu słów kluczowych, o których wiadomo, że występują na początku plików. W takim przypadku rozwiązanie bez
NR>100 {exit}
zajmuje 63 sekundy (50 sekund czasu na ścianie).Pliki nieskompresowane
Nie ma znaczącej różnicy w czasie działania między moim
grep
rozwiązaniem a cas 'awk
propozycją , wykonanie obu zajmuje ułamek sekundy.Zauważ, że inicjalizacja zmiennej
FNR == 1 { f1=f2=f3=0; }
jest w takim przypadku obowiązkowa, aby zresetować liczniki dla każdego kolejnego przetwarzanego pliku. Jako takie, to rozwiązanie wymaga edycji polecenia w trzech miejscach, jeśli chcesz zmienić słowo kluczowe lub dodać nowe. Z drugiej strony,grep
możesz po prostu dołączyć| xargs grep -l four
lub edytować słowo kluczowe.Wadą
grep
rozwiązania wykorzystującego zastępowanie poleceń jest to, że zawiesi się ono, jeśli w dowolnym miejscu w łańcuchu, przed ostatnim krokiem nie będzie pasujących plików. Nie wpływa to naxargs
wariant, ponieważ rura zostanie przerwana, gdygrep
zwróci status niezerowy. Zaktualizowałem skrypt, aby go używać,xargs
więc nie muszę sobie z tym radzić, dzięki czemu skrypt jest prostszy.źródło
not all(p in text for p in patterns)
not
) i zakończyło się w 32 sekundy, więc nie tak wiele ulepszeń, ale z pewnością jest bardziej czytelne.PerlIO::gzip
zamiastIO::Uncompress::AnyUncompress
. teraz przetworzenie moich 74 MB plików dziennika zajmuje tylko 3,1 sekundy zamiast 1m13s.eval $(lesspipe)
(np. W swoim.profile
itp.), Możesz użyćless
zamiast tego,zcat -f
afor
opakowanie pętliawk
będzie w stanie przetworzyć dowolny rodzaj pliku, któryless
może (gzip, bzip2, xz i więcej) .... less może wykryć, czy stdout jest potokiem, i po prostu wyśle strumień do stdout, jeśli jest.Inna opcja - podaj słowa pojedynczo
xargs
, aby działałygrep
na pliku.xargs
może zostać zmuszony do wyjścia, gdy tylko wywołaniegrep
niepowodzenia zakończy się niepowodzeniem przez powrót255
do niego (sprawdźxargs
dokumentację). Oczywiście spawnowanie pocisków i rozwidlanie zaangażowane w to rozwiązanie prawdopodobnie znacznie go spowolniąi zapętlić
źródło
_
ifile
? Czy to wyszukiwanie w wielu plikach zostanie przekazane jako argument i zwróci pliki zawierające wszystkie słowa kluczowe?_
tego, jest przekazywany jako$0
spawnowana powłoka - pojawi się jako nazwa polecenia w danych wyjściowychps
- Chciałbym odłożyć się tutaj do mistrza