Come posso usare due comandi bash in -exec di comando find?


32

È possibile utilizzare 2 comandi nella -execparte di findcomando?

Ho provato qualcosa del tipo:

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

e ottengo:

find: argomento mancante per -exec
chmod: impossibile accedere {}: nessun file o directory di questo tipo
chmod: impossibile accedere ;: nessun file o directory di questo tipo

Risposte:


44

Per quanto riguarda il findcomando, puoi anche solo aggiungere più -execcomandi in una riga:

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

Si noti che questo comando è, nel suo risultato, equivalente all'utilizzo

chgrp -v file new_group && chmod -v file 770

su ogni file.

Tutti i find's parametri quali -name, -exec, -sizee così via, sono in realtà prove : findcontinuerà ad eseguire uno per uno fino a quando l'intera catena finora ha valutato a vero . Quindi ogni -execcomando consecutivo viene eseguito solo se i precedenti restituiscono true (ovvero lo 0stato di uscita dei comandi). Ma findcomprende anche operatori logici come o ( -o) e non ( !). Pertanto, per utilizzare una catena di -exectest indipendentemente dai risultati precedenti, è necessario utilizzare qualcosa del genere:

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

4
+1: Sì, è il modo più elegante per farlo. Se puoi spiegare perché usi '{}'(apostrofi attorno alle parentesi graffe), visita: unix.stackexchange.com/q/8647/4485
utente sconosciuto

1
@utente Purtroppo, non so se sia ancora necessario. Ho fatto alcuni test proprio ora e non ho riscontrato una situazione in cui cambierebbe nulla. Immagino che solo le "buone pratiche" si estingueranno.
rozcietrzewiacz,

2
Le virgolette sono importanti per i file con spazi nei loro nomi.
naught101

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

@Gilles: Le meraviglie della -cstrana gestione di $ 0 mi fanno pensare che sia sbagliato ogni volta che lo guardo, ma è sicuramente corretto.
derobert,

Mi piace definire la shell esplicita ...
Djangofan,

Questa risposta (e la risposta di Giles) sembra la risposta migliore alla domanda data sh -c.

14

Il tuo comando viene prima analizzato dalla shell in due comandi separati da a ;, che equivale a una nuova riga:

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

Se vuoi eseguire un comando shell, invoca esplicitamente una shell con bash -c(o sh -cse non ti interessa che la shell sia specificamente bash):

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

Si noti l'uso di {}come argomento per la shell; è l'argomento zeroth (che normalmente è il nome della shell o dello script, ma qui non importa), quindi indicato come "$0".

Puoi passare più nomi di file alla shell alla volta e far scorrere la shell attraverso di essi, sarà più veloce. Qui passo _come il nome dello script e i seguenti argomenti sono nomi di file, che for x(una scorciatoia per for x in "$@") scorre.

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

Nota che da bash 4, o in zsh, non è necessario trovarlo affatto qui. In bash, esegui shopt -s globstar(mettilo nel tuo ~/.bashrc) per attivare **/standing per un glob ricorsivo di directory. (In zsh, questo è sempre attivo.) Quindi

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

o se si desidera che i file vengano ripetuti in ordine

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

Una differenza con il findcomando è che la shell ignora i file di punti (file il cui nome inizia con a .). Per includerli, in bash, primo set GLOBIGNORE=.:..; in zsh, usa **/*(D)come modello glob.


Questa risposta (e la risposta di Glenn) sembra la risposta migliore alla domanda data sh -c.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.