Como posso usar dois comandos bash no comando -exec of find?

32

É possível usar 2 comandos na -execparte do findcomando?

Eu tentei algo como:

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

e eu recebo:

find: argumento ausente para -exec
chmod: não pode acessar {}: Esse arquivo ou diretório
não existe chmod: não pode acessar;: não existe esse arquivo ou diretório

Luc M
fonte

Respostas:

44

Quanto ao findcomando, você também pode adicionar mais -execcomandos em uma linha:

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

Observe que este comando é, em seu resultado, equivalente ao uso de

Arquivo chgrp -v new_group & arquivo & chmod -v 770

em cada arquivo.

Todos os findparâmetros 's tais como -name, -exec, -sizee assim por diante, são realmente testa : findcontinuará a executá-los um por um, desde que toda a cadeia até o momento avaliou a verdade . Portanto, cada -execcomando consecutivo é executado apenas se os anteriores retornarem true (ou seja, 0status de saída dos comandos). Mas findtambém entende operadores lógicos como or ( -o) e não ( !). Portanto, para usar uma cadeia de -exectestes independentemente dos resultados anteriores, seria necessário usar algo como isto:

find . -name "*" \( -exec chgrp -v new_group {} \; -o -exec chmod -v 770 {} \; \)
rozcietrzewiacz
fonte
4
+1: Sim, essa é a maneira mais elegante de fazer isso. Se você pode explicar por que usa '{}'(apóstrofos ao redor do aparelho), visite: unix.stackexchange.com/q/8647/4485
usuário desconhecido
1
@user Infelizmente, não sei se ainda é necessário. Eu fiz alguns testes agora e não me deparei com uma situação em que isso mudaria alguma coisa. Eu acho que são apenas "boas práticas" que desaparecem.
rozcietrzewiacz
2
As aspas são importantes para arquivos com espaços em seus nomes.
precisa saber é o seguinte
17
find . -name "*" -exec sh -c 'chgrp -v new_group "$0" ; chmod -v 770 "$0"' {} \;
Glenn Jackman
fonte
@Gilles: As maravilhas do -cmanuseio estranho de US $ 0 me fazem pensar que isso está errado toda vez que olho para ele, mas é definitivamente correto.
Derobert 24/08/12
I como o shell explícita a ser definida ...
djangofan
Esta resposta (e a resposta de Giles) parece ser a melhor resposta para a pergunta dada a sh -c.
14

Seu comando é primeiro analisado pelo shell em dois comandos separados por a ;, o que é equivalente a uma nova linha:

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

Se você deseja executar um comando do shell, chame um shell explicitamente com bash -c(ou sh -cse você não se importa que o shell seja especificamente do bash):

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

Observe o uso de {}como um argumento para o shell; é o argumento zeroth (que normalmente é o nome do shell ou script, mas isso não importa aqui), portanto referido como "$0".

Você pode passar vários nomes de arquivos para o shell de cada vez e fazer com que o shell itere através deles, será mais rápido. Aqui passo _como o nome do script e os argumentos a seguir são nomes de arquivos, que for x(um atalho para for x in "$@") iteram.

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

Observe que desde o bash 4, ou no zsh, você não precisa encontrar nada aqui. No bash, execute shopt -s globstar(coloque-o no seu ~/.bashrc) para ativar a **/posição de um diretório recursivo glob. (No zsh, isso fica ativo o tempo todo.)

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

ou se você deseja que os arquivos sejam iterados em ordem

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

Uma diferença com o findcomando é que o shell ignora arquivos de ponto (arquivos cujo nome começa com a .). Para incluí-los, no bash, primeiro conjunto GLOBIGNORE=.:..; no zsh, use **/*(D)como padrão glob.

Gilles 'SO- parar de ser mau'
fonte
Esta resposta (e a resposta de Glenn) parece ser a melhor resposta para a pergunta dada sh -c.