Jan Trippler schrieb:
On Sam, 03 Mai 2003 at 21:04 (+0200), Thomas Michalka wrote:
Jan Trippler schrieb:
[...]
Das obige Konstrukt lebt ja davon, dass dem awk quasi ein { print $2 } *untergeschoben* wird. Man kann dem awk aber auch Shell-Variablen übergeben:
jan@k500:~> echo pos1 pos2 pos3 | awk ' { print $pos } ' pos=2 pos2
Das ist doch keine Parameterübergabe im eigentlichen Sinn, also kein Argument für awk. Die Zuweisung pos=2 gehört doch zur Bash. *Bevor* awk letztlich aufgerufen wird, wird schon in der Shell 'pos' durch '2' ersetzt, oder sehe ich das falsch?
Hier habe ich das richtige gemeint ("kein Argument für awk", s.u.), es aber völlig falsch dargestellt :-\ Das lag daran, daß mir nicht klar war, daß awk als Argument nach dem Programmtext neben Eingabedateien auch Variablenzuweisungen akzeptiert, sogar _anstatt_ einer Eingabedatei - awk liest dann eben von stdin, wie im Beispiel mit der Pipe dargestellt. Also - ich _hab's_ jetzt - in pos=2, das ist die awk-Variable pos, die awk setzt wird bevor es den Programmtext in den {...} auf die Eingabe von stdin anwendet. Aber was in diesem Beispiel von Dir definitiv nicht der Fall sein kann, ist, daß hier eine Shell-Variable an awk übergeben wird, weil $pos hier gar nicht ausgewertet wird. Allerdings hast Du Dich hier wohl nur verschrieben, weil Du das durch Deine Beispiele mit dem in der bash freien $pos nachher selber widerlegst.
Schau Dir mal folgendes Konstrukt dazu an: jan@k500:~/tmp> echo pos1 pos2 pos3 | awk ' { print $pos; pos=3; print $pos; print '$pos', pos; } ' pos=2 pos2 pos3 2 3
Wenn ich vor dieser Zeile in der bash nicht pos=2 setzte, dann gibt es natürlich auch hier den parse error beim Komma (s. Dein Bsp. unten), weil das in der bash _sichtbare_ $pos nichts liefert, denn in pos steht ja kein Text, solange pos in der bash nicht erstmals gesetzt wird. Also hat das letzte print-Statement von awk kein erstes Argument, was es übelnimmt.
Es gibt einen Unterschied zwischen der awk-Variablen pos und der Shell-Variablen $pos, der letzte print zeigt das. Deshalb findet hier schon eine Parameter-Übergabe statt, es wird ja innerhalb des awk mit der awk- und nicht der Shell-Variablen gearbeitet. Das ist der Unterschied zur $'$pos' Variante, bei der ja praktisch der Teil '$pos' sich ausserhalb des awk befindet und durch die Shell ausgewertet wird.
Das gilt sowohl für $'$pos' als auch für '$pos', weil in *beiden* Fällen das $pos in der Kommandozeile für die bash ungeschützt zur Auswertung bereitsteht. Das erste $ in $'$pos' steht ja innerhalb des ersten Hochkomma-Paars (das Beispiel ist hier leider nicht mehr sichtbar) und wird deshalb erst von awk interpretiert. Nun möchte ich aber begründen, warum hier für mich - formal gesehen - *keine Parameterübergabe* stattfindet. Allerdings wird es jetzt kompliziert für mich den logischen Sachverhalt sprachlich korrekt auszudrücken. Aber ich will es versuchen ... Der Unterschied zwischen der awk-Variablen pos und der bash-Variablen pos besteht darin, daß erstere erst belegt (pos=2 am Schluß) wird, nachdem awk gestartet wurde, während die bash schon bei der Interpretation der Kommandozeile das Konstrukt $pos durch den Textinhalt der letzteren ersetzt, d.h. $pos wird durch eine 2 ersetzt, wenn pos=2 in der bash vorher gesetzt wurde (kleine Verwirrung wirklich _nur_ wegen der Namensgleichheit der Variablen). Zum Beginn des awk-Laufs gibt es also schon kein $pos mehr, awk sieht nur noch eine simple 2, und print 2 in awk ergibt nun mal ... Es wird also in diesem Beispiel innerhalb des awk - zumindest an dieser Stelle - überhaupt nicht mit einer Variablen gearbeitet, sondern mit einer (Text-)Konstante. Da diese, wie schon ausgeführt, durch Textersatz der bash eingeführt wurde, sehe ich hier keinesfalls eine Parameterübergabe, sondern so etwas wie den Textersatz im C-Präprozessor, eingeleitet durch ein #define.[1] Als bekräftigendes Gegenbeispiel sei angeführt, daß awk durchaus Parameter übergeben bekommt, z.B. die Zuweisung pos=2 (!) am Schluß der Kommandozeile. Dies ist deshalb so, weil dieser Parameter in das Array ARGV[] (wohl argv[] in main() ) von awk übergeben wird. Obwohl der Programmtext in den geschweiften Klanmmern auch ein solches Argument für awk ist, würde ich nicht so weit gehen, daß ich unter dem Ersatz von $pos durch die bash innerhalb des awk-Programmtexts auch nur eine indirekte Parameterübergabe verstehen wollte.
Die Zuweisung pos=2 ist keine bash-Zuweisung, sondern ein Argument des awk.
Ok, das habe ich inzwischen erkannt, s.o.
Gerade ist mir noch ein besseres Beispiel eingefallen: jan@k500:~/tmp> pos=1 jan@k500:~/tmp> echo pos1 pos2 pos3 | awk ' { print $pos; pos=3; print $pos; print '$pos', pos; } ' pos=2 pos2 pos3 1 3
Das ist das Beispiel von oben, bloß daß Du vorher die bash-Variable pos mit dem Text "2" füllst.
jan@k500:~/tmp> unset pos jan@k500:~/tmp> echo pos1 pos2 pos3 | awk ' { print $pos; pos=3; print $pos; print '$pos', pos; } ' pos=2 awk: cmd. line:1: { print $pos; pos=3; print $pos; print , pos; } awk: cmd. line:1: ^ parse error
Ist klar, weil bei der awk-Ausführung das letzte print-Statement kein erstes Argument vor dem Komma hat (sorry, ich wiederhole mich), da in der bash die Variable pos vorher geleert wurde.
Du siehst also, dass die awk-Variable pos und die Shell-Variable $pos zwei Paar Schuhe sind. Die Verwirrung wäre vielleicht nicht so groß gewesen, wenn ich die Variablen unterschiedlich genannt hätte ;-)
Ja vielleicht. Aber so wie die awk-Variable eine awk-Variable bleibt, so bleibt die Shell-Variable eine Shell-Variable (auch als Platzhalter für Text innerhalb eines awk-Arguments) und kann gerade wegen dieser fomalen Abgrenzung gar nicht "übergeben" werden. Das geht nur mit vollständigen und echten Argumenten für awk, aber die bash - und das ist nichts neues - übergibt an *jedes* Programm, das sie startet alle auf der Kommandozeile eingegebenen _Argumente_ als solche. An dieser Stelle des besseren Verständnisses endlich angelangt, möchte ich Dir, aber auch den anderen Thread-Teilnehmern ein gaaanz herzliches Dankeschön für diese - wohl vor allem für mich - äußerst fruchtbare und lehrreiche Diskussion aussprechen. Jetzt mache ich mich gleich daran, meine etwas unsauberen scripting-hacks, die ich zuletzt verbrochen habe, in eine ordentliche Form zu bringen - nachdem ich mich ausgeschlafen habe, sonst kommt doch nur Schrott dabei raus :-)) - und deshalb jetzt "aus die Maus" und schönen Gruß allerseits, Tom [1] BTW: Wenn man es sehr formal sieht, tut die bash nie etwas anderes, wenn der Interpreter auf ein Konstrukt der Art $VAR trifft. Allerdings kann man auch die Sichtweise vertreten, daß VAR als echte (Text-) Variable zu verstehen ist, und das $-Zeichen als Operator für den Lesezugriff fungiert, denn man schreibt ja allgemein VAR2=$VAR1. Interessanterweise wäre dann das =-Zeichen hinter einer Variablen (VAR2=) konsequent als Schreibzugriffsoperator zu interpretieren.