Jak dołączyć rurkę | w moim poleceniu linux find -exec?

220

To nie działa. Czy można to zrobić w znalezieniu? A może muszę xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;
jakiś facet
źródło

Odpowiedzi:

144

Zadaniem interpretacji symbolu potoku jako instrukcji do uruchomienia wielu procesów i potokowania wyniku jednego procesu do wejścia innego procesu jest odpowiedzialność powłoki (/ bin / sh lub równoważnej).

W twoim przykładzie możesz albo użyć powłoki najwyższego poziomu, aby wykonać potokowanie w następujący sposób:

find -name 'file_*' -follow -type f -exec zcat {} \; | agrep -dEOE 'grep'

Pod względem wydajności wynik ten kosztuje jedno wywołanie find, liczne wywołania Zcat i jedno wywołanie zgody.

Spowodowałoby to odrodzenie tylko jednego procesu uzgadniania, który przetworzyłby wszystkie dane wyjściowe wygenerowane przez liczne wywołania Zcat.

Jeśli z jakiegoś powodu chciałbyś kilkakrotnie wywołać zgodę, możesz:

find . -name 'file_*' -follow -type f \
    -printf "zcat %p | agrep -dEOE 'grep'\n" | sh

To konstruuje listę poleceń za pomocą potoków do wykonania, a następnie wysyła je do nowej powłoki w celu ich wykonania. (Pominięcie ostatniego „| sh” to dobry sposób na debugowanie lub wykonywanie suchych uruchomień linii poleceń w ten sposób.)

Pod względem wydajności wynik ten kosztuje jedno wywołanie find, jedno wywołanie sh, liczne wywołania zcat i liczne wywołania zgody.

Najbardziej wydajnym rozwiązaniem pod względem liczby wywołań poleceń jest sugestia Paula Tomblina:

find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'

... która kosztuje jedno wywołanie znaleziska, jedno wywołanie xargs, kilka wywołań Zcat i jedno wywołanie zgody.

Rolf W. Rasmussen
źródło
1
Kolejną zaletą xargs byłoby to, że możesz przyspieszyć go dzięki nowoczesnemu wielordzeniowemu procesorowi jeszcze bardziej, używając przełącznika -P (-P 0).
flolo,
Tak, przełącznik -P jest rzeczywiście dobrym sposobem na ogólne przyspieszenie wykonania. Niestety istnieje ryzyko, że dane wyjściowe z równoległych procesów Zcat zostaną potokowane do przeplatanych uzgodnień, co może wpłynąć na wynik. Efekt ten można zademonstrować za pomocą: echo -e "1 \ n2" | xargs -P 0 -n 1 tak | uniq
Rolf W. Rasmussen
@Adam, wprowadziłem sugerowaną zmianę.
Paul Tomblin
dla którego możesz zainstalować wspaniałe polecenie xjobs (pierwotnie z Solaris)
patrz
4
Prostszy i bardziej ogólnie odpowiedź na stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Eric O Lebigot,
277

rozwiązanie jest proste: uruchom przez sh

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
flolo
źródło
17
To, co PO próbował osiągnąć, można spełnić za pomocą powyższych sugestii, ale to właśnie ta odpowiedź odpowiada na zadane pytanie. Są powody, aby robić to w ten sposób - exec jest znacznie potężniejszy niż tylko operowanie na plikach zwracanych przez find, szczególnie w połączeniu z testem. Na przykład: znajdź geda-gaf / -type d -exec bash -c 'DIR = {}; [[$ (znajdź $ DIR -maxdepth 1 | xargs grep -i spice | wc -l) -ge 5]] && echo $ DIR '\; Zwróci wszystkie katalogi w ścieżce wyszukiwania, które zawierają więcej niż 5 wierszy łącznie między wszystkimi plikami w tym katalogu zawierającym słowo spice
swarfrat
3
Najlepsza odpowiedź. Grepowanie całego wyniku (jak sugerują inne odpowiedzi) nie jest takie samo jak grep dla każdego pliku. Wskazówka: zamiast sh możesz użyć dowolnej innej powłoki (wypróbowałem to z bash i działa poprawnie).
pagliuca
1
Pamiętaj, aby nie przeoczyć -copcji. W przeciwnym razie pojawi się zagadkowy No such file or directorykomunikat o błędzie.
asmaier,
oto świetny zamiennik ps, który wykorzystuje find z potokowaniem wewnątrz powłoki exec'd: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
parzystość 3
1
Przykład znajdowania plików i zmiany nazwy ich sed za pomocą wyrażenia regularnego find -type f -name '*.mdds' -exec sh -c "echo {} | sed -e 's/_[0-9]\+//g' | xargs mv {}" \;
ROSTFREI
16
find . -name "file_*" -follow -type f -print0 | xargs -0 zcat | agrep -dEOE 'grep'
Paul Tomblin
źródło
Mając nadzieję na uniknięcie -print i xargs ze względu na wydajność. Może to naprawdę mój problem: find nie może obsłużyć potokowych poleceń przez -exec
niekiedy
To nie działa z plikami ze spacjami w ich nazwach; naprawić, zamień -print na -print0 i dodaj opcję -0 do xargs
Adam Rosenfield
2
@someguy - Wha? Unikanie xargs ze względu na wydajność? Wywołanie jednej instancji Zcat i przekazanie jej listy wielu plików jest znacznie bardziej wydajne niż wykonanie nowej instancji dla każdego znalezionego pliku.
Sherm Pendley,
@Adam - Wprowadziłem sugerowaną zmianę. 99% czasu, w którym szukam, znajduje się w moich katalogach kodu źródłowego i żaden z plików nie ma spacji, więc nie zawracam sobie głowy print0. Z drugiej strony, mój katalog dokumentów, pamiętam print0.
Paul Tomblin
10

