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=anythingals Variablenzuweisung behandelt (die zum Zeitpunkt der Verarbeitung der Argumente ausgeführt wird, im Gegensatz zu den (neueren) Argumenten -v var=value, die vor den BEGINAnweisungen 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/ RSpro 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 file1leer 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 awkVariablenname übrig ist .
Was einen gültigen Variablennamen in ausmacht, awkist strenger als in sh.
POSIX erfordert Folgendes:
[_a-zA-Z][_a-zA-Z0-9]*
Mit nur Zeichen des portablen Zeichensatzes. Das /usr/xpg4/bin/awkvon 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=foooder =baroder ./foo=barwird 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.txtkann oder kann nicht, je nach awkImplementierung 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 txtDateien keine =Zeichen enthält .
Beachten Sie auch, dass ein Argument wie -vfoo=bar.txtdieses als Option behandelt werden kann, wenn Sie Folgendes verwenden:
awk -f file.awk -vfoo=bar.txt
(Gilt auch für awk '{code}' -vfoo=bar.txtdie awkvon Busybox-Versionen vor 1.28.0, siehe entsprechenden Fehlerbericht ).
Wiederum ./*.txtfunktioniert using um dieses Problem herum (die Verwendung eines ./Präfix hilft auch bei einer aufgerufenen Datei, -die ansonsten awkals Standardeingabe interpretiert wird).
Das ist auch der Grund
#! /usr/bin/awk -f
Shebangs funktionieren nicht wirklich. Während var=valuediese durch Festlegen der ARGVWerte (Hinzufügen eines ./Präfixes) in einer BEGINAnweisung 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 awkund nicht vom awkSkript gesehen werden.
Ein potenzielles kosmetisches Problem bei der Verwendung dieses ./Präfixes ist , dass es FILENAMEimmer auftritt. Sie können es jedoch jederzeit substr(FILENAME, 3)zum Entfernen verwenden, wenn Sie es nicht möchten.
Die GNU-Implementierung von awkbehebt alle diese Probleme mit ihrer -EOption.
Danach -Eerwartet gawk nur den Pfad des awkSkripts (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 ARGVListe in einer BEGINAnweisung noch bearbeiten können ).
Sie können es auch verwenden als:
gawk -e '...awk code here...' -E /dev/null *.txt
Wir verwenden -Eein leeres Skript ( /dev/null), um sicherzustellen, dass diese *.txtanschließend immer als Eingabedateien behandelt werden, auch wenn sie =Zeichen enthalten .
../foo, /path/to/foound 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=yDa 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_fileund dann my_filewie 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 ./myes 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.txtoder =foooder ./foo=barist alles OK als das .oder +ist nicht ein [_a-zA-Z].
./my=filewird 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/passwdwü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/awktut es auf Solaris, aber weder , gawknoch mawkscheint es auf GNU / Linux zu tun.
awkdiese 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.txtdefiniert wurde, awkwird dies als Variablenzuweisung interpretiert, und wenn keine Datei definiert ist, wird stdin gelesen (dies stracezeigt 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/passwddass der Wert von foofür jede Zeile in / etc / passwd gedruckt wird. Das Angeben ./foo=baroder des vollständigen Pfads funktioniert jedoch.
Beachten Sie, dass Laufen straceauf awk '1' foo=barsowie die Überprüfung mit cat foo=barzeigt , 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=bardie 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