ftblog

:: widerstand zwecklos ::
email jabber gpgkey
hackergotchi

June 28, 2006

Tooltime: zsh #8

Filed under: tooltime -- 22:28

zsh - parameter expansion flags

Das ist eines der mächtigsten Features der zsh.
Trotzdem nutzt kaum jemand die ganzen Möglichkeiten, die es alle gibt.
Ich gebe hier mal ein paar Beispiele, damit man sich vielleicht ein
klein wenig besser vorstellen kann, was es alles so gibt.

Als erstes die # bzw. (#) Flags:
Das einfache '#' ist keine Flag im eigentlichen Sinne, ich zeige es aber
kurz, da es leicht mit (#) verwechselt werden kann. Die beiden machen
nämlich grundverschiedene Dinge:

zsh% i="4*12+10"
zsh% print \"${i}\" \"${#i}\" \"${(#)i}\"
"4*12+10" "7" ":"
zsh% ascii ":"
  ASCII 3/10 is decimal 058, hex 3a, octal 072, bits 00111010: prints as `:'
  Official name: Colon


Was sagt uns das? Die Form ${#variable} gibt die Länge des Wertes von Variable
an (in diesem Fall ist $i 7 Zeichen lang.). Sollte $variable ein Array sein, so
gibt diese Expansion die Anzahl der Elemente im Array an.
Nun zur (#) Flag: 4*12+10 ist 58; wie die Ausgabe des ASCII Befehls zeigt, ist der
Dezimalwert des ':' Zeichens in ASCII Zeichencodierung die 58. Das heisst:
${(#)variable} berechnet den String $variable und gibt dann das entsprechende
Zeichen aus der ASCII Tabelle für das Ergebnis dieser Berechnung zurück.

Das (%) Flag:
Ganz einfach: man bekommt die gleichen Expansions, als wäre die Variable eine
Prompt Variable:

zsh% i='%~ - %D %% - Howdy :)'
zsh% print ${(%)i}
~ - 06-06-27 % - Howdy :)


Das (@) Flag:
Dieses Flag macht es möglich, trotz Double-Quotes seperate Worte bei der
Expansion von Arrays zu bekommen:

zsh% foo=( bar baz foo\ bar bar\ baz )
zsh% print -l "${foo}"
bar baz foo bar bar baz
zsh% print -l "${(@)foo}"
bar
baz
foo bar
bar baz


Die (A) bzw. (AA) Flags:
Es gibt ja direkte Variablenzuweisungen mit ${...::=...} oder ähnlich.
Diese beiden Flags sind dazu da, dass auch für Arrays und assoziative Arrays
zu ermöglichen:

zsh% print -l ${(A)foo::=foo bar baz}
foo bar baz
zsh% print ${#foo}
1


Hieran sieht man ein Feature, das eigentlich sehr nützlich ist, nämlich zshs
Wordsplitting, denn in diesem Fall hält die Shell "foo bar baz" für einen String.
Das möchte man auch fast immer (besonders bei Dateien mit Leerzeichen), aber
gerade hier möchte man das nicht. Daher schaltet man das Ganze in diesem Fall um
und zwar mit einem '=' Zeichen:

zsh% print -l ${(A)=foo::=foo bar baz}
foo
bar
baz
zsh% print ${#foo}
3


So, und nun das Ganze noch für assoziative Arrays (hier werden Werte "Key Value"
Paare benötigt):

zsh% print -l ${(AA)=foo::=foo0 bar foo1 baz}
bar
baz
zsh% print $foo[foo0] - $foo[foo1]
bar - baz
zsh% print ${#foo}
2


Das (c) Flag:
Das ist mal ganz einfach: Mit ${#...} gibt es bei Arrays einfach die Länge aller
Arrayelemente wieder, als wenn man die Elemente mit Spaces aneinander gehängt hat.

zsh% foo=( bar baz foobar )
zsh% print ${(c)#foo}
14


Das (C) Flag:
Auch einfach, das Flag bringt jeden Anfang der Worte in einem Array in
Grossbuchstabenform:

zsh% foo=( bar baz foobar )
zsh% print ${(C)foo}
Bar Baz Foobar


Das (e) Flag:
Das Flag ist ne Sau ;) - sie erzwingt nochmals alle Formen von Substitutionen.

zsh% foo='$(print $ZSH_VERSION)'
zsh% print ${(e)foo}
4.3.2
zsh% foo='print ${(c)#foo}'
zsh% print ${(e)foo}
print 16


Die (f) und (F) Flags:
Diese Flags sind kurzschreibweisen für 'ps:n:' bzw. 'pj:\n:'.
(f) kann man zB zum einlesen von Dateien benützen:

zsh% cat > file
Typing one line.
And another one.
zsh% foo=($(<file))
zsh% print -l $foo
Typing
one
line.
And
another
one.
zsh% foo=("$(<file)")
zsh% print -l $foo
Typing one line.
And another one.
zsh% print -l $foo[1]
Typing one line.
And another one.
zsh% print -l $foo[2]

zsh% foo=("${(f)$(<file)}")
zsh% print -l $foo
Typing one line.
And another one.
zsh% print -l $foo[1]
Typing one line.
zsh% print -l $foo[2]
And another one.


Mit (F) kann man die Elemente eines Arrays mit '\n' aneinander hängen:

zsh% foo=( foo bar baz )
zsh% print ${(F)foo}
foo
bar
baz


Die (k) und (v) Flags:
Wenn man ein assoziatives Array hat, bekommt man damit die Schlüssel und
nicht die Werte:

zsh% declare -A foo
zsh% foo=( foo bar foz baz )
zsh% print ${foo}
bar baz
zsh% print ${(k)foo}
foo foz
zsh% print ${${(k)foo}[1]}
foo
zsh% print ${${(k)foo}[2]}
foz


Zusammen mit (v) bekommt man beides:

zsh% declare -A foo
zsh% foo=( foo bar foz baz )
zsh% print ${(kv)foo}
foo bar foz baz


Die (L) und (U) Flags:
Konvertierung in Gross- bzw. Kleinbuchstaben:

zsh% foo=( wEirD wORDs )
zsh% print ${(U)foo}
WEIRD WORDS
zsh% print ${(L)foo}
weird words


Das (M) Flag:
Dieses Flag ist vor allen Dingen bei Patternmatching auf Arrayelemente
nützlich, da es das Matching Verhalten der ${...:#...} Operation umdreht:

zsh% setopt extended_glob
zsh% print -l ${path}
/home/hawk/bin
/bin
/usr/bin
/usr/local/bin
/opt/opera/bin
/usr/games
zsh% print -l ${path:#(*/)#bin[/]#}
/usr/games
zsh% print -l ${(M)path:#(*/)#bin[/]#}
/home/hawk/bin
/bin
/usr/bin
/usr/local/bin
/opt/opera/bin
zsh% print -l ${(M)path:#^(*/)#bin[/]#}
/usr/games


Das (P) Flag:
Interpretiert den Inhalt einer Variablen als Variablennamen:

zsh% foo=( wEirD wORDs )
zsh% bar="foo"
zsh% print ${(L)${(P)bar}}
weird words


Die (q) und (Q) Flags:
Fügt Quoting Arten hinzu:

zsh% foo='This is a damn parameter!'
zsh% echo ${(q)foo}
This\ is\ a\ damn\ parameter\!
zsh% print ${(qq)foo}
'This is a damn parameter!'
zsh% print ${(qqq)foo}
"This is a damn parameter!"
zsh% print ${(qqqq)foo}
$'This is a damn parameter!'


Manchmal brauch man sowas, wenn man Parameter mit 'eval' elvaluieren lassen will.
Das (Q) Flag arbeitet genau anders herum: Es nimmt ein Quoting Level weg.

Das (t) Flag:
Damit kann man sich den Typ eines Parameters anzeigen lassen:

zsh% foo='This is a damn parameter!'
zsh% print ${(t)foo}
scalar
zsh% foo=( foo bar baz )
zsh% print ${(t)foo}
array


Es gibt noch mehr Arten; eine Auflistung gibt's in zshexpn(1).

Das (u) Flag:
Falls Elemente eines Arrays mehrfach die gleichen Werte haben, bekommt man
durch dieses Flag immer genau einen Match zurückgeliefert:

zsh% foo=(foo foo bar foo baz bar foo bar)
zsh% print ${(u)foo}
foo bar baz


Das (V) Flag:
Dieses Flag ist quasi ein eingebautes 'cat -v':

zsh% foo=$'\e[35mHello World.'
zsh% print ${foo}
Hello World.
  (( würde auf Grund der Kontrollcodes in magenta eingefärbt. ))
zsh% print ${(V)foo}
^[[35mHello World.


Die (w) und (W) Flags:
Mit (w) kann man Worte zählen (und mit (s) sogar andere Zählweisen definieren).
(W) ist so ähnlich, zählt aber auch leere Worte mit:

zsh% foo=$'Hello World, mere mortal.'
zsh% print ${(w)#foo}
4
zsh% print ${(ws:,:)#foo}
2
zsh% foo=$'Hello World,, mere mortal.'
zsh% print ${(Ws:,:)#foo}
3


Das (X) Flag:
Damit kann man Fehlermeldungen für die (Q) und (e) Flags und für Patternmatching
der Art von ${...#...} erzwingen, anstelle sie zu ignorieren. Ich muss zugeben,
dass ich diese Flag selbst falsch verstanden habe. Bart Schaefer brachte Erleuchtung:

zsh% foo="two ' matched ' quotes"
zsh% print ${(Q)foo}
two matched quotes
zsh% foo="only one ' quote"
zsh% print ${(Q)foo}
only one ' quote
zsh% print ${(XQ)foo}
zsh: unmatched '


Das (z) Flag:
...splittet Skalare mit dem Shellparser:

zsh% foo="foo bar baz"
zsh% print ${${(z)foo}[2]}
bar


Die (o) und (O) Flags:
Hiermit kann man Arrays umordnen.
Die (a), (i) und (n) Flags spezifizieren dies näher:
(a): die eigentliche Array Sortierung, (i): "case-insensitive" und
(n): numerische Ordnung.

zsh% foo=( 03foo 01bAR 02BAZ 04baz 06bar 05Foo )
zsh% print ${(Oa)foo}
05Foo 06bar 04baz 02BAZ 01bAR 03foo
zsh% print ${(on)foo}
01bAR 02BAZ 03foo 04baz 05Foo 06bar
zsh% print ${(On)foo}
06bar 05Foo 04baz 03foo 02BAZ 01bAR


Das (j:...:) Flag::__
Hängt Arrayelemente aneinander:

zsh% foo=(foo bar baz)
zsh% print ${(j:.:)foo}
foo.bar.baz
zsh% print ${(j:_-_:)foo}
foo_-_bar_-_baz


Das (s:...:) Flag:
Das Gegenteil von (j):

zsh% foo="foo_-_bar_-_baz"
zsh% print -l ${(s:_-_:)foo}
foo
bar
baz


Das (l:expr::string1::string2:) Flag:
Auf der linken Seite "padden":

zsh% foo="foo"
zsh% bar="foo bar baz"
zsh% for i in foo bar ; do var="$i" ; print ${(l:20::.::/:)${(P)var}} ; done
................/foo
......../foo bar baz


Das (r:expr::string1::string2:) Flag:
Auf der rechten Seite padden:

zsh% foo="foo"
zsh% bar="foo bar baz"
zsh% for i in foo bar ; do var="$i" ; print ${(r:20::.:: :)${(P)var}} ; done
foo ................
foo bar baz ........


(p):
Dieses Flag bringt die (j), (s), (l) und (r) Flags dazu die vom print Builtin
bekannten Escape Sequenzen zu erkennen.


So, nun gibt es noch ein paar Flags die in ${...#...} und ${...%...} funkionieren.
Die (I) und (S) Flags funktionieren auch noch in der ${.../...} Form.

Das (S) Flag:
Suchen werden nicht nur am Anfang bzw am Ende festgenagelt:

zsh% string="Hampelmann Pampelmannnnnnn-"
zsh% print ${(S)string#am*lmann}
H Pampelmannnnnnn-
zsh% print ${(S)string##am*lmann}
Hnnnnn-
zsh% print ${(S)string%e*n}
Hampelmann Pampnnnnnn-
zsh% print ${(S)string%%e*n}
Hampelmann Pamp-


Das (I:n:) Flag:
Erweitert das (S) Flag; sucht nach dem n-ten Match. In der Manual Page
ist dazu ein nettes Beispiel:

zsh% string="which switch is the right switch for Ipswich?"
zsh% for i in {1..4} ; do print ${(SI:${i}:)string#w*ch} ; done
  switch is the right switch for Ipswich?
which s is the right switch for Ipswich?
which switch is the right s for Ipswich?
which switch is the right switch for Ips?


Die (B), (E), (M), (N) und (R) Flags:
Diese Flags beeinflussen die Ausgabe von (S) Matchings:
  (B) zeigt die Stelle an wo der gematchte Bereich beginnt.
  (E) wie (B), zeigt aber das Ende an.
  (M) gibt den gematchten Bereich aus.
  (N) gibt die Länge desgematchten Bereichs aus.
  (R) gibt den nicht gematchten Teil aus.

zsh% string="Hampelmann"
zsh% print -l ${(SBEMNR)string#am*lmann} ${(SB)string#am*lmann} \
  ${(SE)string#am*lmann} ${(SM)string#am*lmann} ${(SN)string#am*lmann} \
  ${(SR)string#am*lmann}
ampelmann H 2 11 9
2
11
ampelmann
9
H



Eine ganze Menge, oder etwa nicht? Und Beispiele tragen immer zum Verständnis
bei, wie ich finde. Ich denke, da bin ich nicht der einzige.

Als Rausschmeisser noch mal ein ganz harter, aus dem Guide von pws:

zsh% array=(a b c deee ehh eff g h iiiiiiiiiiii j k ll m )
zsh% print ${array[(r)${(l.${#${(O@)array//?/X}[1]}..?.)}]}
iiiiiiiiiiii


Das expandiert also zum längsten Array Element...
Wild? Ein wenig. ;)

Mit dem obigen Wissen ist das nicht zu lösen, da hier auch ein Subscript Flag
Anwendung findet [ das '(r)' ]. Wenn ich die hier auch noch ranhänge dann
wird es doch ein wenig zu lang. Beim nächsten Mal gibt's also eine Tooltime
zu Subscript Flags und dann erkläre ich auch mal wie das Ding da funktioniert.

Powered by zblog
valid css | valid xhtml | utf-8 encoded | best viewed with anybrowser