2010-11-19 20 views
90

Come si trasforma il contenuto del file in un argomento per un comando Unix?argomenti della riga di comando dal contenuto di un file

Passando un argomento nel contenuto del file è fatto come:

echo ABC > file.txt 

Ma l'altra direzione?

+2

"argomenti", plurale o "argomento", singolo? La risposta accettata è corretta solo nel caso a un solo argomento - che è ciò di cui si chiede il testo del corpo - ma non nel caso a più argomenti, su cui l'argomento sembra indagare. –

+0

Le 4 risposte sottostanti di solito hanno risultati identici, eppure hanno una semantica leggermente diversa. Ora ricordo perché ho smesso di scrivere script di bash: P – Navin

risposta

135

Se la shell è bash (tra gli altri), una scorciatoia per $(cat afile) è $(< afile), quindi devi scrivere:

mycommand "$(< file.txt)" 

documentato nel pagina man di bash nella sezione "Sostituzione comandi".

Alterately, avere il vostro comando di leggere da stdin, quindi: mycommand < file.txt

+4

Per essere pedante, non è una scorciatoia per usare l'operatore '<. Significa che la shell stessa eseguirà il reindirizzamento piuttosto che l'esecuzione del file binario 'cat' per le sue proprietà di reindirizzamento. – lstyls

+0

Esiste un limite al numero di argomenti? Se è così, come posso superarlo? – becko

+0

È un'impostazione del kernel quindi è necessario ricompilare il kernel. O indagare il comando xargs –

13

lo fai con apici inversi:

echo World > file.txt 
echo Hello `cat file.txt` 
+0

Questo non crea * un * argomento - perché non è quotato, è soggetto a divisione di stringhe, quindi se hai emesso 'echo" Hello * Starry * World " > file.txt' nel primo passaggio, otterresti almeno quattro argomenti separati passati al secondo comando - e probabilmente di più, dato che '*' s si espanderebbe ai nomi dei file presenti nella directory corrente. –

+0

... e poiché sta eseguendo espansione glob, non emette * esattamente * cosa c'è nel file. ** E ** perché sta eseguendo un'operazione di sostituzione di comando, è estremamente inefficiente - in realtà 'fork()' effettua una sottochiave con una FIFO collegata al suo stdout, quindi richiama '/ bin/cat' come figlio di quello subshell, quindi leggendo l'output attraverso la FIFO; confronta con '$ (

9
command `< file` 

passerà contenuto del file al comando su stdin, ma metterà a nudo a capo, il che significa non si poteva iterare su ogni linea singolarmente. Per questo si potrebbe scrivere uno script con un 'in' loop:

for i in `cat input_file`; do some_command $i; done 
+0

http://mywiki.wooledge.org/DontReadLinesWithFor –

+0

... per l'approccio corretto, http://mywiki.wooledge.org/BashFAQ/001 –

+0

Inoltre, mettere '

30

Come già accennato, è possibile utilizzare i backticks o $(cat filename).

Ciò che non è stato menzionato, e penso sia importante notare, è che è necessario ricordare che la shell spezzerà il contenuto di quel file in base allo spazio, dando ogni "parola" che trova al comando come argomento . E mentre potresti essere in grado di racchiudere un argomento da riga di comando tra virgolette in modo che possa contenere spazi bianchi, sequenze di escape, ecc., La lettura dal file non farà la stessa cosa. Ad esempio, se il file contiene:

a "b c" d 

gli argomenti che si ottengono sono:

a 
"b 
c" 
d 

Se si vuole tirare ogni riga come argomento, utilizzare il tempo/lettura/non costruire:

while read i ; do command_name $i ; done < filename 
+0

Avrei dovuto accennare, presumo che stai usando bash. Mi rendo conto che ci sono altre shell là fuori, ma quasi tutte le macchine * nix su cui ho lavorato hanno pubblicato bash o qualche equivalente.IIRC, questa sintassi dovrebbe funzionare allo stesso modo su ksh e zsh. – Will

+1

La lettura dovrebbe essere 'read -r' a meno che non si voglia espandere le sequenze di backslash-escape e NUL è un delimitatore più sicuro da utilizzare rispetto alla newline, in particolare se gli argomenti che si stanno passando sono come nomi di file, che possono contenere righe nuove letterali . Inoltre, senza eliminare IFS, si ottengono gli spazi bianchi iniziali e finali eliminati implicitamente da 'i'. –

3

Nel mio bash Shell quanto segue lavorato come un fascino:

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;' 

dove input_file è

arg1 
arg2 
arg3 

Come risulta evidente, questo permette di eseguire più comandi con ogni riga da file_input, un bel po 'di trucco che ho imparato here.

+1

Il tuo "bel trucchetto" è pericoloso dal punto di vista della sicurezza - se hai un argomento contenente '$ (somecommand)', otterrai quel comando eseguito piuttosto che passato come testo. Allo stesso modo, '>/etc/passwd' sarà elaborato come reindirizzamento e sovrascrittura'/etc/passwd' (se eseguito con permessi appropriati), ecc. –

+1

È molto più sicuro fare quanto segue (su un sistema con estensioni GNU) : 'xargs -d $ '\ n' sh -c 'per arg; do comando1 "$ arg"; command2 "arg"; command3 "arg"; fatto '_' - e anche più efficiente, dal momento che passa il maggior numero di argomenti per ogni shell possibile invece di avviare una shell per riga nel file di input. –

5

Se si desidera eseguire questa operazione in modo affidabile, funziona per ogni possibile argomento della riga di comando (valori con spazi, valori con newline, valori con caratteri letterali, valori non stampabili, valori con caratteri glob, ecc.), diventa un po 'più interessante.


Per scrivere in un file, data una matrice di argomenti:

printf '%s\0' "${arguments[@]}" >file 

... sostituirlo con "argument one", "argument two", ecc a seconda dei casi.


Per leggere dal file e utilizzare il suo contenuto (in bash, ksh93 o altro recente shell con array):

declare -a args=() 
while IFS='' read -r -d '' item; do 
    args+=("$item") 
done <file 
run_your_command "${args[@]}" 

Per leggere dal file e utilizzare il suo contenuto (in una shell senza matrici, si noti che sovrascriverà l'elenco degli argomenti della riga di comando locale ed è quindi fatto meglio all'interno di una funzione, in modo tale da sovrascrivere gli argomenti della funzione e non l'elenco globale):

set -- 
while IFS='' read -r -d '' item; do 
    set -- "[email protected]" "$item" 
done <file 
run_your_command "[email protected]" 

Si noti che -d (che consente di utilizzare un delimitatore di fine riga diverso) è un'estensione non POSIX e una shell senza array potrebbe non supportarla. Se questo dovesse essere il caso, potrebbe essere necessario utilizzare un linguaggio non-shell per trasformare il contenuto NUL delimitato in una forma eval -safe:

quoted_list() { 
    ## Works with either Python 2.x or 3.x 
    python -c ' 
import sys, pipes, shlex 
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote 
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1])) 
    ' 
} 

eval "set -- $(quoted_list <file)" 
run_your_command "[email protected]" 
4

Ecco come passo contenuto di un file come argomento di un comando:

./foo --bar "$(cat ./bar.txt)" 
+1

Questo è - ma per essere sostanzialmente meno efficiente - efficacemente equivalente a '$ (

Problemi correlati