Risposte:
Poiché solo la shell sa come eseguire le funzioni della shell, è necessario eseguire una shell per eseguire una funzione. È inoltre necessario contrassegnare la funzione per l'esportazione con export -f
, altrimenti la subshell non le erediterà:
export -f dosomething
find . -exec bash -c 'dosomething "$0"' {} \;
find . -exec bash -c 'dosomething "$0"' {} \;
, gestirà gli spazi (e altri strani personaggi) nei nomi dei file ...
export -f
funziona solo in alcune versioni di bash. Non è posix, non crossplatforn, /bin/sh
avrà un errore con esso
find . | while read file; do dosomething "$file"; done
while read
per un ciclo for; for item in $(find . ); do some_function "${item}"; done
La risposta di Jak sopra è ottima ma ha un paio di insidie che possono essere facilmente superate:
find . -print0 | while IFS= read -r -d '' file; do dosomething "$file"; done
Questo utilizza null come delimitatore invece di un avanzamento riga, quindi i nomi di file con avanzamenti riga funzioneranno. Utilizza anche il -r
flag che disabilita la escape della barra rovesciata, senza le barre rovesciate nei nomi dei file non funzionerà. Si cancella anche in IFS
modo che i potenziali spazi bianchi finali nei nomi non vengano scartati.
/bin/bash
ma non funzionerà /bin/sh
. Che peccato.
Aggiungi le virgolette {}
come mostrato di seguito:
export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;
Ciò corregge qualsiasi errore dovuto a caratteri speciali restituiti da find
, ad esempio file con parentesi nel loro nome.
{}
. Questo si interromperà per un nome file contenente virgolette doppie. touch '"; rm -rf .; echo "I deleted all you files, haha
. Ops.
-exec bash -c 'echo $0' '{}' \;
Nota che quando si usa bash -c
, $ 0 è il primo argomento, non il nome dello script.
Per una maggiore efficienza, molte persone usano xargs
per elaborare i risultati in blocco, ma è molto pericoloso. Per questo motivo è stato introdotto un metodo alternativo find
che esegue risultati in blocco.
Si noti tuttavia che questo metodo potrebbe presentare alcuni avvertimenti come ad esempio un requisito in POSIX- find
da avere {}
alla fine del comando.
export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +
find
passerà molti risultati come argomenti a una singola chiamata di bash
e il for
-loop scorre attraverso quegli argomenti, eseguendo la funzione dosomething
su ognuno di questi.
La soluzione sopra inizia argomenti a $1
, motivo per cui c'è un _
(che rappresenta $0
).
Allo stesso modo, penso che la risposta migliore accettata debba essere corretta
export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;
Questo non è solo più sano, perché gli argomenti dovrebbero sempre iniziare da $1
, ma anche l'uso $0
potrebbe portare a comportamenti imprevisti se il nome file restituito find
ha un significato speciale per la shell.
Chiedi allo script di chiamarsi, passando ogni elemento trovato come argomento:
#!/bin/bash
if [ ! $1 == "" ] ; then
echo "doing something with $1"
exit 0
fi
find . -exec $0 {} \;
exit 0
Quando esegui lo script da solo, trova ciò che stai cercando e si chiama passando ogni risultato di ricerca come argomento. Quando lo script viene eseguito con un argomento, esegue i comandi sull'argomento e quindi esce.
find: ‘myscript.sh’: No such file or directory
se iniziato come bash myscript.sh
...
Per quelli di voi che cercano una funzione bash che eseguirà un determinato comando su tutti i file nella directory corrente, ne ho compilato uno dalle risposte sopra:
toall(){
find . -type f | while read file; do "$1" "$file"; done
}
Si noti che si interrompe con i nomi di file contenenti spazi (vedere di seguito).
Ad esempio, prendi questa funzione:
world(){
sed -i 's_hello_world_g' "$1"
}
Supponiamo di voler cambiare tutte le istanze di Hello in World in tutti i file nella directory corrente. Farei:
toall world
Per sicurezza con qualsiasi simbolo nei nomi dei file, utilizzare:
toall(){
find . -type f -print0 | while IFS= read -r -d '' file; do "$1" "$file"; done
}
(ma hai bisogno di un find
che gestisca -print0
ad esempio GNU find
).
Per riferimento, evito questo scenario usando:
for i in $(find $dir -type f -name "$name" -exec ls {} \;); do
_script_function_call $i;
done;
Ottieni l'output di find nel file di script corrente e scorre l'output come desideri. Sono d'accordo con la risposta accettata, ma non voglio esporre la funzione al di fuori del mio file di script.
Non è possibile eseguire una funzione in questo modo.
Per ovviare a questo, puoi inserire la tua funzione in uno script di shell e chiamarla da find
# dosomething.sh
dosomething () {
echo "doing something with $1"
}
dosomething $1
Ora usalo in trova come:
find . -exec dosomething.sh {} \;
dosomething $1
=> dosomething "$1"
e avviare correttamente il file confind . -exec bash dosomething.sh {} \;
Per fornire aggiunte e chiarimenti ad alcune delle altre risposte, se si utilizza l'opzione bulk per exec
o execdir
( -exec command {} +
) e si desidera recuperare tutti gli argomenti posizionali, è necessario considerare la gestione di $0
with bash -c
. Più concretamente, considera il comando seguente, che usa bash -c
come suggerito sopra, e fa semplicemente eco ai percorsi dei file che terminano con '.wav' da ogni directory che trova:
find "$1" -name '*.wav' -execdir bash -c 'echo $@' _ {} +
Il manuale di bash dice:
If the -c option is present, then commands are read from the first non-option argument command_string. If there are arguments after the command_string, they are assigned to the
positional parameters, starting with $0.
Qui 'check $@'
è la stringa di comando e _ {}
sono gli argomenti dopo la stringa di comando. Si noti che $@
è un parametro posizionale speciale in bash che si espande a tutti i parametri posizionali a partire da 1 . Si noti inoltre che con l' -c
opzione, il primo argomento viene assegnato al parametro posizionale $0
. Ciò significa che se si tenta di accedere a tutti i parametri posizionali con $@
, si otterranno solo parametri a partire da $1
e verso l'alto. Questo è il motivo per cui la risposta di Dominik ha il _
, che è un argomento fittizio per riempire il parametro $0
, quindi tutti gli argomenti che vogliamo sono disponibili in seguito se usiamo l' $@
espansione dei parametri per esempio, o il ciclo for come in quella risposta.
Naturalmente, simile alla risposta accettata, bash -c 'shell_function $0 $@'
funzionerebbe anche passando esplicitamente $0
, ma ancora una volta, dovresti tenere a mente che $@
non funzionerà come previsto.
Non direttamente, no. Trova è in esecuzione in un processo separato, non nella shell.
Crea uno script di shell che fa lo stesso lavoro della tua funzione e trova -exec
che può farlo.
$0
.