Как я могу использовать две команды bash в -exec команды find?

32

Можно ли использовать 2 команды в -execсоставе findкоманды?

Я пробовал что-то вроде:

find . -name "*" -exec  chgrp -v new_group {}  ; chmod -v 770 {}  \;

и я получаю:

find: отсутствует аргумент для -exec
chmod: нет доступа {}: нет такого файла или каталога,
chmod: нет доступа;: нет такого файла или каталога

Люк М
источник

Ответы:

44

Что касается findкоманды, вы также можете просто добавить больше -execкоманд подряд:

find . -name "*" -exec chgrp -v new_group '{}' \; -exec chmod -v 770 '{}' \;

Обратите внимание, что эта команда по своему результату эквивалентна использованию

chgrp -v файл новой группы && chmod -v 770 file

на каждом файле.

Все findпараметры «s , такие как -name, -exec, -sizeи так далее, на самом деле являются тесты : findбудет продолжать работать их один за другим, пока вся цепь до сих пор оценивается в верно . Таким образом, каждая последующая -execкоманда выполняется только в том случае, если предыдущие вернули истину (т. 0Е. Состояние выхода команд). Но findтакже понимает логические операторы, такие как или ( -o) и не ( !). Поэтому, чтобы использовать цепочку -execтестов независимо от предыдущих результатов, нужно использовать что-то вроде этого:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
rozcietrzewiacz
источник
4
+1: да, это самый элегантный способ сделать это. Если вы можете объяснить, почему вы используете '{}'(апострофы вокруг скобок), пожалуйста, посетите: unix.stackexchange.com/q/8647/4485
пользователь неизвестен
1
@user К сожалению, я не знаю, если это все еще необходимо. Я только что провела какое-то тестирование и не сталкивалась с ситуацией, когда это что-то изменит Я думаю, что это просто "хорошая практика", которая вымрет.
rozcietrzewiacz
2
Кавычки важны для файлов с пробелами в их именах.
naught101
17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
Гленн Джекман
источник
@Gilles: чудеса -cстранной обработки $ 0 заставляют меня думать, что это неправильно, каждый раз, когда я смотрю на это, но это определенно правильно.
Дероберт
Мне нравится определенная явная оболочка ...
djangofan
Этот ответ (и ответ Джайлза) кажется лучшим ответом на заданный вопрос sh -c.
14

Ваша команда сначала анализируется оболочкой на две команды, разделенные ;символом a , что эквивалентно переводу строки:

find . -name "*" -exec chgrp -v new_group {}
chmod -v 770 {} \;

Если вы хотите запустить команду оболочки, вызовите оболочку явно с помощью bash -c(или sh -cесли вам не важно, что оболочка специально bash):

find . -name "*" -exec sh -c 'chgrp -v new_group "$0"; chmod -v 770 "$0"' {} \;

Обратите внимание на использование {}в качестве аргумента оболочки; это нулевой аргумент (обычно это имя оболочки или скрипта, но здесь это не имеет значения), поэтому на него ссылаются как "$0".

Вы можете передавать несколько имен файлов в оболочку за раз и заставлять оболочку проходить через них, это будет быстрее. Здесь я _передаю имя сценария, а следующие аргументы являются именами файлов, которые for x(ярлык для for x in "$@") повторяется.

find . -name "*" -exec sh -c 'for x; do chgrp -v new_group "$x"; chmod -v 770 "$x"; done' _ {} +

Обратите внимание, что начиная с bash 4 или zsh, вам вообще не нужно искать здесь. В bash запустите shopt -s globstar(поместите в свой ~/.bashrc), чтобы активировать **/стоящий за рекурсивный глобус каталога. (В Zsh это активно все время.) Тогда

chgrp -v new_group -- **/*; chmod -v 770 -- **/*

или если вы хотите, чтобы файлы были перебраны в порядке

for x in **/*; do
  chgrp -v new_group -- "$x"
  chmod -v 770 -- "$x"
done

Одно из отличий findкоманды заключается в том, что оболочка игнорирует точечные файлы (файлы, имя которых начинается с a .). Чтобы включить их, в bash, первый набор GLOBIGNORE=.:..; в zsh используйте **/*(D)в качестве шаблона glob.

Жиль "ТАК - перестань быть злым"
источник
Этот ответ (и ответ Гленна) кажется лучшим ответом на заданный вопрос sh -c.