Możesz także wykonać potok do whilepętli, która może wykonywać wiele akcji na findzlokalizowanym pliku . Więc tutaj jest jeden do przeszukiwania jararchiwów dla danego pliku klasy Java w folderze z dużą dystrybucją jarplików

find /usr/lib/eclipse/plugins -type f -name \*.jar | while read jar; do echo $jar; jar tf $jar | fgrep IObservableList ; done

kluczową kwestią jest to, że whilepętla zawiera wiele poleceń odnoszących się do przekazanej nazwy pliku oddzielonych średnikiem, a te polecenia mogą zawierać potoki. W tym przykładzie odbijam nazwę pasującego pliku, a następnie wymieniam zawartość filtrowania archiwum dla danej nazwy klasy. Dane wyjściowe wyglądają następująco:

/usr/lib/eclipse/plugins/org.eclipse.core.contenttype.source_3.4.1.R35x_v20090826-0451.jar /usr/lib/eclipse/plugins/org.eclipse.core.databinding.observable_1.2.0.M20090902-0800 .jar org / eclipse / core / databinding / observable / list / IObservableList .class /usr/lib/eclipse/plugins/org.eclipse.search.source_3.5.1.r351_v20090708-0800.jar / usr / lib / eclipse / plugins / org.eclipse.jdt.apt.core.source_3.3.202.R35x_v20091130-2300.jar /usr/lib/eclipse/plugins/org.eclipse.cvs.source_1.0.400.v201002111343.jar / usr / lib / eclipse / plugins / org.eclipse.help.appserver_3.1.400.v20090429_1800.jar

w mojej powłoce bash (xubuntu10.04 / xfce) naprawdę sprawia, że ​​dopasowana nazwa klasy jest pogrubiona, ponieważ fgreppodświetla dopasowany ciąg; dzięki temu bardzo łatwo jest zeskanować listę setek jarplików, które zostały przeszukane i łatwo zobaczyć wszelkie dopasowania.

w systemie Windows możesz zrobić to samo z:

for /R %j in (*.jar) do @echo %j & @jar tf %j | findstr IObservableList

zwróć uwagę, że w systemie Windows separatorem poleceń jest „&” not ”; i że „@” tłumi echo polecenia, aby uzyskać uporządkowane wyjście, tak jak w przypadku linux find powyżej; chociaż findstrnie powoduje pogrubienia dopasowanego łańcucha, więc musisz przyjrzeć się bliżej wynikowi, aby zobaczyć nazwę pasującej klasy. Okazuje się, że polecenie „for” systemu Windows zna kilka sztuczek, takich jak przechodzenie między plikami tekstowymi ...

cieszyć się

simbo1905
źródło
2

Przekonałem się, że uruchomienie polecenia powłoki łańcuchowej (sh -c) działa najlepiej, na przykład:

find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;
Andrew Khoury
źródło
0

Jeśli szukasz prostej alternatywy, możesz to zrobić za pomocą pętli:

for i in $(find -name 'file_*' -follow -type f);do zcat $i | agrep -dEOE 'grep');done

lub bardziej ogólna i łatwa do zrozumienia forma:

for i in $(YOUR_FIND_COMMAND);do YOUR_EXEC_COMMAND_AND_PIPES );done

i zamień {} na $ i w YOUR_EXEC_COMMAND_AND_PIPES

Louis Gagnon
źródło