`find` с несколькими` -name` и `-exec` выполняет только последние совпадения` -name`

74

Когда я использую

find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt"

он находит все типы файлов. Но когда я добавлю -execв конце:

find . -type f -name "*.htm*" -o -name "*.js*" -o -name "*.txt" -exec sh -c 'echo "$0"' {} \;

кажется, он печатает только .txtфайлы. Что я делаю неправильно?

Примечание: используя MINGW (Git Bash)

jakub.g
источник
Подсказка: первая команда также напечатает каталоги , имена которых совпадают *.js*или *.txt.
Wildcard

Ответы:

99
находить . -тип f -name "* .htm *" -o -name "* .js *" -o -name "* .txt"

это сокращение от:

находить . \ (\ ( -type f -a -name "* .htm *" \) -o \
          \ ( -name "* .js *" \) -o \
          \ ( -name "* .txt" \) \
       \) -a -принт

То есть, поскольку предикат действия не указан (только условия ), -printдействие неявно добавляется для файлов, соответствующих условиям.

(и, кстати, что бы печатать нестандартные .jsфайлы (это -type fкасается только .htmфайлов)).

Пока:

находить . -тип f -name "* .htm *" -o -name "* .js *" -o -name "* .txt" \
  -exec sh -c 'echo "$ 0"' {} \;

это сокращение от:

находить . \ ( -типа f -a -name "* .htm *" \) -o \
       \ ( -name "* .js *" \) -o \
       \ ( -name "* .txt" -a -exec sh -c 'echo "$ 0"' {} \; \)

Поскольку find(как и во многих языках), AND ( -a; неявное, если не указано ) имеет приоритет над OR ( -o), а добавление явного предиката действия (здесь -exec) отменяет -printнеявное действие, замеченное выше. Здесь вы хотите:

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) \
  -exec sh -c 'echo "$0"' {} \;

Или же:

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) -exec sh -c '
   for i do
     echo "$i"
   done' sh {} +

Чтобы избежать запуска по одному shна файл.

Стефан Шазелас
источник
В некоторых из этих случаев использования sh -c вам необходимо добавить нулевой аргумент для sh (хотя в других вы уже включили его).
Джеймс Янгман
1
Это отличный ответ!
Маринос Ан
30

Это подразумеваемые скобки. Добавьте явные скобки.\( \)

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) -exec sh -c 'echo "$0"' {} \;

или используя xargs (мне нравится xargs, мне проще, но, видимо, не так портативно).

find . -type f \( -name "*.htm*" -o -name "*.js*" -o -name "*.txt" \) -print0 | xargs -0 -n1 echo
Ctrl-Alt-Делор
источник