bash - я могу сделать: найти ... -exec это && это?

23

Есть ли способ логически объединить две команды оболочки, которые вызываются с помощью find - exec ?

Например, чтобы распечатать все файлы .csv, которые содержат строку foo вместе с ее появлением, я бы хотел сделать:

find . -iname \*.csv -exec grep foo {} && echo {} \;

но bash жалуется на «отсутствующий аргумент для« -exec »»

Маркус Юний Брут
источник
2
Вы можете использовать 2 -execв последовательности или использовать один -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
jw013
Это неоднократно сбивало меня с толку: я всегда ожидаю, что первый передаваемый аргумент sh(в данном случае {}) будет $1и $0будет чем-то вроде sh. Но на самом деле, вы правы, первый аргумент проявляется как $0. Наличие в качестве первого аргумента имени вызывающей команды - это просто соглашение, которое в этих случаях не применяется автоматически.
dubiousjim
Возможно, следует объединить с этим вопросом: unix.stackexchange.com/q/18077/4801
dubiousjim

Ответы:

24

-execявляется предикатом, который запускает команду (не оболочку) и оценивает ее как true или false на основе результата команды (нулевое или ненулевое состояние выхода).

Так:

find . -iname '*.csv' -exec grep foo {} \; -print

будет печатать путь к файлу , если grepнаходки Foo в файле. Вместо этого -printвы можете использовать другой -execпредикат или любой другой предикат

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Смотрите также операторы !и -oнайдите операторы отрицания и или .

Кроме того, вы можете запустить оболочку как:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

Или чтобы избежать запуска оболочки для каждого файла:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +
Стефан Шазелас
источник
10

Проблема, с которой вы сталкиваетесь, заключается в том, что оболочка сначала анализирует командную строку и видит две простые команды, разделенные &&оператором:, find . -iname \*.csv -exec grep foo {}и echo {} \;. Цитирование &&(find . -iname \*.csv -exec grep foo {} '&&' echo {} \; ) обходит, но теперь команда выполняется findчто - то вроде grepс аргументами foo, wibble.csv, &&, echoи wibble.csv. Вам нужно дать команду findзапустить оболочку, которая будет интерпретировать &&оператор:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Обратите внимание , что первый аргумент после sh -c SOMECOMMANDэто $0не $1.

Вы можете сохранить время запуска процесса оболочки для каждого файла, сгруппировав вызовы команд с помощью -exec … +. Для простоты обработки передайте какое-нибудь фиктивное значение, $0чтобы "$@"перечислить имена файлов.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Если команда оболочки состоит только из двух программ, разделенных &&, findможет выполнить задание самостоятельно: записать два последовательных -execдействия, а второе будет выполнено только в том случае, если первая выйдет со статусом 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Я предполагаю, что grep и echoпредназначен только для иллюстрации, так как -exec echoможет быть заменен , -printи полученный выход не особенно полезно в любом случае.)

Жиль "ТАК - перестань быть злым"
источник
2
Я стараюсь не использовать для этого «$ 0», так как он также используется оболочкой для отображения сообщений об ошибках. Например, вы можете увидеть сбивающее с толку ./some-file: grep: command not foundсообщение об ошибке. -exec find sh -c '... "$1"' sh {} \;не было бы проблемы. В вашей второй команде поиска есть опечатка (связанная).
Стефан Шазелас
3

В этом конкретном случае я бы сделал:

find . -iname \*.csv -exec grep -l foo \{\} \;

Или если у вас есть подтверждение :

ack -al -G '.*\.csv' foo

Чтобы ответить на ваш актуальный вопрос, что-то вроде этого может работать:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;

Деннис Каарсемакер
источник
3
Эта последняя команда find не только непереносима, но и очень опасна, поскольку пути к файлам в конечном итоге оцениваются как шелл-код.
Стефан Шазелас
Все больше причин, чтобы попытаться избежать этого, правильно используя ack / grep :)
Деннис Каарсемакер,
1
Альтернатива jw013 (приведенная в комментарии к вопросу) в этом отношении более безопасна. (Просто заметил, что ответы Жиля и Стефана используют одну и ту же технику.)
dubiousjim