Wie übergebe ich jede Zeile einer Textdatei als Argument an einen Befehl?

42

Ich möchte ein Skript schreiben, das einen .txtDateinamen als Argument verwendet, die Datei zeilenweise liest und jede Zeile an einen Befehl übergibt. Beispielsweise läuft es command --option "LINE 1"dann command --option "LINE 2"usw. Die Ausgabe des Befehls wird in eine andere Datei geschrieben. Wie mache ich das? Ich weiß nicht, wo ich anfangen soll.

WojciechF
quelle

Antworten:

26

Verwendung while readLoop:

: > another_file  ## Truncate file.

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

Eine andere Möglichkeit besteht darin, die Ausgabe blockweise umzuleiten:

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

Zuletzt öffnen Sie die Datei:

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

Wenn einer der Befehle input liest, ist es eine gute Idee, einen anderen fd für die Eingabe zu verwenden, damit die Befehle ihn nicht essen (hier unter der Annahme ksh, zshoder bashfür -u 3, <&3stattdessen portabel zu verwenden):

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

Um schließlich Argumente zu akzeptieren, können Sie Folgendes tun:

#!/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"

Welches könnte man laufen als:

bash script.sh file another_file

Extra Idee. Mit bashverwenden Sie readarray:

readarray -t LINES < "$FILE"

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

Hinweis: IFS=Kann weggelassen werden, wenn es Ihnen nichts ausmacht, Zeilenwerte von führenden und nachfolgenden Leerzeichen trennen zu lassen.

konsolebox
quelle
28

Eine andere Option ist xargs.

Mit GNU xargs:

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

{} ist der Platzhalter für die Textzeile.

Andere xargshaben keine -d, aber einige haben eine -0durch NUL getrennte Eingabe. Mit diesen können Sie Folgendes tun:

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

Auf Unix-konformen Systemen ( -Iist in POSIX optional und nur für Unix-konforme Systeme erforderlich) müssen Sie die Eingabe vorverarbeiten, um die Zeilen in dem Format zu zitieren, das erwartet wird von xargs:

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

Beachten Sie jedoch, dass einige xargsImplementierungen eine sehr niedrige Grenze für die maximale Größe des Arguments haben (255 unter Solaris zum Beispiel das in der Unix-Spezifikation zulässige Minimum).

Strg-Alt-Delor
quelle
<file xargs -L 1 -I{} command --option {} other args
Reduzierbar
@iruvar, nein, das würde Zitate verarbeiten und einige Leerzeichen entfernen.
Stéphane Chazelas
7

Halten Sie sich genau an die Frage:

#!/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
Miigotu
quelle
1
arbeitete wie Charme :-)
BugHunter
3

Die beste Antwort, die ich gefunden habe, ist:

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

BEARBEITEN:

... vier Jahre später ...

nach mehreren downstimmen und etwas mehr erfahrung würde ich nun folgendes empfehlen

xargs -l COMMAND < file
Steffen Roller
quelle
3
Dadurch wird der Befehl einmal für jedes Wort in der Datei aufgerufen. Sie sollten auch immer Verweise auf Shell-Variablen (wie in do "$cmd" "$i";) angeben, es sei denn, Sie haben einen Grund, dies nicht zu tun. Wenn die Datei *ein Wort für sich enthalten würde $cmd *, würde Ihr Code ausgeführt , der natürlich den Befehl mit einer Liste der Dateien im aktuellen Verzeichnis ausführen würde .
G-Man sagt, dass Monica am
1
@ G-Man, außer in zsh, das `cat`würde schon das erweitern *(das nicht zitierte $ikönnte noch etwas Wildcard erweitern (eine zweite Runde) wenn die Erweiterung von `cat`einiges einführt). Auf jeden Fall ist dieser Ansatz in der Tat falsch.
Stéphane Chazelas
1
+1. Dies funktioniert einwandfrei, wenn jede Zeile nur die Eingabe enthält, die für den Befehl benötigt wird, der ohne Leerzeichen verwendet wird.
Subin Sebastian
Dies wirkt sich auf jedes Wort aus. Gibt es eine Variante davon, die auf jede Zeile einwirkt?
Thebunnyrules
Die Frage war, eine Liste von Dateinamen Zeile für Zeile zu verarbeiten.
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

AUSGABE

--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
mikeserv
quelle
-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

Nehmen Sie alle Zeilen einer Datei und übergeben Sie sie als Argumente an einen einzelnen Befehl, dh

command line1 line2 line3 ....

Wenn Sie das --optionFlag vor jeder Zeile benötigen , ändern Sie den zweiten Befehl in:

%g/^/s// --option /
user2217522
quelle
1
Was -1 für die Verwendung von ed ... wirklich?
user2217522
3
Ich habe Sie nicht abgelehnt, aber die Person, die dies getan hat, hatte wahrscheinlich folgende Gründe: (1) Dies tut nicht das, was die Frage verlangt. Dadurch wird der Befehl einmal mit dem gesamten Inhalt der Datei in der Befehlszeile aufgerufen. anstatt einmal pro Zeile . (2) Dies hat keine Auswirkung auf Sonderzeichen für die Shell (die möglicherweise in der Datei enthalten sind). zB ', ", <, >, ;, etc. (3) Dies bedeutet eine unnötige temporäre Datei. (4) Solche Dinge werden im Allgemeinen mit "hier Dokumenten" gemacht. (5) Ihre edBefehle sind ungeschickt; Die ersten beiden Befehle können auf %s/^/ /und reduziert werden %j.
G-Man sagt, dass Monica am