Ich versuche erfolglos, einen awk
-Befehl in einem for
Schleife.
Ich habe eine Variable, die eine Reihe von Zeichenfolgen enthält, die ich mit awk
schneiden möchte um die Daten zu erhalten.
Ich weiß, wie das geht, aber ich möchte wirklich, dass die Daten nacheinander geschnitten werden.
Also habe ich diese Variable:
var="data1,data2,data3"
Und hier, wo ich gerade bin:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Ich versuche, die $1
durch die Schleife $i
, jedoch ohne Erfolg.
Kommentare
- Jedes Zeichen zwischen zwei einfachen Anführungszeichen wird als wörtliches Zeichen behandelt.
- Was ist Ihr wirklicher Anwendungsfall? Eine Schleife über awk erscheint hier unnötig (zum Beispiel in bash könnten Sie eine Parametersubstitution verwenden
echo "${var//,/$'\n'}"
) - Tatsächlich enthält die Variable URLs aus einer Zenity-Form. Ich möchte diese URLs separat verwenden, damit ich sie einzeln abrufen kann.
- Relevant, aber in diesem Fall keine gute Lösung: Gewusst wie Wert zur Laufzeit im AWK-Befehl zuweisen
Antwort
Sie können das erreichen, was Sie sind Versuchen Sie dies, indem Sie im awk-Skript doppelte Anführungszeichen verwenden, um die Shell-Variable einzufügen. Sie möchten immer noch ein Literal $
darin behalten, was Sie tun können, indem Sie es mit Backslash maskieren :
echo $(awk -F, "{print \$$i}" <<<$var)
Dadurch wird die $i
auf 1
erweitert. 2
und 3
in jeder der Iterationen, daher sieht awk $1
, $2
und $3
, wodurch jedes der Felder erweitert wird.
Eine andere Möglichkeit besteht darin, die Shell-Variable als zu injizieren awk-Variable mit dem Flag -v
:
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Damit wird die awk-Variable i dem Inhalt der gleichnamigen Shell-Variablen zugewiesen. Variablen in awk verwenden kein $
, das für Felder verwendet wird. $i
reicht also aus, um auf das i zu verweisen -thes Feld, wenn i eine Variable in awk ist.
Das Zuweisen einer awk-Variablen mit -v
ist im Allgemeinen sicherer Ansatz, insbesondere wenn es beliebige Zeichenfolgen enthalten kann, besteht in diesem Fall ein geringeres Risiko, dass der Inhalt gegen Ihre Absichten als awk-Code ausgeführt wird. Da die Variable in Ihrem Fall jedoch eine einzelne Ganzzahl enthält, ist dies weniger bedenklich.
Eine weitere Option ist die Verwendung einer for
-Schleife in awk selbst Weitere Informationen dazu finden Sie in der awk-Dokumentation (oder durchsuchen Sie diese Site).
Kommentare
- … oder legen Sie den Eingabedatensatz fest Trennzeichen entsprechend
awk -v RS='[,\n]' 1 <<< "$var"
(oder vielleicht portablerprintf "$var" | awk -v RS=, 1
) - @steeldriver Dieses awk-Skript druckt alle drei Felder unter einmal. Was in Ordnung ist, wenn das OP genau das tut … Am Ende konzentrierte ich die Antwort auf das Extrahieren eines einzelnen Feldes unter der Annahme, dass sie daran interessiert waren, andere Befehle in der Shell-Schleife auszuführen (was möglicherweise der Fall ist) waren die Motivation, überhaupt eine zu verwenden …)
- Verstanden – das ‚ ist der Grund, warum ich das OP kommentiert habe, um zu klären, was sie wirklich wollen;)
- Das erste ist eine Injektion (und ein Sicherheitsbedenken), das zweite o ne (mit
-v
) ist keine Injektion. - Danke, Ihre Antwort hat mein Problem gelöst! Zum ersten Mal poste ich hier eine Frage, nachdem ich jahrelang Beiträge gelesen habe. Nicht enttäuscht. Eine so großartige Community!
Antwort
Die Verwendung von awk
scheint Wie wäre es unter diesen Umständen mit einer tr
und einer while-Schleife:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Ausgabe:
data1 data2 data3
Kommentare
- Großartig.
REPLY
ist das Standardargumentname
für den integrierten Shell-Befehlread
. - Beachten Sie, dass dies erfordert, dass die Shell eine Standardvariable verwendet, um die Daten von
read
einzugeben, wasbash
zufällig tut , aber das ist nicht Standard. Außerdem kann Ihr Befehlread
die Daten ändern, wenn sie Backslashes enthalten, und flankierende Leerzeichen aus den Werten entfernen. Es würde auch Werte mit eingebetteten Zeilenumbrüchen als mehrere Werte lesen. Sie benötigen außerdem"$REPLY"
, um zu verhindern, dass die Shell den Wert aufteilt und eine Dateinamenerweiterung durchführt.
Antwort
awk kann akzeptieren sowohl j
(als Variable) als auch $j
(als Feldindex):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
im Beispiel“ verwirrt „awk
welche verwendet werden soll (Shell oder eigene Variable – Vorrang), da beide mit dem Präfix $
bezeichnet werden.
note
sh Shell, die Standard für „portable“ Skripte ist, unterstützt nicht:
(( i=1; i<=3; i++; ))
und <<< $var
Konstrukte
Sie können auch den Befehl seq
in for
Schleife für eine feinere Steuerung bei der Generierung von Zahlenfolgen, falls verfügbar.
Kommentare
- Ihre Schleife würde nur funktionieren, wenn zufällig drei Dateien aufgerufen würden ,
2
und3
im aktuellen Verzeichnis. Außerdem verwenden Sie die in der Shell nicht zitierte Variablenerweiterung, die unerwünschte Folgen haben kann, wenn die Daten Dateinamenmuster enthalten (z. B.*
). Dieecho
kann die Daten außerdem ändern, wenn sie Backslashes enthält.
Antwort
#!/bin/sh var="data1,data2,data3" unset data while [ "$var" != "$data" ]; do data=${var%%,*} # delete first comma and the bit after it var=${var#*,} # delete bit up to first comma (and the comma) printf "data = "%s"\n" "$data" done
Hier verwenden wir Variablensubstitutionen, um jedes aufeinanderfolgende durch Kommas getrennte Datenfeld aus dem Wert der Variablen var
zu erhalten. Die erste Zuweisung zu data
in der Schleife entfernt nach dem ersten Komma alles aus $var
. Die Variable var
wird dann so geändert, dass das erste Bit bis zum ersten Komma gelöscht wird.
Dies wird fortgesetzt, bis "$var" = "$data"
was bedeutet, dass mit der Zeichenfolge nichts mehr getan werden kann.
Auf diese Weise können wir durch Kommas getrennte Datenzeichenfolgen verarbeiten, die eingebettete Zeilenumbrüche enthalten:
var="line1 line2,data2,last bit goes here"
Mit den obigen Werten in var
würde das obige Skript
data = "line1 line2" data = "data2" data = "last bit goes here"
Keine Sorge um eingebettete Zeilenumbrüche; Sie müssen sehr selten Aufrufe von awk
durchlaufen.
Beachten Sie, dass awk
freut sich sehr, Ihre Zeichenfolge als eine Reihe von durch Kommas getrennten Feldern zu lesen und diese zu durchlaufen:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Mit var="data1,data2,data3"
, dies würde
data1 data2 data3
eine andere Shell-Lösung drucken, die die Variable zum Aufteilen des $var
-Werts in Bits, während set -f
zum Deaktivieren der Dateinamenerweiterung verwendet wird:
set -f oldIFS=$IFS; IFS="," set -- $var IFS=$oldIFS; unset oldIFS set +f for data do printf "data = "%s"\n" "$data" done