Bash Esecuzione di comandi su un file trovato


Esempio

A volte dovremo eseguire comandi su molti file. Questo può essere fatto usando xargs .

find . -type d -print | xargs -r chmod 770

Il comando sopra troverà in modo ricorsivo tutte le directory ( -type d ) relative a . (che è la directory di lavoro corrente) ed esegui chmod 770 su di essi. L'opzione -r specifica a xargs di non eseguire chmod se find non ha trovato alcun file.

Se i nomi o le directory dei file contengono caratteri di spazio, questo comando potrebbe soffocare; una soluzione è usare il seguente

find . -type d -print0 | xargs -r -0 chmod 770

Nell'esempio sopra, i -print0 e -0 specificano che i nomi dei file saranno separati usando un byte null e consente l'uso di caratteri speciali, come spazi, nei nomi dei file. Questa è un'estensione GNU e potrebbe non funzionare in altre versioni di find e xargs .


Il modo migliore per farlo è saltare il comando xargs e lasciare che find invochi il sottoprocesso stesso:

find . -type d -exec chmod 770 {} \;

Qui, il {} è un segnaposto che indica che si desidera utilizzare il nome del file in quel punto. find eseguirà chmod su ogni file individualmente.

In alternativa puoi passare tutti i nomi di file a una singola chiamata di chmod , usando

find . -type d -exec chmod 770 {} +

Questo è anche il comportamento dei frammenti di xargs sopra. (Per chiamare singolarmente ciascun file, è possibile utilizzare xargs -n1 ).


Una terza opzione è lasciare che il loop di bash sopra l'elenco di nomi di file find output:

find . -type d | while read -r d; do chmod 770 "$d"; done

Questo è sintatticamente il più goffo, ma comodo quando si vogliono eseguire più comandi su ciascun file trovato. Tuttavia, questo non è sicuro rispetto ai nomi di file con nomi strani.

find . -type f | while read -r d; do mv "$d" "${d// /_}"; done

che sostituirà tutti gli spazi nei nomi dei file con caratteri di sottolineatura. (Anche questo esempio non funzionerà se ci sono spazi nei principali nomi di directory .)

Il problema con quanto sopra è che while read -r aspetta una entry per riga, ma i nomi dei file possono contenere newline (e anche, read -r perderà ogni spazio bianco finale). Puoi sistemarlo ruotando le cose:

find . -type d -exec bash -c 'for f; do mv "$f" "${f// /_}"; done' _ {} +

In questo modo, -exec riceve i nomi dei file in un formato completamente corretto e portatile; il bash -c li riceve come un numero di argomenti, che saranno trovati in $@ , quotati correttamente ecc. (Lo script dovrà gestire questi nomi correttamente, ovviamente, ogni variabile che contiene un nome di file deve essere in doppio citazioni.)

Il misterioso _ è necessario perché il primo argomento di bash -c 'script' è usato per popolare $0 .