Ich weiß, dass ich dieses Problem auf mehrere Arten lösen kann, aber ich frage mich, ob es eine Möglichkeit gibt, dies nur mit Bash-Integrationen zu tun, und wenn nicht, welche Methode am effizientesten ist.
Ich habe eine Datei mit Inhalten wie
AAA
B C DDD
FOO BAR
womit ich nur meine, dass es mehrere Zeilen hat und jede Zeile Leerzeichen haben kann oder nicht. Ich möchte einen Befehl wie ausführen
cmd AAA "B C DDD" "FOO BAR"
Wenn ich benutze cmd $(< file)
bekomme ich
cmd AAA B C DDD FOO BAR
und wenn ich benutze cmd "$(< file)"
bekomme ich
cmd "AAA B C DDD FOO BAR"
Wie bekomme ich für jede Zeile genau einen Parameter?
bash
command-substitution
Alter Pro
quelle
quelle
Antworten:
Tragbar:
Oder verwenden Sie eine Subshell, um die
IFS
und -Option lokal zu ändern:Die Shell führt eine Feldaufteilung und eine Dateinamengenerierung für das Ergebnis einer Variablen- oder Befehlsersetzung durch, die nicht in doppelten Anführungszeichen steht. Sie müssen also die Dateinamengenerierung mit deaktivieren und die Feldaufteilung mit
set -f
konfigurierenIFS
, um nur Zeilenumbrüche als separate Felder zu verwenden.Mit bash- oder ksh-Konstrukten lässt sich nicht viel erreichen. Sie können
IFS
eine Funktion lokalisieren, aber nichtset -f
.In bash oder ksh93 können Sie die Felder in einem Array speichern, wenn Sie sie an mehrere Befehle übergeben müssen. Sie müssen die Erweiterung zum Zeitpunkt der Erstellung des Arrays steuern. Dann
"${a[@]}"
dehnt sich auf die Elemente des Arrays, eine pro Wort.quelle
Sie können dies mit einem temporären Array tun.
Installieren:
Füllen Sie das Array aus:
Verwenden Sie das Array:
Ohne diese temporäre Variable
IFS
ist dies nicht möglich - es sei denn, die Änderung ist für Folgendes nicht wichtigcmd
:Sollte es tun.
quelle
IFS
bringt mich immer durcheinander.IFS=$'\n' cmd $(<input)
funktioniert nichtIFS=$'\n'; cmd $(<input); unset IFS
funktioniert. Warum? Ich denke, ich werde verwenden(IFS=$'\n'; cmd $(<input))
IFS=$'\n' cmd $(<input)
funktioniert nicht, da es nurIFS
in der Umgebung von festgelegt wirdcmd
.$(<input)
wird erweitert, um den Befehl zu bilden, bevor die Zuordnung zu ausgeführtIFS
wird.Es sieht so aus, als ob die kanonische Art, dies zu tun, in etwa so
bash
istoder, wenn Ihre Version von Bash hat
mapfile
:Der einzige Unterschied besteht zwischen dem Mapfile und der While-Read-Schleife und dem Einzeiler
ist, dass ersteres eine leere Zeile in ein leeres Argument umwandelt, während der Einzeiler eine leere Zeile ignoriert. In diesem Fall ist das Ein-Linien-Verhalten sowieso das, was ich vorziehen würde, also doppelter Bonus, wenn es kompakt ist.
Ich würde es verwenden,
IFS=$'\n' cmd $(<file)
aber es funktioniert nicht, da$(<file)
es so interpretiert wird, dass es die Befehlszeile bildet, bevor esIFS=$'\n'
wirksam wird.Obwohl es in meinem Fall nicht funktioniert, habe ich jetzt erfahren, dass viele Tools das Beenden von Zeilen unterstützen,
null (\000)
anstattnewline (\n)
dies beim Umgang mit beispielsweise Dateinamen, die häufige Ursachen für diese Situationen sind, zu vereinfachen :Füttert md5 eine Liste vollqualifizierter Dateinamen als Argumente, ohne dass ein Globbing oder eine Interpolation erforderlich ist. Das führt zur nicht eingebauten Lösung
Auch dies ignoriert zwar leere Zeilen, erfasst jedoch Zeilen, die nur Leerzeichen enthalten.
quelle
cmd $(<file)
Werten ohne Anführungszeichen (Verwendung der Fähigkeit von Bash, Wörter zu trennen) ist immer eine riskante Wette. Wenn eine Zeile vorhanden ist*
, wird sie von der Shell zu einer Liste von Dateien erweitert.Sie können die integrierte Bash-Funktion verwenden
mapfile
, um die Datei in ein Array einzulesenoder, ungetestet,
xargs
könnte es tunquelle
xargs
hilft auch nicht.xargs -d
oderxargs -L
-d
Option und führexargs -L 1
den Befehl einmal pro Zeile aus, teile aber immer noch Args in Leerzeichen auf.mapfile
ist sehr praktisch für mich, da es leere Zeilen als Array-Elemente erfasst, was dieIFS
Methode nicht tut.IFS
Behandelt zusammenhängende Zeilenumbrüche als ein einzelnes Trennzeichen. Vielen Dank für die Darstellung, da mir der Befehl nicht bekannt war (auf der Grundlage der Eingabedaten des OP und der erwarteten Befehlszeile scheint er jedoch tatsächlich Leerzeilen ignorieren zu wollen).Der beste Weg, den ich finden konnte. Funktioniert einfach.
quelle
IFS
auf Raum setzen, aber die Frage ist, nicht auf Raum zu teilen?Datei
Die einfachste Schleife (portabel) zum Teilen einer Datei in Zeilenumbrüche ist:
Welches wird drucken:
Ja, mit Standard IFS = spacetabnewline.
Warum es funktioniert
IFS
nötig.set -f
nötig.-r
(rohe) Option besteht darin, das Entfernen der meisten Backslashs zu vermeiden.Dies funktioniert nicht , wenn eine Aufteilung und / oder Verschiebung erforderlich ist. In solchen Fällen ist eine komplexere Struktur erforderlich.
Wenn Sie (noch tragbar) benötigen, um:
IFS= read -r line
IFS=':' read -r a b c
.Teilen Sie die Datei auf ein anderes Zeichen auf (nicht portierbar, funktioniert mit ksh, bash, zsh):
Erweiterung
Im Titel Ihrer Frage geht es natürlich darum, eine Befehlsausführung in Zeilenumbrüche aufzuteilen und die Aufteilung in Leerzeichen zu vermeiden.
Die einzige Möglichkeit, die Shell zu spalten, besteht darin, eine Erweiterung ohne Anführungszeichen zu hinterlassen:
Dies wird durch den Wert von IFS gesteuert, und bei nicht zitierten Erweiterungen wird auch Globbing angewendet. Um diese Arbeit zu machen, brauchen Sie:
Deaktivieren Sie die Globbing-Shell-Option
set +f
:setze + f IFS = '' cmd $ (<Datei)
Dies ändert natürlich den Wert von IFS und Globbing für den Rest des Skripts.
quelle