Risposte:
Questo perché non esiste una sola risposta ...
shell
espansione della riga di comandoxargs
strumento dedicatowhile read
con alcune osservazioniwhile read -u
utilizzando dedicato fd
, per l' elaborazione interattiva (esempio)Per quanto riguarda la richiesta OP: in esecuzione chmod
su tutte le destinazioni elencate nel file , xargs
è lo strumento indicato. Ma per alcune altre applicazioni, una piccola quantità di file, ecc ...
Se il tuo file non è troppo grande e tutti i file hanno un buon nome (senza spazi o altri caratteri speciali come virgolette), potresti usare l' shell
espansione della riga di comando . Semplicemente:
chmod 755 $(<file.txt)
Per una piccola quantità di file (linee), questo comando è quello più leggero.
xargs
è lo strumento giustoPer un numero maggiore di file o quasi un numero qualsiasi di righe nel file di input ...
Per molti Binutils strumenti, come chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Se hai caratteri speciali e / o molte righe in file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
se il comando deve essere eseguito esattamente 1 volta per voce:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Questo non è necessario per questo esempio, poiché chmod
accetta più file come argomento, ma corrisponde al titolo della domanda.
Per alcuni casi speciali, potresti persino definire la posizione dell'argomento del file nei comandi generati da xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
come inputProva questo:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
Dove viene eseguito il comando una volta per riga .
while read
e varianti.Come suggerisce l'OP cat file.txt | while read in; do chmod 755 "$in"; done
funzionerà, ma ci sono 2 problemi:
cat |
è una forcella inutile , e
| while ... ;done
diventerà una subshell in cui l'ambiente scomparirà dopo ;done
.
Quindi questo potrebbe essere scritto meglio:
while read in; do chmod 755 "$in"; done < file.txt
Ma,
Potresti essere avvisato $IFS
e read
bandiere:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
In alcuni casi, potrebbe essere necessario utilizzare
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Per evitare problemi con nomi di file sconosciuti. E forse se riscontri problemi con UTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
Mentre lo usi STDIN
per la lettura file.txt
, il tuo script non potrebbe essere interattivo (non puoi STDIN
più utilizzarlo ).
while read -u
, utilizzando dedicato fd
.Sintassi: while read ...;done <file.txt
reindirizzerà STDIN
a file.txt
. Ciò significa che non sarai in grado di gestire il processo fino a quando non avranno terminato.
Se si prevede di creare uno strumento interattivo , è necessario evitare l'uso STDIN
e l'uso di un descrittore di file alternativo .
I descrittori di file costanti sono: 0
per STDIN , 1
per STDOUT e 2
per STDERR . Li puoi vedere da:
ls -l /dev/fd/
o
ls -l /proc/self/fd/
Da lì, devi scegliere un numero inutilizzato, tra 0
e 63
(più, in effetti, a seconda dello sysctl
strumento superutente) come descrittore di file :
Per questa demo, userò fd 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Quindi potresti usare in read -u 7
questo modo:
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
Per chiudere fd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Nota: ho lasciato la versione colpita perché questa sintassi potrebbe essere utile quando si eseguono molti I / O con processi paralleli:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
è circa il 50% più veloce del metodo che hai descritto.
Sì.
while read in; do chmod 755 "$in"; done < file.txt
In questo modo puoi evitare un cat
processo.
cat
fa quasi sempre male per uno scopo come questo. Puoi leggere di più sull'uso inutile di Cat.
cat
è una buona idea, ma in questo caso il comando indicato èxargs
chmod
(ovvero eseguire un comando per ogni riga nel file).
read -r
legge una singola riga dall'input standard ( read
senza -r
interpretare le barre rovesciate, non lo vuoi)."
se hai un bel selettore (ad esempio tutti i file .txt in una directory) puoi fare:
for i in *.txt; do chmod 755 "$i"; done
o una tua variante:
while read line; do chmod 755 "$line"; done <file.txt
Se sai di non avere spazi bianchi nell'input:
xargs chmod 755 < file.txt
Se potrebbero esserci spazi bianchi nei percorsi e se hai GNU xargs:
tr '\n' '\0' < file.txt | xargs -0 chmod 755
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 di dimensioni molto grandi e potrebbe ridurre notevolmente il numero di fork fino al comando finale. Vedi la mia risposta e / o man xargs
.
brew install findutils
), e quindi invocare GNU xargs con gxargs
invece, ad es.gxargs chmod 755 < file.txt
Se vuoi eseguire il tuo comando in parallelo per ogni linea puoi usare 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
Vedo che hai taggato bash, ma Perl sarebbe anche un buon modo per farlo:
perl -p -e '`chmod 755 $_`' file.txt
Puoi anche applicare una regex per assicurarti di ottenere i file giusti, ad esempio per elaborare solo i file .txt:
perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt
Per "visualizzare in anteprima" ciò che sta accadendo, basta sostituire i backtick con doppie virgolette e anteporre print
:
perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
chmod
funzione
perl -lpe 'chmod 0755, $_' file.txt
- uso -l
per la funzione "auto-Chomp"
Puoi anche utilizzare AWK che ti offre maggiore flessibilità nella gestione del file
awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt
se il tuo file ha un separatore di campo come:
campo1, campo2, field3
Per ottenere solo il primo campo che fai
awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt
Puoi controllare maggiori dettagli sulla documentazione GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
La logica si applica a molti altri obiettivi. E come leggere .sh_history di ogni utente da / home / filesystem? E se ce ne fossero migliaia?
#!/bin/ksh
last |head -10|awk '{print $1}'|
while IFS= read -r line
do
su - "$line" -c 'tail .sh_history'
done
Ecco lo script https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
So che è tardi ma comunque
Se per caso ti imbatti in file di testo salvato con Windows \r\n
invece di \n
, potresti essere confuso dall'output se il tuo comando ha sth dopo aver letto la riga come argomento. Quindi rimuovere \r
, ad esempio:
cat file | tr -d '\r' | xargs -L 1 -i echo do_sth_with_{}_as_line
xargs
stato inizialmente creato per rispondere a questo tipo di necessità, alcune funzionalità, come la creazione di comandi il più a lungo possibile nell'ambiente attuale per invocarechmod
in questo caso il meno possibile, ridurre le forche garantiscono efficienza.while ;do..done <$file
implie eseguendo 1 fork per 1 file.xargs
potrebbe eseguire 1 fork per migliaia di file ... in modo affidabile.