Come passare ogni riga di un file di testo come argomento a un comando?


42

Sto cercando di scrivere uno script che accetta un .txtnome file come argomento, legge il file riga per riga e passa ciascuna riga a un comando. Ad esempio, viene eseguito command --option "LINE 1", quindi command --option "LINE 2", ecc. L'output del comando viene scritto in un altro file. Come faccio a farlo? Non so da dove iniziare.

Risposte:


26

Usa while readloop:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

Un altro è reindirizzare l'output per blocco:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

L'ultimo è aprire il file:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

Se uno dei comandi legge l'input, sarebbe una buona idea usare un altro fd per l'input in modo che i comandi non lo mangino (qui assumendo ksh, zsho bashper -u 3, usare <&3invece portabilmente):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

Infine, per accettare gli argomenti, puoi fare:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

Quale potrebbe essere eseguito come:

bash script.sh file another_file

Idea extra. Con bash, utilizzare readarray:

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

Nota: IFS=può essere omesso se non ti dispiace avere valori di linea tagliati di spazi iniziali e finali.


28

Un'altra opzione è xargs.

Con GNU xargs:

< file xargs -I{} -d'\n' command --option {} other args

{} è il segnaposto per la riga di testo.

Altri xargsnon hanno -d, ma alcuni hanno -0input delimitati da NUL. Con quelli, puoi fare:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

Sui sistemi conformi a Unix ( -Iè facoltativo in POSIX e richiesto solo per sistemi conformi a Unix), è necessario preelaborare l'input per quotare le righe nel formato previsto da xargs:

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

Tuttavia, si noti che alcune xargsimplementazioni hanno un limite molto basso sulla dimensione massima dell'argomento (255 su Solaris, ad esempio, il minimo consentito dalla specifica Unix).


riducibile a<file xargs -L 1 -I{} command --option {} other args
iruvar il

@iruvar, no, che elabora le virgolette e rimuove alcuni spazi.
Stéphane Chazelas,

7

Tenendo esattamente alla domanda:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option

1
ha funzionato come un fascino :-)
BugHunter l'

3

La migliore risposta che ho trovato è:

for i in `cat`; do "$cmd" "$i"; done < $file

MODIFICARE:

... quattro anni dopo ...

dopo diversi voti negativi e qualche altra esperienza, consiglierei ora quanto segue

xargs -l COMMAND < file

3
Questo invocherà il comando una volta per ogni parola nel file. Inoltre, dovresti sempre citare i riferimenti alle variabili della shell (come in do "$cmd" "$i";) a meno che tu non abbia un motivo per non farlo; se il file contenesse una *parola da solo, il codice verrebbe eseguito $cmd *, il che, ovviamente, eseguirà il comando con un elenco dei file nella directory corrente.
G-Man dice "Ripristina Monica" il

1
@ G-Man, tranne in zsh, il `cat`già espanderebbe il *(il non quotato $ipotrebbe ancora espandere alcuni caratteri jolly (un secondo round) se l'espansione di `cat`introduce alcuni). In ogni caso, questo approccio è davvero sbagliato.
Stéphane Chazelas,

1
+1. Funzionerà perfettamente, se ogni riga contiene solo l'input necessario per il comando che viene utilizzato senza spazi.
Sottomarino Sebastian

Questo agisce su ogni parola, esiste una variante di ciò che agisce su ogni riga?
thebunnyrules,

La domanda era di elaborare un elenco di nomi di file riga per riga.
Steffen Roller

2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

PRODUZIONE

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo

-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

Prendi tutte le righe di un file e passale come argomenti a un singolo comando, ad es.

command line1 line2 line3 ....

Se hai bisogno del --optionflag per precedere ogni riga, modifica il secondo comando in:

%g/^/s// --option /

1
Cosa -1 per usare ed ... davvero?
user2217522

3
Non ti ho sottovalutato, ma la persona che ha probabilmente avuto questi motivi: (1) Questo non fa ciò che la domanda chiede. Questo invoca il comando una volta con tutto il contenuto del file sulla riga di comando; piuttosto che una volta per riga . (2) Questo non fa nulla per gestire i caratteri speciali della shell (che potrebbero trovarsi nel file); ad esempio, ', ", <, >, ;, ecc (3) Questo crea un file temporanei non necessari. (4) Cose come questa sono generalmente fatte con "qui documenti". (5) I tuoi edcomandi sono goffi; i primi due comandi possono essere ridotti a %s/^/ /e %j.
G-Man dice "Ripristina Monica" il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.