Как мне включить трубу | в моем linux найти команду -exec?

220

Это не работает Можно ли это сделать в find? Или мне нужно xargs?

find -name 'file_*' -follow -type f -exec zcat {} \| agrep -dEOE 'grep' \;
какой-то парень
источник

Ответы:

145

Ответственность за интерпретацию символа канала как инструкции для запуска нескольких процессов и передачи результатов одного процесса на вход другого процесса лежит на оболочке (/ bin / sh или эквивалентной).

В вашем примере вы можете выбрать использование оболочки верхнего уровня для выполнения конвейера следующим образом:

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

С точки зрения эффективности этот результат стоит одного вызова find, многочисленных вызовов zcat и одного вызова agrep.

Это приведет к появлению только одного agrep-процесса, который обработает весь вывод, полученный при многочисленных вызовах zcat.

Если по какой-то причине вы хотите вызывать agrep несколько раз, вы можете сделать:

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

Это создает список команд, использующих каналы для выполнения, а затем отправляет их новой оболочке для фактического выполнения. (Пропуск финального «| sh» - это хороший способ отладки или выполнения пробных запусков командных строк, подобных этой.)

С точки зрения эффективности этот результат стоит одного вызова find, одного вызова sh, многочисленных вызовов zcat и многочисленных вызовов agrep.

Наиболее эффективным решением с точки зрения количества вызовов команд является предложение от Пола Томблина:

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

... который стоит один вызов find, один вызов xargs, несколько вызовов zcat и один вызов agrep.

Рольф В. Расмуссен
источник
1
Еще одно преимущество xargs заключается в том, что вы можете еще быстрее ускорить его с помощью современного многоядерного процессора, используя ключ -P (-P 0).
flolo
Да, -P swich действительно хороший способ ускорить выполнение в целом. К сожалению, вы рискуете, если выходные данные параллельных процессов zcat будут перенаправлены в чередование agrep, что повлияет на результат. Этот эффект можно продемонстрировать с помощью: echo -e "1 \ n2" | xargs -P 0 -n 1 да | uniq
Рольф В. Расмуссен
@ Адам, я внес предложенное вами изменение.
Пол Томблин
для которого вы можете установить команду splendid xjobs (изначально из Solaris)
сэх
4
Более простой и более общий ответ на stackoverflow.com/a/21825690/42973 : -exec sh -c "… | … " \;.
Эрик О Лебигот
279

решение простое: выполнить через sh

... -exec sh -c "zcat {} | agrep -dEOE 'grep' " \;
flolo
источник
17
То, что ОП пытался выполнить, можно встретить с предложениями, приведенными выше, но именно это действительно отвечает на заданный вопрос. Есть причины сделать это таким образом - exec намного мощнее, чем просто работа с файлами, возвращаемыми find, особенно в сочетании с test. Например: найти geda-gaf / -type d -exec bash -c 'DIR = {}; [[$ (найти $ DIR -maxdepth 1 | xargs grep -i spice | wc -l) -ge 5]] && echo $ DIR '\; Вернет все каталоги в пути поиска, которые содержат более 5 строк среди всех файлов в этом каталоге, содержащих слово spice
swarfrat
3
Лучший ответ. Grepping весь вывод (как предлагают другие ответы) не то же самое, что grep каждого файла. Совет: вместо sh вы можете использовать любую другую оболочку, какую захотите (я пробовал это с bash, и она работает нормально).
pagliuca
1
Убедитесь, что вы не пропустили эту -cопцию. В противном случае вы получите удивительное No such file or directoryсообщение об ошибке.
Asmaier
вот отличная замена ps, которая использует find с конвейером внутри оболочки exec'd: / usr / bin / find / proc -mindepth 1 -maxdepth 1 -type d -regex '. * / [0-9] +' - print -exec bash -c "cat {} / cmdline | tr '\\ 0' ''; echo" \;
четность3
1
Пример поиска файлов и переименования их с помощью sed с использованием регулярного выражения 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'
Пол Томблин
источник
Надеемся избежать -print и xargs по соображениям эффективности. Возможно, это действительно моя проблема: find не может обрабатывать переданные по каналу команды через -exec
someguy
Это не работает с файлами с пробелами в их именах; исправить, заменить -print на -print0 и добавить опцию -0 в xargs
Адам Розенфилд
2
@ Someguy - Что? Избегать xargs по соображениям эффективности? Вызов одного экземпляра zcat и передача ему списка из нескольких файлов гораздо эффективнее, чем запуск нового экземпляра для каждого найденного файла.
Шерм Пендли
@ Адам - ​​я внес предложенное вами изменение. 99% времени, когда я делаю поиск, он находится в моих каталогах исходного кода, и ни в одном из файлов нет пробелов, поэтому я не беспокоюсь о print0. Теперь мой каталог документов, с другой стороны, я помню print0.
Пол Томблин
10

Вы также можете передать в whileцикл, который может выполнять несколько действий с файлом, который findнаходится. Так что вот один для поиска в jarархивах для данного файла класса Java в папке с большим дистрибутивом jarфайлов

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

Ключевым моментом является то, что whileцикл содержит несколько команд, ссылающихся на переданное имя файла, разделенных точкой с запятой, и эти команды могут содержать каналы. Таким образом, в этом примере я отображаю имя соответствующего файла, затем перечисляю, что находится в архивной фильтрации для данного имени класса. Вывод выглядит так:

/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.M2009090-0800 .jar org / eclipse / core / привязка данных / наблюдаемый / список / 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

в моей оболочке bash (xubuntu10.04 / xfce) это действительно делает сопоставленное имя класса полужирным, поскольку fgrepвыделяет соответствующую строку; это позволяет легко просматривать список сотен jarфайлов, которые были найдены, и легко видеть любые совпадения.

на окнах вы можете сделать то же самое с:

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

обратите внимание, что в Windows разделителем команд является '&' not ';' и что '@' подавляет эхо команды, чтобы выдать аккуратный вывод точно так же, как вывод linux find выше; хотя findstrэто не делает сопоставляемую строку жирным шрифтом, поэтому вам нужно взглянуть немного ближе к выводу, чтобы увидеть соответствующее имя класса. Оказывается, команда windows 'for' знает немало хитростей, таких как циклический просмотр текстовых файлов ...

наслаждаться

simbo1905
источник
2

Я обнаружил, что лучше всего работает команда string shell (sh -c), например:

find -name 'file_*' -follow -type f -exec bash -c "zcat \"{}\" | agrep -dEOE 'grep'" \;
Эндрю Хури
источник
0

Если вы ищете простую альтернативу, это можно сделать с помощью цикла:

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

или, более общая и простая для понимания форма:

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

и замените любой {} на $ i в YOUR_EXEC_COMMAND_AND_PIPES

Луи Ганьон
источник