2016-06-07 40 views
5

Czasami jestem grep -ing tysięcy plików i byłoby miło zobaczyć jakiś postęp (pasek lub status).grep - jak wypisać pasek postępu lub status

wiem, że to nie jest trywialny, ponieważ grep wyprowadza wyniki wyszukiwania do STDOUT i mój domyślny przepływ pracy jest to, że dane wyjściowe wyniki do pliku, a chcieliby postęp bar/stan na wyjściu do STDOUT lub STDERR.

Czy wymagałoby to modyfikacji kodu źródłowego grep?

Idealny polecenia:

grep -e "STRING" --results="FILE.txt"

i postęp:

[curr file being searched], number x/total number of files 

zapisywane STDOUT lub STDERR

+0

Czy rozważałeś użycie skryptu, aby to zrobić? To prostsze niż edycja kodu źródłowego grep –

Odpowiedz

7

To niekoniecznie wymagają modyfikacji grep, choć prawdopodobnie można uzyskać dokładniejsze pasek postępu z taką modyfikacją.

Jeśli przeglądasz "tysiące plików" za pomocą jednego wywołania polecenia grep, najprawdopodobniej używasz opcji -r do rekursywnie struktury katalogów.W takim przypadku nie jest nawet jasne, czy grep wie, ile plików będzie badać, ponieważ uważam, że zaczyna analizować pliki, zanim przejrzy całą strukturę katalogów. Najpierw odkrywanie struktury katalogów prawdopodobnie zwiększyłoby całkowity czas skanowania (i, w rzeczy samej, zawsze istnieje koszt tworzenia raportów z postępu, dlatego kilka tradycyjnych narzędzi Unix to robi).

W każdym razie prosty, ale nieznaczny niedokładny pasek postępu można uzyskać, tworząc kompletną listę plików do zeskanowania, a następnie przesyłając je do grep w partiach o pewnym rozmiarze, może 100, lub może w oparciu o całkowity rozmiar partii. Małe partie pozwoliłyby na dokładniejsze raporty z postępów, ale zwiększyłyby również koszty ogólne, ponieważ wymagałyby dodatkowego uruchomienia procesu grep, a czas rozpoczęcia procesu może być czymś więcej niż otwieraniem małego pliku. Raport postępu byłby aktualizowany dla każdej partii plików, więc chciałbyś wybrać rozmiar wsadu, który dawałby regularne aktualizacje bez zwiększania narzutów za dużo. Oparcie rozmiaru wsadu na całkowitym rozmiarze plików (przy użyciu na przykład stat w celu uzyskania rozmiaru pliku) sprawiłoby, że raport postępu byłby dokładniejszy, ale dodał dodatkowy koszt do uruchomienia procesu.

Jedną z zalet tej strategii jest to, że można równolegle uruchomić dwa lub więcej komunikatów, co może nieco przyspieszyć proces.


W szerokim ujęciu, prosty skrypt (który właśnie dzieli pliki liczbowo, a nie wielkość, a co nie próbować parallelize).

