2011-10-13 3 views
5

Come si esegue un'iterazione su tutte le righe emesse da un comando utilizzando zsh, senza impostare IFS?Come si esegue l'iterazione su tutte le righe emesse da un comando in zsh?

Il motivo è che voglio eseguire un comando su ogni file prodotto da un comando e alcuni di questi file contengono spazi.

Ad esempio, dato il file eliminato:

foo/bar baz/gamma 

Cioè, una singola directory 'foo', che contiene una sottodirectory 'bar baz', che contiene un file 'gamma'.

poi eseguire:

git ls-files --deleted | xargs ls 

riporterà in quel file viene trattato come due file: 'foo/bar', e '/ baz/gamma'.

Mi serve per gestirlo come un file: 'foo/bar baz/gamma'.

+0

il sistema ha il comando xargs? – lunixbochs

+0

@lunixboches: Sì, lo fa - tuttavia, non riesce ancora a gestire correttamente le linee. – Arafangion

+2

'git ls-files -z | xargs -0 ls' userà i separatori null (\ 0) invece degli spazi bianchi – lunixbochs

risposta

4

Uso tr e l'opzione di -0xargs, assumendo che le linee non contengono \000 (NUL), che è ragionevole prevedere causa NUL essere uno dei caratteri che non possono essere visualizzati in nomi:

git ls-files --deleted | tr '\n' '\000' | xargs -0 ls 

questo trasforma la linea: foo/bar baz/gamma\n in foo/bar baz/gamma\000 che xargs -0 sa come gestire

+0

Non posso migliorare, grazie! (E funziona anche in bash!) – Arafangion

7

Se si desidera eseguire il comando una volta per tutte le linee:

ls "${(@f)$(git ls-files --deleted)}" 

Il fparameter expansion flag significa dividere l'output del comando su newline. C'è una forma più generale (@s:||:) da suddividere in una stringa arbitraria come ||. Il flag @ significa conservare i record vuoti. Un po 'di confusione, l'intera espansione deve essere racchiusa tra virgolette doppie, per evitare la divisione IFS nell'output dello command substitution, ma produrrà parole separate per ogni record.

Se si desidera eseguire il comando per ogni linea, a sua volta, l'idioma portatile non è particolarmente complicato:

git ls-filed --deleted | while IFS= read -r line; do ls $line; done 

Se si desidera eseguire il comando come poche volte come i permessi di comando limite della lunghezza della linea , utilizzare zargs.

autoload -U zargs 
zargs -- "${(@f)$(git ls-files --deleted)}" -- ls 
+0

Grazie per questo - ci sono molte informazioni utili qui, ma penso che xargs sia ancora più efficiente perché eseguirà un comando per ogni gruppo di file. (Cosa succede se ho così tanti file che non rientrano nella lista degli argomenti?) – Arafangion

+0

@Arafangion 'zargs' è come' xargs' (esegue il minor numero di istanze possibile mentre si inserisce all'interno del limite di lunghezza della riga di comando), solo con un interfaccia ragionevole – Gilles

+0

Ancora meglio, grazie. :) Verificherò – Arafangion