Angenommen, ich habe eine Datei foo.txt
mit N
Argumenten
arg1
arg2
...
argN
was ich an den Befehl übergeben muss my_command
Wie verwende ich die Zeilen einer Datei als Argumente eines Befehls?
Angenommen, ich habe eine Datei foo.txt
mit N
Argumenten
arg1
arg2
...
argN
was ich an den Befehl übergeben muss my_command
Wie verwende ich die Zeilen einer Datei als Argumente eines Befehls?
Antworten:
Wenn Ihre Shell (unter anderem) bash ist, ist eine Verknüpfung für $(cat afile)
is $(< afile)
, also würden Sie schreiben:
mycommand "$(< file.txt)"
Dokumentiert in der Bash-Manpage im Abschnitt 'Befehlsersetzung'.
Lassen Sie Ihren Befehl alternativ von stdin lesen, also: mycommand < file.txt
<
Operator zu verwenden. Dies bedeutet, dass die Shell selbst die Umleitung ausführt, anstatt die cat
Binärdatei für ihre Umleitungseigenschaften auszuführen .
"$(…)"
, der Inhalt von file.txt
an die Standardeingabe von übergeben würde mycommand
, nicht als Argument. "$(…)"
bedeutet , den Befehl auszuführen und die Ausgabe als Zeichenfolge zurückzugeben ; hier liest der "Befehl" nur die Datei, aber es könnte komplexer sein.
Wie bereits erwähnt, können Sie die Backticks oder verwenden $(cat filename)
.
Was nicht erwähnt wurde, und ich denke, es ist wichtig anzumerken, ist, dass Sie sich daran erinnern müssen, dass die Shell den Inhalt dieser Datei nach Leerzeichen aufteilt und jedes "Wort", das sie findet, Ihrem Befehl als Argument gibt. Und obwohl Sie möglicherweise ein Befehlszeilenargument in Anführungszeichen setzen können, damit es Leerzeichen, Escape-Sequenzen usw. enthält, funktioniert das Lesen aus der Datei nicht dasselbe. Zum Beispiel, wenn Ihre Datei enthält:
a "b c" d
Die Argumente, die Sie erhalten, sind:
a
"b
c"
d
Wenn Sie jede Zeile als Argument ziehen möchten, verwenden Sie das Konstrukt while / read / do:
while read i ; do command_name $i ; done < filename
read -r
es sei denn, Sie möchten Backslash-Escape-Sequenzen erweitern - und NUL ist ein sicherer Trennzeichen als die Newline, insbesondere wenn es sich bei den übergebenen Argumenten um Dateinamen handelt, die wörtliche Newlines enthalten können. Ohne IFS zu löschen, werden implizit führende und nachfolgende Leerzeichen gelöscht i
.
command `< file`
Übergibt den Dateiinhalt an den Befehl auf stdin, entfernt jedoch Zeilenumbrüche, sodass Sie nicht jede Zeile einzeln durchlaufen können. Dafür könnten Sie ein Skript mit einer 'for'-Schleife schreiben:
for line in `cat input_file`; do some_command "$line"; done
Oder (die mehrzeilige Variante):
for line in `cat input_file`
do
some_command "$line"
done
Oder (mehrzeilige Variante mit $()
statt ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
Einfügen von Backticks bedeutet auch, dass überhaupt keine Umleitung durchgeführt wird command
.
command `< file`
weder in Bash noch in POSIX sh; zsh ist eine andere Sache, aber das ist nicht die Shell, um die es in dieser Frage geht).
Hello World
eine Zeile. Sie werden sehen , es zuerst ausgeführt some_command Hello
, dann some_command World
, nicht some_command "Hello World"
oder some_command Hello World
.
Sie tun dies mit Backticks:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
im ersten Schritt, Sie mindestens vier separate Argumente auf den zweiten Befehl übergeben bekommen würde - und wahrscheinlich mehr, da das *
s auf die Namen der Dateien erweitert würde, die im aktuellen Verzeichnis vorhanden sind.
fork()
löst tatsächlich eine Unterschale mit einem an seine Standardausgabe angehängten FIFO aus, ruft dann /bin/cat
als untergeordnetes Element dieser Unterschale auf und liest dann die Ausgabe durch das FIFO; Vergleichen Sie mit $(<file.txt)
, wodurch der Inhalt der Datei direkt in Bash gelesen wird, ohne dass Subshells oder FIFOs beteiligt sind.
Wenn Sie dies auf robuste Weise tun möchten, die für jedes mögliche Befehlszeilenargument funktioniert (Werte mit Leerzeichen, Werte mit Zeilenumbrüchen, Werte mit wörtlichen Anführungszeichen, nicht druckbare Werte, Werte mit Glob-Zeichen usw.), wird es etwas interessanter.
So schreiben Sie in eine Datei mit einer Reihe von Argumenten:
printf '%s\0' "${arguments[@]}" >file
... ersetzen "argument one"
, "argument two"
usw. als angemessen.
So lesen Sie aus dieser Datei und verwenden ihren Inhalt (in bash, ksh93 oder einer anderen aktuellen Shell mit Arrays):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Um aus dieser Datei zu lesen und ihren Inhalt zu verwenden (in einer Shell ohne Arrays; beachten Sie, dass dies Ihre lokale Befehlszeilenargumentliste überschreibt und daher am besten innerhalb einer Funktion ausgeführt wird, sodass Sie die Argumente der Funktion überschreiben und nicht die globale Liste):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Beachten Sie, dass -d
(die Verwendung eines anderen Zeilenende-Trennzeichens) eine Nicht-POSIX-Erweiterung ist und eine Shell ohne Arrays diese möglicherweise auch nicht unterstützt. In diesem Fall müssen Sie möglicherweise eine Nicht-Shell-Sprache verwenden, um den durch NUL getrennten Inhalt in eine eval
sichere Form umzuwandeln:
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 "$@"
So übergebe ich den Inhalt einer Datei als Argument an einen Befehl:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(bei Verwendung einer Shell wie Bash mit der entsprechenden Erweiterung). $(<foo)
ist ein Sonderfall: Im Gegensatz zur regulären Befehlssubstitution, wie sie in verwendet wird $(cat ...)
, wird eine Subshell nicht für den Betrieb freigegeben und somit ein hoher Overhead vermieden.
Wenn Sie nur eine Datei arguments.txt
mit Inhalten drehen müssen
arg1
arg2
argN
in my_command arg1 arg2 argN
dann können Sie einfach verwenden xargs
:
xargs -a arguments.txt my_command
Sie können dem xargs
Aufruf zusätzliche statische Argumente hinzufügen, wie xargs -a arguments.txt my_command staticArg
sie aufgerufen werdenmy_command staticArg arg1 arg2 argN
In meiner Bash-Shell funktionierte Folgendes wie ein Zauber:
cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'
wo input_file ist
arg1
arg2
arg3
Auf diese Weise können Sie mit jeder Zeile aus input_file mehrere Befehle ausführen, ein netter kleiner Trick, den ich hier gelernt habe .
$(somecommand)
, wird dieser Befehl ausgeführt und nicht als Text weitergegeben. Ebenso >/etc/passwd
wird als Umleitung verarbeitet und überschrieben /etc/passwd
(wenn mit entsprechenden Berechtigungen ausgeführt) usw.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- und auch effizienter, da so viele Argumente wie möglich an jede Shell übergeben werden, anstatt eine Shell pro Zeile in Ihrer Eingabedatei zu starten.
cat
Nachdem ich die Antwort von @Wesley Rice ein paar Mal bearbeitet hatte, entschied ich, dass meine Änderungen einfach zu groß wurden, um seine Antwort weiter zu ändern, anstatt meine eigene zu schreiben. Also habe ich beschlossen, dass ich meine eigenen schreiben muss!
Lesen Sie jede Zeile einer Datei ein und bearbeiten Sie sie zeilenweise wie folgt:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Dies kommt direkt vom Autor Vivek Gite hier: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Er bekommt den Kredit!
Syntax: Datei zeilenweise in einer Bash Unix- und Linux-Shell lesen:
1. Die Syntax für bash, ksh, zsh und alle anderen Shells lautet wie folgt, um eine Datei zeilenweise zu lesen
.while read -r line; do COMMAND; done < input.file
3. Die-r
Option wurde an den Befehl read übergeben verhindert, dass Backslash-Escapezeichen interpretiert werden.
4. Fügen SieIFS=
vor dem Lesebefehl eine Option hinzu, um zu verhindern, dass führende / nachfolgende Leerzeichen gekürzt werden -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
Und nun, um diese jetzt geschlossene Frage zu beantworten, die ich auch hatte: Ist es möglich, eine Liste von Dateien aus einer Datei hinzuzufügen? - Hier ist meine Antwort:
Beachten Sie, dass dies FILES_STAGED
eine Variable ist, die den absoluten Pfad zu einer Datei enthält, die eine Reihe von Zeilen enthält, wobei jede Zeile ein relativer Pfad zu einer Datei ist, für die ich etwas tun möchte git add
. Dieses Code-Snippet wird bald Teil der Datei "eRCaGuy_dotfiles / nützliche_skripte / sync_git_repo_to_build_machine.sh" in diesem Projekt, um die einfache Synchronisierung von Dateien in der Entwicklung von einem PC ( z. B. einem Computer, auf dem ich codiere) auf einen anderen ( z. B. a) zu ermöglichen leistungsstärkerer Computer, auf dem ich aufbaue): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
ihn aus: Ist es möglich, eine Liste von Dateien aus einer Datei hinzuzufügen?