# Requires bash 4 and Gnu grep 
shopt -s globstar 
files=(**) 
total=${#files[@]} 
for ((i=0; i<total; i+=100)); do 
    echo $i/$total >>/dev/stderr 
    grep -d skip -e "$pattern" "${files[@]:i:100}" >>results.txt 
done 

Dla uproszczenia używam globstar (**), aby bezpiecznie umieścić wszystkie pliki w tablicy. Jeśli twoja wersja bash jest zbyt stara, możesz to zrobić, wykonując pętlę nad wynikiem find, ale to nie jest zbyt wydajne, jeśli masz dużo plików. Niestety nie ma sposobu, aby napisać wyrażenie globstar, które dopasowuje tylko pliki. (**/ dopasowuje tylko katalogi.) Na szczęście GNU grep udostępnia opcję -d skip, która po cichu pomija katalogi. Oznacza to, że liczba plików będzie nieznacznie niedokładna, ponieważ katalogi będą zliczane, ale prawdopodobnie nie ma to znaczenia.

Prawdopodobnie chcesz wyczyścić raport postępu za pomocą niektórych kodów konsoli. Powyższe jest tylko po to, abyś zaczął.

Najprostszym sposobem na podzielenie tego na różne procesy byłoby po prostu podzielenie listy na X różnych segmentów i uruchomienie X różnych dla pętli, z których każda ma inny punkt początkowy. Jednak prawdopodobnie nie wszyscy skończą w tym samym czasie, więc nie jest to optymalne. Lepszym rozwiązaniem jest równoległe GNU. Można zrobić coś takiego:

find . -type f -print0 | 
parallel --progress -L 100 -m -j 4 grep -e "$pattern" > results.txt 

(tutaj -L 100 określa, że ​​do 100 pliki powinny być podane do każdej instancji grep i -j 4 określa cztery procesy równoległe właśnie wyciągnął te numery z powietrza; ci”. ll prawdopodobnie chcą je dostosować.)

+0

Bardzo dobra i prawie pełna odpowiedź. Proszę napisać przykład, jak używać poleceń 'find, parallel, grep', aby wykonać zadanie, a ja oznaczy je jako zaakceptowany. – Adrian

+0

@adrian: pomogłoby to dowiedzieć się, w jaki sposób obecnie wywołujesz grep: '' r' 'było tylko odgadnięciem. – rici

+0

moim zwykłym poleceniem grep jest 'grep -e" STRING "* -r'. Jednoczesne przesyłanie plików * X to doskonały pomysł. – Adrian

0

jestem całkiem pewny, że trzeba zmień kod źródłowy grep. A te zmiany byłyby ogromne.

Obecnie grep nie wie, ile wierszy pliku, dopóki nie zakończyło parsowania całego pliku. Dla Twojego wymagania będzie musiała przeanalizować plik 2 razy lub najmniej określić pełną linię liczenia w inny sposób.

Za pierwszym razem określi liczbę linii dla paska postępu. Za drugim razem wykona pracę i poszuka twojego wzoru.

Nie tylko zwiększy to środowisko wykonawcze, ale także złamie jedną z głównych filozofii systemu UNIX.

  1. każdym programem robić jedną rzecz dobrze. Aby wykonać nową pracę, buduj od nowa, zamiast komplikować stare programy, dodając nowe "funkcje". (source)

Mogą istnieć inne narzędzia tam za potrzebą, ale AFAIK grep nie pasuje tutaj.

+1

OP nie mówi nic o licznikach linii, tylko pliki.Nie jest nawet jasne, czy liczba linii byłaby przydatna; prostszą statystyką do zebrania byłyby całkowite bajty (które można uzyskać z call to stat), a to byłaby dokładniejsza statystyka również, ponieważ grep faktycznie czyta się w blokach, a nie liniach. Zgadzam się jednak z podstawową filozofią twojej odpowiedzi. – rici

+0

Przepraszam, że źle zrozumiałem wynik "numer x" myśl, że on oznacza linię x w pliku y. – cb0

1

I normalnie używać coś takiego:

grep | tee "FILE.txt" | cat -n | sed 's/^/match: /;s/$/  /' | tr '\n' '\r' 1>&2 

to nie jest idealny, ponieważ jest wyświetlany tylko zapałki, a jeśli im się długo lub różnią się znacznie w długości są błędy, ale powinna zapewnić ty z ogólną ideą.

lub zwykłą kropki:

grep | tee "FILE.txt" | sed 's/.*//' | tr '\n' '.' 1>&2 
+1

Jak to wskazuje na status? – Adrian

+0

'grep -e" STRING "| tee "FILE.txt" 'ma nadzieję, że odpowiedź na twoje' grep -e "STRING" --results = "FILE.txt" ', ale nie ma to być pełny status jak' x/całkowita liczba plików' . Pokazuje tylko liczbę przetworzonych już meczów. –

1

Spróbuj program równoległy

find * -name \*.[ch] | parallel -j5 --bar '(grep grep-string {})' > output-file 

Choć znalazłem to być wolniejsze niż prosty

find * -name \*.[ch] | xargs grep grep-string > output-file