Dì, ho un file che foo.txt
specifica gli N
argomenti
arg1
arg2
...
argN
che devo passare al comando my_command
Come posso usare le righe di un file come argomenti di un comando?
Dì, ho un file che foo.txt
specifica gli N
argomenti
arg1
arg2
...
argN
che devo passare al comando my_command
Come posso usare le righe di un file come argomenti di un comando?
Risposte:
Se la tua shell è bash (tra gli altri), una scorciatoia $(cat afile)
è $(< afile)
, quindi dovresti scrivere:
mycommand "$(< file.txt)"
Documentato nella pagina man di bash nella sezione 'Sostituzione comandi'.
In alternativa, fai leggere il tuo comando da stdin, quindi: mycommand < file.txt
<
operatore. Significa che la shell stessa eseguirà il reindirizzamento anziché eseguire il cat
binario per le sue proprietà di reindirizzamento.
"$(…)"
, il contenuto di file.txt
verrebbe passato all'input standard di mycommand
, non come argomento. "$(…)"
significa eseguire il comando e restituire l'output come stringa ; qui il "comando" legge solo il file ma potrebbe essere più complesso.
Come già accennato, è possibile utilizzare i backtick o $(cat filename)
.
Ciò che non è stato menzionato, e penso sia importante notare, è che devi ricordare che la shell spezzerà il contenuto di quel file in base allo spazio bianco, dando ogni "parola" che trova al tuo comando come argomento. E mentre potresti essere in grado di racchiudere un argomento della 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 tuo file contiene:
a "b c" d
gli argomenti che otterrai saranno:
a
"b
c"
d
Se vuoi tirare ogni riga come argomento, usa il costrutto while / read / do:
while read i ; do command_name $i ; done < filename
read -r
meno che non si desideri espandere le sequenze backslash-escape - e NUL è un delimitatore più sicuro da utilizzare rispetto alla nuova riga, in particolare se gli argomenti che si stanno passando sono cose come i nomi di file, che possono contenere letterali letterali. Inoltre, senza cancellare IFS, si ottiene uno spazio bianco iniziale e finale implicitamente eliminato i
.
command `< file`
passerà il contenuto del file al comando su stdin, ma rimuoverà le nuove righe, il che significa che non è possibile scorrere su ciascuna riga singolarmente. Per questo potresti scrivere uno script con un ciclo 'for':
for line in `cat input_file`; do some_command "$line"; done
Oppure (la variante multilinea):
for line in `cat input_file`
do
some_command "$line"
done
Oppure (variante multilinea con $()
invece di ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
backtick significa che in realtà non esegue affatto un reindirizzamento command
.
command `< file`
Non funziona sia in bash o POSIX sh; zsh è una questione diversa, ma non è la shell a questa domanda è di circa).
Hello World
su una riga. Lo vedrai prima eseguito some_command Hello
, quindi some_command World
, no some_command "Hello World"
o some_command Hello World
.
Lo fai usando i backtick:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
nel primo passaggio, si otterrebbero almeno quattro argomenti separati passati al secondo comando - e probabilmente di più, poiché la *
s si espanderebbe ai nomi dei file presenti nella directory corrente.
fork()
inghiottendo una subshell con un FIFO collegato al suo stdout, quindi invocando /bin/cat
come figlio di quella subshell, quindi leggendo l'output tramite FIFO; confronta con $(<file.txt)
, che legge il contenuto del file in bash direttamente senza sottotitoli o FIFO coinvolti.
Se vuoi farlo in un modo robusto che funzioni per ogni possibile argomento della riga di comando (valori con spazi, valori con nuove righe, valori con caratteri di virgolette letterali, valori non stampabili, valori con caratteri glob, ecc.), Diventa un po ' più interessante.
Per scrivere su un file, dato un array di argomenti:
printf '%s\0' "${arguments[@]}" >file
... sostituirlo con "argument one"
, "argument two"
ecc a seconda dei casi.
Per leggere da quel file e usare il suo contenuto (in bash, ksh93 o un'altra shell recente con array):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Per leggere da quel file e usare il suo contenuto (in una shell senza array; nota che questo sovrascriverà il tuo elenco di argomenti della riga di comando locale, e quindi è meglio farlo all'interno di una funzione, in modo da sovrascrivere gli argomenti della funzione e non l'elenco globale):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Si noti che -d
(consentendo l'utilizzo di un diverso delimitatore di fine riga) è un'estensione non POSIX e una shell senza array potrebbe non supportarla. In tal caso, potrebbe essere necessario utilizzare un linguaggio non shell per trasformare il contenuto delimitato da NUL in un eval
formato sicuro:
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 "$@"
Ecco come passo i contenuti di un file come argomento a un comando:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(quando si usa una shell, come bash, con l'estensione appropriata). $(<foo)
è un caso speciale: a differenza della normale sostituzione dei comandi, come usato in $(cat ...)
, non esegue il fork di una subshell in cui operare, evitando così un notevole sovraccarico.
Se tutto ciò che devi fare è girare il file arguments.txt
con i contenuti
arg1
arg2
argN
in my_command arg1 arg2 argN
allora puoi semplicemente usare xargs
:
xargs -a arguments.txt my_command
È possibile inserire ulteriori argomenti statici nella xargs
chiamata, ad esempio xargs -a arguments.txt my_command staticArg
quale chiameràmy_command staticArg arg1 arg2 argN
Nella mia shell bash il seguente ha funzionato come un incantesimo:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
dove input_file è
arg1
arg2
arg3
Come evidente, questo ti consente di eseguire più comandi con ciascuna riga da input_file, un piccolo trucco che ho imparato qui .
$(somecommand)
, riceverai quel comando piuttosto che passarlo come testo. Allo stesso modo, >/etc/passwd
verrà elaborato come reindirizzamento e sovrascrivere /etc/passwd
(se eseguito con le autorizzazioni appropriate), ecc.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- e anche più efficiente, poiché trasmette quanti più argomenti possibile a ciascuna shell anziché avviare una shell per riga nel file di input.
cat
Dopo aver modificato la risposta di @Wesley Rice un paio di volte, ho deciso che le mie modifiche stavano diventando troppo grandi per continuare a cambiare la sua risposta invece di scrivere la mia. Quindi, ho deciso di scrivere il mio!
Leggi ogni riga di un file e operaci riga per riga in questo modo:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Questo viene direttamente dall'autore Vivek Gite qui: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Ottiene il merito!
Sintassi: leggi il file riga per riga su una shell Unix e Linux di Bash:
1. La sintassi è la seguente per bash, ksh, zsh e tutte le altre shell per leggere un file riga per riga
2.while read -r line; do COMMAND; done < input.file
3. L'-r
opzione è passata al comando read impedisce l'interpretazione delle escape di backslash.
4. AggiungereIFS=
un'opzione prima del comando di lettura per evitare che gli spazi bianchi iniziali / finali vengano tagliati -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
E ora per rispondere a questa domanda ora chiusa che ho anche avuto: è possibile `git aggiungere` un elenco di file da un file? - ecco la mia risposta:
Si noti che FILES_STAGED
è una variabile che contiene il percorso assoluto di un file che contiene un gruppo di righe in cui ogni riga è un percorso relativo di un file su cui mi piacerebbe fare git add
. Questo frammento di codice sta per diventare parte del file "eRCaGuy_dotfiles / util_scripts / sync_git_repo_to_build_machine.sh" in questo progetto, per consentire una facile sincronizzazione dei file in sviluppo da un PC (es: un computer su cui accendo ) ad un altro (es: a computer più potente su cui costruisco): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
su di esso: è possibile `git aggiungere` un elenco di file da un file?