awk 'processing_script_here' my=file.txt
scheint anzuhalten und auf unbestimmte Zeit zu warten ...
Was ist hier los und wie lasse ich es funktionieren?
awk 'processing_script_here' my=file.txt
scheint anzuhalten und auf unbestimmte Zeit zu warten ...
Was ist hier los und wie lasse ich es funktionieren?
Antworten:
Wie Chris sagt , werden Argumente des Formulars variablename=anything
als Variablenzuweisung behandelt (die zum Zeitpunkt der Verarbeitung der Argumente ausgeführt wird, im Gegensatz zu den (neueren) Argumenten -v var=value
, die vor den BEGIN
Anweisungen ausgeführt werden), anstelle von Eingabedateinamen.
Das kann nützlich sein in Dingen wie:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Wo können Sie eine andere FS
/ RS
pro Datei angeben . Es wird auch häufig verwendet in:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Welches ist eine sicherere Version von:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(was nicht funktioniert wenn file1
leer ist)
Das stört jedoch, wenn Sie Dateien haben, deren Name =
Zeichen enthält .
Nun, das ist nur dann ein Problem, wenn vom ersten =
noch ein gültiger awk
Variablenname übrig ist .
Was einen gültigen Variablennamen in ausmacht, awk
ist strenger als in sh
.
POSIX erfordert Folgendes:
[_a-zA-Z][_a-zA-Z0-9]*
Mit nur Zeichen des portablen Zeichensatzes. Das /usr/xpg4/bin/awk
von Solaris 11 ist jedoch zumindest in dieser Hinsicht nicht kompatibel und lässt alle alphabetischen Zeichen im Gebietsschema in Variablennamen zu, nicht nur a-zA-Z.
Ein Argument wie x+y=foo
oder =bar
oder ./foo=bar
wird also immer noch als Eingabedateiname und nicht als Zuweisung behandelt, da das, was vom ersten übrig =
bleibt, kein gültiger Variablenname ist. Ein Argument wie Stéphane=Chazelas.txt
kann oder kann nicht, je nach awk
Implementierung und Gebietsschema.
Aus diesem Grund wird bei awk empfohlen, Folgendes zu verwenden:
awk '...' ./*.txt
anstatt
awk '...' *.txt
Zum Beispiel, um das Problem zu vermeiden, wenn Sie nicht garantieren können, dass der Name der txt
Dateien keine =
Zeichen enthält .
Beachten Sie auch, dass ein Argument wie -vfoo=bar.txt
dieses als Option behandelt werden kann, wenn Sie Folgendes verwenden:
awk -f file.awk -vfoo=bar.txt
(Gilt auch für awk '{code}' -vfoo=bar.txt
die awk
von Busybox-Versionen vor 1.28.0, siehe entsprechenden Fehlerbericht ).
Wiederum ./*.txt
funktioniert using um dieses Problem herum (die Verwendung eines ./
Präfix hilft auch bei einer aufgerufenen Datei, -
die ansonsten awk
als Standardeingabe interpretiert wird).
Das ist auch der Grund
#! /usr/bin/awk -f
Shebangs funktionieren nicht wirklich. Während var=value
diese durch Festlegen der ARGV
Werte (Hinzufügen eines ./
Präfixes) in einer BEGIN
Anweisung umgangen werden können:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Das hilft nicht bei den Optionen, da diese von awk
und nicht vom awk
Skript gesehen werden.
Ein potenzielles kosmetisches Problem bei der Verwendung dieses ./
Präfixes ist , dass es FILENAME
immer auftritt. Sie können es jedoch jederzeit substr(FILENAME, 3)
zum Entfernen verwenden, wenn Sie es nicht möchten.
Die GNU-Implementierung von awk
behebt alle diese Probleme mit ihrer -E
Option.
Danach -E
erwartet gawk nur den Pfad des awk
Skripts (wo -
immer noch stdin bedeutet) und dann nur eine Liste der Eingabedateipfade (und wird dort nicht einmal -
speziell behandelt).
Es wurde speziell entwickelt für:
#! /usr/bin/gawk -E
Shebangs, bei denen die Liste der Argumente immer Eingabedateien sind (beachten Sie, dass Sie diese ARGV
Liste in einer BEGIN
Anweisung noch bearbeiten können ).
Sie können es auch verwenden als:
gawk -e '...awk code here...' -E /dev/null *.txt
Wir verwenden -E
ein leeres Skript ( /dev/null
), um sicherzustellen, dass diese *.txt
anschließend immer als Eingabedateien behandelt werden, auch wenn sie =
Zeichen enthalten .
../foo
, /path/to/foo
und Pfade , die in einer anderen Kodierung) sind - in diesem Fall substr(FILENAME,3)
wird nicht genug sein, oder es ist ein One - Shot - Skript , in dem der Benutzer im Grunde weiß , was die Dateinamen - in diesem Fall sollte er / sie wahrscheinlich mit einem von ihnen , die nicht stören =
entweder ;-)
./
was ein Problem ist, aber dass es unter bestimmten Umständen unerwünscht sein kann, beispielsweise in Fällen, in denen Dateiname in der Ausgabe enthalten sein muss. In diesem Fall ./
sollten Sie redundant und unnötig sein muss es irgendwie loswerden. Hier ist mindestens ein Beispiel . Was den Benutzer anbelangt, der weiß, was Dateinamen sind - nun, in diesem Fall wissen wir auch, was Dateinamen sind, behindern aber =
dennoch die ordnungsgemäße Verarbeitung. So kann das Führen -
in die Quere kommen.
./
Präfix verwenden möchten, um diese awk
(falsche) Funktion zu umgehen, aber dann erhalten Sie eine ./
Ausgabe, die Sie möglicherweise entfernen möchten. Sehen Sie, wie Sie überprüfen, ob die erste Zeile der Datei eine bestimmte Zeichenfolge enthält? als Beispiel.
./
sondern auch der globale (absolute Pfad), /
der awk veranlasst, das Argument als Datei zu interpretieren.
In den meisten Versionen von awk lauten die Argumente nach dem auszuführenden Programm entweder:
x=y
Da Ihr Dateiname als Fall # 2 interpretiert wird, wartet awk immer noch auf etwas zum Lesen von stdin (da es nicht wahrnimmt, dass ein Dateiname übergeben wurde).
Portabel ist dieses Verhalten in POSIX dokumentiert :
Beide der folgenden Argumenttypen können miteinander vermischt werden:
- Datei: Ein Pfadname einer Datei, die die zu lesende Eingabe enthält, die mit den Mustern im Programm abgeglichen wird. Wenn keine Dateioperanden angegeben sind oder wenn ein Dateioperand '-' ist, muss die Standardeingabe verwendet werden.
- Zuweisung: Ein Operand, der mit einem Unterstrich oder einem alphabetischen Zeichen aus dem portablen Zeichensatz beginnt (siehe die Tabelle im Band Basisdefinitionen von IEEE Std 1003.1-2001, Abschnitt 6.1, Portabler Zeichensatz), gefolgt von einer Folge von Unterstrichen, Ziffern, Alphabetische Zeichen aus dem portablen Zeichensatz, gefolgt vom Zeichen "=", geben eine variable Zuweisung anstelle eines Pfadnamens an.
Als solches haben Sie portabel ein paar Möglichkeiten (Nr. 1 ist wahrscheinlich die am wenigsten aufdringliche):
awk ... ./my=file
, was dies umgeht, da .
es sich nicht um "einen Unterstrich oder ein alphabetisches Zeichen aus dem portablen Zeichensatz" handelt.awk ... < my=file
. Dies funktioniert jedoch nicht gut mit mehreren Dateien.ln my=file my_file
und dann my_file
wie gewohnt verwenden. Es wird kein Kopieren durchgeführt und beide Dateien werden mit denselben Daten und Inode-Metadaten gesichert. Nach der Verwendung kann der erstellte Link sicher entfernt werden, da die Anzahl der Verweise auf den Inode immer noch größer als 0 ist../my=file
funktioniert? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Dies sollte portierbar sein, da ./my
es sich nicht um einen gültigen Variablennamen handelt, und sollte daher nicht auf diese Weise analysiert werden.
=
vorangestellt ist (siehe die Tabelle im Band Basisdefinitionen von IEEE Std 1003.1-2001, Abschnitt 6.1, Portabler Zeichensatz). gefolgt von einer Folge von Unterstrichen, Ziffern und Buchstaben aus dem portablen Zeichensatz . also ein Dateipfad wie ++foo=bar.txt
oder =foo
oder ./foo=bar
ist alles OK als das .
oder +
ist nicht ein [_a-zA-Z]
.
./my=file
wird wörtlich durchgereicht.
awk '{print $1,$2}' /etc/passwd
. Der Punkt ist, dass es keinen Unterschied macht, ob die Shell die Datei öffnet, im Gegensatz zu awk, ob sie durchsuchbar ist oder nicht. Tatsächlich awk '{exit}' < /etc/passwd
würden Sie in erwarten awk
, bis zum Ende des ersten Datensatzes danach zu suchen exit
, um sicherzustellen, dass er die Position innerhalb von stdin dort belässt. POSIX benötigt das. /usr/xpg4/bin/awk
tut es auf Solaris, aber weder , gawk
noch mawk
scheint es auf GNU / Linux zu tun.
awk
diese Weise identifizierte Position .
So zitieren Sie die Gawk-Dokumentation (Hervorhebung hinzugefügt):
Alle zusätzlichen Argumente in der Befehlszeile werden normalerweise als Eingabedateien behandelt, die in der angegebenen Reihenfolge verarbeitet werden sollen. Ein Argument mit der Form var = value weist der Variablen var jedoch den Wert value zu - es gibt überhaupt keine Datei an.
Warum stoppt der Befehl und wartet? Da in dem Formular awk 'processing_script_here' my=file.txt
keine Datei angegeben ist, die durch die obige Definition my=file.txt
definiert wurde, awk
wird dies als Variablenzuweisung interpretiert, und wenn keine Datei definiert ist, wird stdin gelesen (dies strace
zeigt auch, dass awk in einem solchen Befehl auf read(0,'...)
syscall wartet .
Dies ist auch in den POSIX awk-Spezifikationen dokumentiert (siehe Abschnitt OPERANDS und dazugehörige Zuweisungen ).
Die Zuweisung von Variablen ist darin ersichtlich, awk '{print foo}' foo=bar /etc/passwd
dass der Wert von foo
für jede Zeile in / etc / passwd gedruckt wird. Das Angeben ./foo=bar
oder des vollständigen Pfads funktioniert jedoch.
Beachten Sie, dass Laufen strace
auf awk '1' foo=bar
sowie die Überprüfung mit cat foo=bar
zeigt , dass dies awk-spezifisches Problem, und execve Zeigt Dateinamen als Argument übergeben, so Schalen nichts mit env Variablenzuweisungen in diesem Fall zu tun.
Beachten Sie außerdem, dass awk '...script...' foo=bar
die Umgebungsvariablen nicht durch die Shell erstellt werden, da die Zuweisung von Umgebungsvariablen vor einem Befehl erfolgen sollte, damit sie wirksam wird. Siehe POSIX-Shell-Grammatikregeln , Punkt 7. Zusätzlich kann dies über überprüft werdenawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd