2013-02-22 6 views
11

Ho uno script che scrive solo i dati su stdout. Ho bisogno di eseguirlo per più file e generare un file di output diverso per ogni file di input e mi chiedevo come usare find -exec per quello. Così ho praticamente provato diverse varianti di questo (ho sostituito lo script cat solo per scopi di testabilità):Reindirizzamento dello stdout con find -exec e senza creare una nuova shell

trovare * -type f cat exec "{}"> "{} .stdout" \;

ma non è riuscito a farlo funzionare poiché tutti i dati venivano scritti in un file chiamato letteralmente {}.stdout.

Alla fine, ho potuto farlo funzionare con:

ritrovamento * -type f exec sh -c "cat {}> {} .stdout" \;

Ma mentre questo ultimo modulo funziona bene con cat, il mio script richiede variabili d'ambiente caricate attraverso diversi script di inizializzazione, quindi io alla fine con:

ritrovamento * -type f exec sh -c " initscript1; initscript2; ...; myscript {}> {} .stdout "\;

Quale sembra uno spreco perché ho già tutto inizializzato nella mia shell corrente.

C'è un modo migliore per farlo con find? Altre one-liner sono benvenute.

+2

Se sono inizializzati nella shell originale, ma non sono impostati nella subshell, non sono variabili di ambiente. Scrivi 'set -a' nella parte superiore degli initscript. –

+0

È l'ultimo esempio che fornisci corretto, oppure è il comando: 'find. -tipo f -exec sh -c ". initscript1;. initscript2; ...; myscript {}> {} .stdout" \; '(Invece di invocare semplicemente' initscript1', si sta effettivamente chiamando '. Initscript1', cioè si sta acquisendo il file con il comando punto). –

risposta

5

Una soluzione semplice sarebbe quella di mettere un wrapper script:

#!/bin/sh 

myscript "$1" > "$1.stdout" 

Chiamatela myscript2 e invocarla con find:

find . -type f -exec myscript2 {} \; 

Nota che, anche se la maggior parte delle implementazioni di trovarti consentono di fai ciò che hai fatto, tecnicamente il comportamento di find non è specificato se si utilizza {} più di una volta nell'elenco degli argomenti di -exec.

+2

Ma nel manuale di 'find', da qualche parte in' -exec' si dice che: _La stringa '{}' è sostituita dal nome del file corrente che viene elaborato ovunque si verifichi negli argomenti del comando, non solo negli argomenti in cui esso è solo, come in alcune versioni di find._ [link] (http://unixhelp.ed.ac.uk/CGI/man-cgi?find). Comunque, grazie per la soluzione. – jserras

+3

Il manuale per la tua implementazione particolare di 'find' afferma che funziona, ma lo standard dice:' Se è presente più di un argomento contenente solo i due caratteri "{}", il comportamento non è specificato. Non è un grosso problema , ma è qualcosa che può bruciarti (a quel punto diventa improvvisamente un grosso problema!) –

+3

Uno svantaggio più importante è che cose come '-exec sh -c" myscript {}> {} .stdout "\;' può causare esecuzione di codice arbitrario di fronte a nomi di file ostili. È più sicuro fare '-exec sh -c 'myscript" $ 1 ">" $ 1.stdout "' sh {} \;'. – jilles

2

Puoi farlo con eval. Può essere brutto, ma così è dover fare uno script di shell per questo. Inoltre, è tutto su un'unica riga. Per esempio

find -type f -exec bash -c "eval md5sum {} > {}.sum " \; 
+0

Il 'bash -c' è il manzo qui, l'eval non sta facendo nulla di utile. Ma non stai evitando la shell. – tripleee

+0

Se estrai l'eval, penso che questa dovrebbe essere la risposta accettata in realtà, anche se l'OP sarebbe utile per evitare una shell. (Mettere uno script in un file separato crea una shell quando si esegue lo script in ogni caso. Ciò che l'OP sta chiedendo non è realmente possibile.) – tripleee

+0

Qui l'eval è attivamente pericoloso. Se hai un nome di file che contiene '$ (rm -rf $ HOME)', questo sarà ** molto ** brutta notizia. –

2

Se esportazione le variabili d'ambiente, faranno essere già presente nel guscio bambino (Se si utilizza invece di bash -csh -c, e la shell genitore è di per sé bash, allora si può anche esportazione funzioni nella shell padre e averli utilizzabili nel bambino; ​​vedere export -f).

Inoltre, utilizzando -exec ... {} +, è possibile limitare il numero di conchiglie per il minor numero possibile necessarie per superare tutti gli argomenti sulla linea di comando:

set -a # turn on automatic export of all variables 
source initscript1 
source initscript2 

# pass as many filenames as possible to each sh -c, iterating over them directly 
find * -name '*.stdout' -prune -o -type f \ 
    -exec sh -c 'for arg; do myscript "$arg" > "${arg}.stdout"' _ {} + 

In alternativa, è possibile solo eseguire l'esecuzione in vostro attuale shell direttamente:

while IFS= read -r -d '' filename; do 
    myscript "$filename" >"${filename}.out" 
done < <(find * -name '*.stdout' -prune -o -type f -print0) 

Vedi UsingFind discutere in modo sicuro e corretto svolgimento azioni collettive attraverso find; e BashFAQ #24 discutendo l'uso della sostituzione di processo (la sintassi <(...)) per garantire che le operazioni vengano eseguite nella shell principale.

+0

L'uso di '_' come $ 0 per l'invocato sh è un po 'offuscante! –

+0

@WilliamPursell, è un idioma comune: puoi trovare i link se lo desideri. ('_' è anche un valore inutilizzato/segnaposto convenzionale in alcune altre lingue, come Python, ma la mia comprensione è che era comune in guscio prima). –

+0

L'ho visto usato in go e perl, ma mai in questa impostazione. Tendo ad ignorarlo e impostare $ 0 su {}, che è probabilmente una pratica molto peggiore! –

Problemi correlati