come passare il risultato di `find` come un elenco di file?


11

La situazione è che ho un lettore MP3 mpg321che accetta un elenco di file come argomento. Conservo la mia musica in una directory chiamata "music", in cui ci sono alcune directory in più. Voglio solo giocarci tutti, quindi eseguo il programma con

mpg321 $(find /music -iname "*\.mp3")

. Il problema è che alcuni nomi di file contengono spazi bianchi e il programma li suddivide in parti più piccole e si lamenta dei file mancanti. Avvolgere il risultato findtra virgolette

mpg321 "$(find /music -iname "*\.mp3")"

non aiuta perché tutto diventerà un unico "nome file", che ovviamente non si trova.

Come posso farlo allora? Se è importante, lo sto usando bash, ma passerò zshpresto a.

Risposte:


17

Prova a utilizzare find -print0o l' -printfopzione in combinazione con in xargsquesto modo:

find /music -iname "*\.mp3" -print0 | xargs -0 mpg321

Come funziona è spiegato dalla pagina del manuale di find :

print0

Vero; stampa il nome file completo sull'output standard, seguito da un carattere null (invece del carattere newline che -print usa). Ciò consente ai nomi di file che contengono newline o altri tipi di spazi bianchi di essere interpretati correttamente dai programmi che elaborano l'output find. Questa opzione corrisponde all'opzione -0 di xargs.


Ci scusiamo per tutte le modifiche. Mi sembra di ricordare che il modello -print0 xarg -0 non è riuscito su altri tipi di spazi bianchi, ma non sono riuscito a trovare un esempio.
Steven D

oh sì che funziona! ma mi chiedo ancora perché mpg321 $(find /music -iname "*\.mp3" -print0)no?
phunehehe,

1
Bene, credo che non funzioni per due motivi: (1) i nomi dei file sono separati da caratteri null, il che non è affatto quello che mpg321 si aspetta e (2) xargs sta facendo il lavoro di fuga dall'altro spazio bianco, non trova.
Steven D

2
@phunehehe, @Steven: mpg321non c'entra niente, è la shell che suddivide l'output findin argomenti separati. E -print0 | xargs -0funzionerà con ogni possibile nome di file.
Gilles 'SO- smetti di essere malvagio'

8
find /music -iname "*\.mp3" -exec mpg123 {} +

Con GNU find puoi anche usare -print0exargs -0 , ma c'è poco senso nell'apprendere un altro strumento. La -exec ... {} +sintassi viene poco menzionata perché Linux l'ha acquisita più tardi -print0, ma non c'è motivo di non usarla ora.

Con zsh o bash 4, questo è molto più semplice:

mpg123 **/*.[Mm][Pp]3

Solo in zsh, puoi rendere insensibile al maiuscolo / minuscolo di un modello (parte di a):

mpg123 (#i)**/*.mp3

+1 a questo, "find -print0" è un brutto hack.
p-static,

2

Penso che la soluzione di Steven sia la migliore, ma un altro modo è usare il -Iflag di xargs , che consente di specificare una stringa che verrà quindi sostituita nel comando con l'argomento (invece di aggiungere semplicemente l'argomento alla fine del comando). Puoi usarlo per citare l'argomento:

find /music -iname "*\.mp3" | xargs -0 -Ifoo mpg321 "foo"

Non capisco questa risposta ... Stai usando la -0bandiera di xargs senza trovare -print0. Inoltre, la -Ibandiera di xargs ha una serie di conseguenze. A differenza di un semplice xargsche comprimerà tutte le righe dallo stdin in un comando, xargs -Iverrà eseguito mpg321potenzialmente centinaia o migliaia di volte (una volta per ogni file) che non è certamente l'intento. Inoltre, tieni presente che la citazione di foo non è necessaria come lo xargsfa internamente. Nota se comprimi tutti i nomi di file sulla linea e li invii a xargs -I, non si apriranno.
Sei

1

Di solito è meglio direttamente, -exec ${tgt_process} \{\} +ma se hai bisogno di ottenere un elenco affidabile di delimitati nomi di file in un file o flusso da findqualsiasi motivo, allora puoi farlo:

find -exec sh -c 'printf "///%s///\n" "$@"' -- \{\} +

Ciò che ne ricava sono due stringhe uniche . Alla fine di ogni nome di file c'è la stringa \n///e alla fine di ogni nome di file c'è la stringa ///\n. Queste due stringhe non si verificano in nessun'altra parte finddell'output tranne che in quelle posizioni indipendentemente dai caratteri che contengono i nomi dei file.

Inoltre, l'utilizzo sopra è portatile POSIX di base e può essere utilizzato per funzionare su quasi tutti i sistemi unix. Ciò non è vero per l'uso di un delimitatore di byte null - nonostante la sua praticità - raccomandato da altri.

Ma, di nuovo, questo è necessario solo se non puoi direttamente il -exectuo $tgt_processper qualsiasi motivo, poiché quello dovrebbe essere il tuo obiettivo. Per prima cosa, il metodo sopra richiede ancora l'analisi. Ad esempio, se si desidera citare ogni shell di nome file, è necessario innanzitutto assicurarsi che le virgolette fisse nel nome file siano state salvate:

find ... + | sed 's|'\''|&"&"&|g;s|///|'\''|g'

Ciò genera una matrice di nomi di file con escape nella shell, indipendentemente da qualunque sia il loro carattere costituente. Ora devi solo sperare che la tua applicazione sul lato ricevente non la rovini.


0

Un altro modo per farlo è quello di sfuggire a tutti i caratteri speciali presenti nei nomi dei file. Per esempio:

find /music -iname "*\.mp3" | sed 's!\([] \*\$\/&[]\)!\\\1!g' | xargs mpg321

Questo sostanzialmente passerà i nomi di file con escape corretto a xargs per l'esecuzione e non ci saranno problemi.


1
Ciò si interrompe ancora sulle nuove righe nei nomi dei file. E potresti anche solo sed 's|.|\\&|g'- questo è ciò che POSIX raccomanda comunque.
Mikeserv,
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.