3

Какой правильный синтаксис для передачи комбинированных команд для find с опцией -exec ?


Пример:

find . -type f -name "*.txt" -exec grep FirstKeyWord {} | grep SecondKeyWord \;

завершается со следующим сообщением об ошибке:

find: grep: missing argument to `-exec`
;: No such file or directory

Сбой такого же типа, когда команды объединяются с использованием && или ; вместо | , Я даже пытался заключить в кавычки объединенные команды с помощью 'или с помощью $() или {} или чего-то подобного, но это (с треском) не удалось.

Примечание: я ищу решение с одной строкой, без использования пользовательских сценариев.

3 ответа3

3

Вы можете использовать сложную команду оболочки в аргументе для exec, явно вызывая там оболочку.

find . -type f -name "*.txt" -exec sh -c 'grep FirstKeyWord "$1" | grep SecondKeyWord' -- {} \;

Это выполняет sh с предоставленным списком команд для каждого файла, передавая имя файла в $1 .

РЕДАКТИРОВАТЬ: ОП указывает, что это работает и проще:

find . -type f -name "*.txt" -exec sh -c 'grep FirstKeyWord {} | grep SecondKeyWord' \;
2

Для вашего конкретного примера вы можете сделать это:

find . -type f -name "*.txt" -exec grep FirstKeyWord {} \; | grep "SecondKeyWord"

Кстати, это также может быть сделано без использования второго grep. Вместо этого используйте egrep с двумя альтернативами FirstKeyWord за которыми следует SecondKeyWord или SecondKeyWord а затем FirstKeyWord:

find . -type f -name "*.txt" -exec egrep "FirstKeyWord.*SecondKeyWord|SecondKeyWord.*FirstKeyWord" {} \;

Если вы можете дать некоторые гарантии относительно результирующих имен путей из find (например, без начальных или внутренних пробелов и без новых строк в путевых именах), вы также можете сделать это следующим образом:

find . -type f -name "*.txt" -print | while read filename; do grep "FirstKeyWord" "${filename}" | grep "SecondKeyWord"; done

Более общий ответ «вы не можете» непосредственно из find . Передача команд или использование && или || это функция оболочки, а не функция выполнения процессов.

Я использовал этот шаблон раньше, но он хрупкий и более сложный:

find . -type f -name "*.txt" -print \
    | sed -e 's;[\\$"`!];\\&;g' \
          -e 's;^.*$;grep "FirstKeyWord" "&" | grep "SecondKeyWord";' \
    | sh

Распечатайте каждое имя файла, используйте sed чтобы преобразовать его в сценарий оболочки на лету (старайтесь избегать символов в имени файла, которые могут вызвать проблемы при заключении в двойные кавычки оболочки), затем пропустите результат через оболочку.

Другое решение - создать сценарий оболочки, который выполняет вашу обработку, и вызвать этот сценарий оболочки из find:

$ cat grepit.sh
#!/bin/sh
grep "FirstKeyWord" "$1" | grep "SecondKeyWord"
$ find . -type f -name "*.txt" -exec sh grepit.sh {} \;
0

Использование "-exec" крайне не рекомендуется, потому что это создает много подпроцесса.

Пожалуйста, подумайте об использовании "find" с "xargs", (используя "-print0" и "-0")

find . -type f -name "*.txt" -print0 | xargs -0 grep FirstKeyWord | grep SecondKeyWord

При этом "grep" порождается один раз для многих файлов (до ограничения количества аргументов, которые может иметь программа).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .