vendita trovare-exec una funzione di shell in Linux?




sdr usb receiver (10)

Trovo il modo più semplice come seguire, ripetendo due comandi in singola do

func_one () {
  echo "first thing with $1"
}

func_two () {
  echo "second thing with $1"
}

find . -type f | while read file; do func_one $file; func_two $file; done

C'è un modo per trovare find l'esecuzione di una funzione che definisco nella shell? Per esempio:

dosomething () {
  echo "doing something with $1"
}
find . -exec dosomething {} \;

Il risultato è:

find: dosomething: No such file or directory

C'è un modo per find es- -exec vedere il dosomething ?


I risultati di elaborazione alla rinfusa

Per aumentare l'efficienza, molte persone usano xargs per elaborare i risultati alla rinfusa, ma è molto pericoloso. Per questo motivo è stato introdotto un metodo alternativo in find che esegue risultati in massa.

Si noti tuttavia che questo metodo potrebbe presentare alcuni avvertimenti come ad esempio un requisito in POSIX: find {} alla fine del comando.

export -f dosomething
find . -exec bash -c 'for f; do dosomething "$f"; done' _ {} +

find passerà molti risultati come argomenti ad una singola chiamata di bash e il for -loop itererà attraverso quegli argomenti, eseguendo la funzione dosomething su ognuno di questi.

La soluzione precedente avvia gli argomenti a $1 , motivo per cui esiste un _ (che rappresenta $0 ).

Elaborazione dei risultati uno per uno

Allo stesso modo, penso che la risposta accettata dovrebbe essere corretta

export -f dosomething
find . -exec bash -c 'dosomething "$1"' _ {} \;

Questo non è solo più sano di mente, perché gli argomenti dovrebbero sempre iniziare da $1 , ma anche usando $0 potrebbe portare a comportamenti imprevisti se il nome del file restituito da find ha un significato speciale per la shell.


La risposta di Jak sopra è grande ma ha un paio di insidie ​​che sono facilmente superabili:

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 dei file con i linefeed funzioneranno. Usa anche il flag -r che disabilita l'escape del backslash, senza che i backslash nei nomi dei file non funzionino. Elimina inoltre IFS modo che i potenziali spazi bianchi finali nei nomi non vengano scartati.


Non è possibile eseguire una funzione in questo modo.

Per superare questo è possibile posizionare la funzione in uno script di shell e chiamarla da find

# dosomething.sh
dosomething () {
  echo "doing something with $1"
}
dosomething $1

Ora usalo in cerca come:

find . -exec dosomething.sh {} \;

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 può -exec quello.


Aggiungi le citazioni in {} come mostrato di seguito:

export -f dosomething
find . -exec bash -c 'dosomething "{}"' \;

Corregge qualsiasi errore dovuto ai caratteri speciali restituiti da find , ad esempio i file con parentesi nel loro nome.


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 chiama se stesso passando ogni risultato di ricerca come argomento. Quando lo script viene eseguito con un argomento, esegue i comandi sull'argomento e quindi esce.


Metti la funzione in un file separato e find di eseguirla.

Le funzioni della shell sono interne alla shell in cui sono definite; find non sarà mai in grado di vederli.


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 precedenti:

toall(){
    find . -type f | while read file; do "$1" "$file"; done
}

Si noti che si rompe con i nomi dei file contenenti spazi (vedi sotto).

Ad esempio, prendi questa funzione:

world(){
    sed -i 's_hello_world_g' "$1"
}

Dire che volevo cambiare tutte le istanze di ciao al mondo in tutti i file nella directory corrente. Farei:

toall world

Per sicurezza con qualsiasi simbolo nei nomi dei file, usare:

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 ).


find . | while read file; do dosomething "$file"; done




bsd