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.