2012-12-18 12 views
89

Per esempio, in questo momento sto utilizzando la seguente per cambiare un paio di file i cui percorsi Unix ho scritto in un file:Come si esegue un comando per ogni riga di un file?

cat file.txt | while read in; do chmod 755 "$in"; done 

Esiste un modo più elegante, più sicuro?

risposta

68

Se il file non è troppo grande e tutti i file sono ben nominato (senza spazi o altri caratteri speciali come virgolette), si potrebbe semplicemente:

chmod 755 $(<file.txt) 

Se si dispone di caratteri speciali e/o molte linee in file.txt.

xargs -0 chmod 755 < <(tr \\n \\0 <file.txt) 

se il comando deve essere eseguito esattamente 1 ora da ingresso:

xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt) 

Questo non è necessario per questo campione, come chmod accetta più file come argomento, ma questa partita il titolo di domanda .

Per qualche caso particolare, si potrebbe anche definire la posizione del file come argomento in comandi generateds da xargs:

xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt) 
+3

Come 'xargs' è stato creato inizialmente per rispondere a questo tipo di necessità, alcune funzionalità, come * building command il più a lungo possibile nell'ambiente corrente * per richiamare 'chmod' in questo caso il meno possibile, riducendo * i fork * assicurano l'efficienza. 'while; do..done <$ file' implie esegue 1 fork per 1 file. 'xargs' potrebbe eseguire 1 fork per migliaia di file ... in maniera affidabile. –

+1

perché il terzo comando non funziona in un makefile? sto ricevendo "errore di sintassi vicino a un token imprevisto" <"", ma l'esecuzione direttamente dalla riga di comando funziona. –

+2

Questo sembra collegato alla sintassi specifica del Makefile. Si potrebbe provare a invertire la riga di comando: 'cat file.txt | tr \\ n \\ 0 | xargs -0 -n1 chmod 755' –

88

Sì.

while read in; do chmod 755 "$in"; done < file.txt 

In questo modo si può evitare un processo cat.

cat è quasi sempre negativo per uno scopo come questo. Si può leggere di più su Useless Use of Cat.

+0

Evitare * un * 'cat' è una buona idea, ma in questo caso ** il ** comando indicato è' xargs' –

+0

Questo link non sembra essere rilevante, forse il contenuto della pagina web è cambiato? Il resto della risposta è fantastico però :) – starbeamrainbowlabs

+0

@starbeamrainbowlabs Sì. Sembra che la pagina sia stata spostata. Ho ricollegato e dovrebbe essere ok ora. Grazie :) –

9

se si dispone di un bel selettore (ad esempio tutti i file TXT in un dir) si potrebbe fare:

for i in *.txt; do chmod 755 "$i"; done 

bash for loop

o una variante di il tuo:

while read line; do chmod 755 "$line"; done <file.txt 
+0

Ciò che non funziona è che se ci sono spazi nella linea, l'input è diviso per spazi, non per linea. –

9

Se si sa che non si dispone di spazi bianchi nell'input:

xargs chmod 755 < file.txt 

se ci potrebbe essere spazio bianco nei percorsi, e se si dispone di GNU xargs:

tr '\n' '\0' < file.txt | xargs -0 chmod 755 
+0

Conosco gli xargs, ma (purtroppo) mi sembra meno una soluzione affidabile rispetto alle funzionalità incorporate di bash come while e read. Inoltre, non ho GNU xargs, ma sto usando OS X e xargs ha anche un'opzione -0 qui. Grazie per la risposta. – hawk

+1

@hawk No: 'xargs' è robusto. Questo strumento è molto vecchio e il suo codice è fortemente * rivisitato *. Il suo obiettivo era inizialmente quello di costruire linee rispetto alle limitazioni della shell (64kchar/linea o qualcosa del genere). Ora questo strumento potrebbe funzionare con file molto grandi e può ridurre molto il numero di fork al comando finale. Vedi [la mia risposta] (http://stackoverflow.com/a/13941223/1765658) e/o 'man xargs'. –

+0

@hawk Meno di una soluzione affidabile in che modo? Se funziona in Linux, Mac/BSD e Windows (sì, i bundle GNU xargs di MSYSGIT), allora è affidabile come lo è. –

2

vedo che hai contrassegnato bash, ma Perl sarebbe anche un buon modo per farlo:

perl -p -e '`chmod 755 $_`' file.txt 

È anche possibile applicare un'espressione regolare per assicurarsi di ottenere i file corretti, ad es. al solo processo txt file:

perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt 

"Anteprima" quello che sta succedendo, basta sostituire il backticks con virgolette doppie e fallo precedere print:

perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt 
+2

Perché utilizzare i backtick? Perl ha una funzione ['chmod'] (http://perldoc.perl.org/functions/chmod.html) –

+0

Non me ne sono reso conto. Grazie per avermelo fatto notare :) –

+1

Vorresti 'perl -lpe 'chmod 0755, $ _' file.txt' - usa' -l' per la funzione "auto-chomp" –

7

Se si desidera eseguire il comando in parallelo per ogni linea è possibile utilizzare GNU Parallel

parallel -a <your file> <program> 

Ogni riga del file verrà passata al programma come argomento. Per impostazione predefinita, parallel esegue quanti thread contano le CPU. Ma puoi specificarlo con -j

Problemi correlati