# Test parameter expansion. Phew. # (By the way, did I say "phew"?) %prep mkdir parameter.tmp cd parameter.tmp touch boringfile evenmoreboringfile %test foo='the first parameter' bar='the second parameter' print -l $foo ${bar} 0:Basic scalar parameter substitution >the first parameter >the second parameter array1=(the first array) array2=(the second array) print -l $array1 ${array2} 0:Basic array parameter substitution >the >first >array >the >second >array setopt ksharrays print -l $array1 ${array2} unsetopt ksharrays 0:Basic ksharray substitution >the >the setopt shwordsplit print -l $foo ${bar} print -l ${==bar} unsetopt shwordsplit 0:Basic shwordsplit option handling >the >first >parameter >the >second >parameter >the second parameter print $+foo ${+foo} $+notappearinginthistest ${+notappearinginthistest} 0:$+... >1 1 0 0 x=() print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} x=(foo) print ${+x} ${+x[1]} ${+x[(r)foo]} ${+x[(r)bar]} 0:$+... with arrays >1 0 0 0 >1 1 1 0 set1=set1v null1= print ${set1:-set1d} ${set1-set2d} ${null1:-null1d} ${null1-null2d} x print ${unset1:-unset1d} ${unset1-unset2d} x 0:${...:-...} and ${...-...} >set1v set1v null1d x >unset1d unset2d x set2=irrelevant print ${set1:=set1d} ${set2::=set2d} print $set2 wasnull1= wasnull2= print ${wasnull1=wasnull1d} ${wasnull2:=wasnull2d} print $wasnull1 $wasnull2 0:${...:=...}, ${...::=...}, ${...=...} >set1v set2d >set2d >wasnull2d >wasnull2d (print ${set1:?okhere}; print ${unset1:?exiting1}; print not reached;) (print ${null1?okhere}; print ${null1:?exiting2}; print not reached;) 1:${...:?...}, ${...?...} >set1v > ?(eval):1: unset1: exiting1 ?(eval):2: null1: exiting2 print ${set1:+word1} ${set1+word2} ${null1:+word3} ${null1+word4} print ${unset1:+word5} ${unset1+word6} 0:${...:+...}, ${...+...} >word1 word2 word4 > str1='This is very boring indeed.' print ${str1#*s} print ${str1##*s} print $str1##s 0:${...#...}, ${...##...} > is very boring indeed. > very boring indeed. >This is very boring indeed.##s str2='If you'\''re reading this you should go and fix some bugs instead.' print ${str2%d*} print ${str2%%d*} 0:${...%...}, ${...%%...} >If you're reading this you should go and fix some bugs instea >If you're rea str1='does match' str2='does not match' print ${str1:#does * match} print ${str2:#does * match} 0:${...:#...} >does match > array1=(arthur boldly claws dogs every fight) print ${array1:#[aeiou]*} print ${(M)array1:#[aeiou]*} 0:${...:#...}, ${(M)...:#...} with array >boldly claws dogs fight >arthur every str1="$array1" print ${str1/[aeiou]*g/a braw bricht moonlicht nicht the nic} print ${(S)str1/[aeiou]*g/relishe} 0:scalar ${.../.../...}, ${(S).../.../...} >a braw bricht moonlicht nicht the nicht >relishes every fight print ${array1/[aeiou]*/Y} print ${(S)array1/[aeiou]*/Y} 0:array ${.../.../...}, ${(S).../.../...} >Y bY clY dY Y fY >Yrthur bYldly clYws dYgs Yvery fYght str1='o this is so, so so very dull' print ${str1//o*/Please no} print ${(S)str1//o*/Please no} 0:scalar ${...//.../...}, ${(S)...//.../...} >Please no >Please no this is sPlease no, sPlease no sPlease no very dull print ${array1//[aeiou]*/Y} print ${(S)array1//[aeiou]*/Y} 0:array ${...//.../...}, ${(S)...//.../...} >Y bY clY dY Y fY >YrthYr bYldly clYws dYgs YvYry fYght print ${array1:/[aeiou]*/expletive deleted} 0:array ${...:/...} >expletive deleted boldly claws dogs expletive deleted fight str1='a\string\with\backslashes' str2='a/string/with/slashes' print "${str1//\\/-}" print ${str1//\\/-} print "${str2//\//-}" print ${str2//\//-} 0:use of backslashes in //-substitutions >a-string-with-backslashes >a-string-with-backslashes >a-string-with-slashes >a-string-with-slashes args=('one' '#foo' '(bar' "'three'" two) mod=('#foo' '(bar' "'three'" sir_not_appearing_in_this_film) print ${args:|mod} print ${args:*mod} print "${(@)args:|mod}" print "${(@)args:*mod}" args=(two words) mod=('one word' 'two words') print "${args:|mod}" print "${args:*mod}" scalar='two words' print ${scalar:|mod} print ${scalar:*mod} print ${args:*nonexistent} empty= print ${args:*empty} 0:"|" array exclusion and "*" array intersection >one two >#foo (bar 'three' >one two >#foo (bar 'three' > >two words > >two words > > str1='twocubed' array=(the number of protons in an oxygen nucleus) print $#str1 ${#str1} "$#str1 ${#str1}" $#array ${#array} "$#array ${#array}" 0:${#...}, $#... >8 8 8 8 8 8 8 8 set 1 2 3 4 5 6 7 8 9 print ${##} set 1 2 3 4 5 6 7 8 9 10 print ${##} print ${##""} print ${##1} print ${##2} print ${###<->} # oh, for pete's sake... 0:${##} is length of $#, and other tales of hash horror >1 >2 >10 >0 >10 > array=(once bitten twice shy) print IF${array}THEN print IF${^array}THEN 0:basic ${^...} >IFonce bitten twice shyTHEN >IFonceTHEN IFbittenTHEN IFtwiceTHEN IFshyTHEN # Quote ${array} here because {...,...} doesn't like unquoted spaces. print IF{"${array}",THEN}ELSE print IF{${^array},THEN}ELSE 0:combined ${^...} and {...,...} >IFonce bitten twice shyELSE IFTHENELSE >IFonceELSE IFTHENELSE IFbittenELSE IFTHENELSE IFtwiceELSE IFTHENELSE IFshyELSE IFTHENELSE str1='one word' print -l $str1 ${=str1} "split ${=str1}wise" 0:${=...} >one word >one >word >split one >wordwise str1='*' print $str1 ${~str1} $~str1 setopt globsubst print $str1 unsetopt globsubst 0:${~...} and globsubst >* boringfile evenmoreboringfile boringfile evenmoreboringfile >boringfile evenmoreboringfile # The following tests a bug where globsubst didn't preserve # backslashes when printing out the original string. str1='\\*\\' ( setopt globsubst nonomatch [[ \\\\ = $str1 ]] && print -r '\\ matched by' $str1 [[ \\foo\\ = $str1 ]] && print -r '\\foo matched by' $str1 [[ a\\b\\ = $str1 ]] || print -r 'a\\b not matched by' $str1 ) 0:globsubst with backslashes >\\ matched by \\*\\ >\\foo matched by \\*\\ >a\\b not matched by \\*\\ ( setopt globsubst foo="boring*" print ${foo+$foo} print ${foo+"$foo"} print ${~foo+"$foo"} ) 0:globsubst together with nested quoted expansion >boringfile >boring* >boringfile print -l "${$(print one word)}" "${=$(print two words)}" 0:splitting of $(...) inside ${...} >one word >two >words (setopt shwordsplit # ensure this doesn't get set in main shell... test_splitting () { array="one two three" for e in $array; do echo "'$e'" done } test_split_var= echo _${test_split_var:=$(test_splitting)}_ echo "_${test_split_var}_") 0:SH_WORD_SPLIT inside $(...) inside ${...} >_'one' 'two' 'three'_ >_'one' >'two' >'three'_ print -l "${(f)$(print first line\\nsecond line\\nthird line)}" 0:${(f)$(...)} >first line >second line >third line array1=( uno ) print -l ${(A)newarray=splitting by numbers} print -l ${(t)newarray} print -l ${(A)=newarray::=splitting by spaces, actually} print -l ${(t)newarray} print -l ${(A)newarray::=$array1} print -l ${(t)newarray} print -l ${newarray::=$array1} print -l ${(t)newarray} print -l ${newarray::=$array2} print -l ${(t)newarray} 0:${(A)...=...}, ${(A)...::=...}, ${scalar=$array} >splitting by numbers >array >splitting >by >spaces, >actually >array >uno >array >uno >scalar >the second array >scalar newarray=("split me" "split me" "I\'m yours") print -l "${(@)newarray}" 0:"${(@)...}" >split me >split me >I'm yours foo='$(print Howzat usay)' print -l ${(e)foo} 0:${(e)...} >Howzat >usay foo='`print Howzat usay`' print -l ${(e)foo} 0:Regress ${(e)...} with backticks (see zsh-workers/15871) >Howzat >usay foo='\u65\123' print -r ${(g:o:)foo} foo='\u65\0123^X\C-x' print -r ${(g::)foo} foo='^X' bar='\C-\130' [[ ${(g:c:)foo} == ${(g:oe:)bar} ]] echo $? 0:${(g)...} >eS >eS^X\C-x >0 foo='I'\''m nearly out of my mind with tedium' bar=foo print ${(P)bar} 0:${(P)...} >I'm nearly out of my mind with tedium #' deconfuse emacs foo=(I could be watching that programme I recorded) print ${(o)foo} print ${(oi)foo} print ${(O)foo} print ${(Oi)foo} 0:${(o)...}, ${(O)...} >I I be could programme recorded that watching >be could I I programme recorded that watching >watching that recorded programme could be I I >watching that recorded programme I I could be foo=(yOU KNOW, THE ONE WITH wILLIAM dALRYMPLE) bar=(doing that tour of India.) print ${(L)foo} print ${(U)bar} 0:${(L)...}, ${(U)...} >you know, the one with william dalrymple >DOING THAT TOUR OF INDIA. foo='instead here I am stuck by the computer' print ${(C)foo} 0:${(C)...} >Instead Here I Am Stuck By The Computer foo=$'\x7f\x00' print ${(V)foo} 0:${(V)...} >^?^@ foo='playing '\''stupid'\'' "games" \w\i\t\h $quoting.' print -r ${(q)foo} print -r ${(qq)foo} print -r ${(qqq)foo} print -r ${(qqqq)foo} print -r ${(q-)foo} 0:${(q...)...} >playing\ \'stupid\'\ \"games\"\ \\w\\i\\t\\h\ \$quoting. >'playing '\''stupid'\'' "games" \w\i\t\h $quoting.' >"playing 'stupid' \"games\" \\w\\i\\t\\h \$quoting." >$'playing \'stupid\' "games" \\w\\i\\t\\h $quoting.' >'playing '\'stupid\'' "games" \w\i\t\h $quoting.' x=( a '' '\b' 'c d' '$e' ) print -r ${(q)x} print -r ${(q-)x} 0:Another ${(q...)...} test >a '' \\b c\ d \$e >a '' '\b' 'c d' '$e' print -r -- ${(q-):-foo} print -r -- ${(q-):-foo bar} print -r -- ${(q-):-"*(.)"} print -r -- ${(q-):-"wow 'this is cool' or is it?"} print -r -- ${(q-):-"no-it's-not"} 0:${(q-)...} minimal single quoting >foo >'foo bar' >'*(.)' >'wow '\''this is cool'\'' or is it?' >no-it\'s-not foo="'and now' \"even the pubs\" \\a\\r\\e shut." print -r ${(Q)foo} 0:${(Q)...} >and now even the pubs are shut. foo="X$'\x41'$'\x42'Y" print -r ${(Q)foo} 0:${(Q)...} with handling of $'...' >XABY # The following may look a bit random. # For the split we are checking that anything that # would normally be followed by a different word has # an argument break after it and anything that wouldn't doesn't. # For the (Q) we are simply checking that nothing disappears # in the parsing. foo=' {six} (seven) >eight< }nine{ |forty-two| $many$ )ten( more' array=(${(z)foo}) print -l ${(Q)array} 0:${(z)...} and ${(Q)...} for some hard to parse cases >< >five >> >{six} >( >seven >) >> >eight >< >}nine{ >| >forty-two >| >$many$ >) >ten( more strings=( 'foo=(1 2 3)' '(( 3 + 1 == 8 / 2 ))' 'for (( i = 1 ; i < 10 ; i++ ))' '((0.25542 * 60) - 15)*60' ) for string in $strings; do array=(${(z)string}) for (( i = 1; i <= ${#array}; i++ )); do print -r -- "${i}:${array[i]}:" done done 0:Some syntactical expressions that are hard to split into words with (z). >1:foo=(: >2:1: >3:2: >4:3: >5:): >1:(( 3 + 1 == 8 / 2 )): >1:for: >2:((: # Leading whitespace is removed, because the word proper hasn't started; # trailing whitespace is left because the word is terminated by the # semicolon or double parentheses. Bit confusing but sort of consistent. >3:i = 1 ;: >4:i < 10 ;: >5:i++ : >6:)): # This one needs resolving between a math expression and # a command, which causes interesting effects internally. >1:(: >2:(: >3:0.25542: >4:*: >5:60: >6:): >7:-: >8:15: >9:): >10:*60: line=$'A line with # someone\'s comment\nanother line # (1 more\nanother one' print "*** Normal ***" print -l ${(z)line} print "*** Kept ***" print -l ${(Z+c+)line} print "*** Removed ***" print -l ${(Z+C+)line} 0:Comments with (z) >*** Normal *** >A >line >with ># >someone's comment >another line # (1 more >another one >*** Kept *** >A >line >with ># someone's comment >; >another >line ># (1 more >; >another >one >*** Removed *** >A >line >with >; >another >line >; >another >one line='with comment # at the end' print -l ${(Z+C+)line} 0:Test we don't get an additional newline token >with >comment line=$'echo one\necho two # with a comment\necho three' print -l ${(Z+nc+)line} 0:Treating zplit newlines as ordinary whitespace >echo >one >echo >two ># with a comment >echo >three psvar=(dog) setopt promptsubst foo='It shouldn'\''t $(happen) to a %1v.' bar='But `echo what can you do\?`' print -r ${(%)foo} print -r ${(%%)bar} 0:${(%)...} >It shouldn't $(happen) to a dog. >But what can you do? foo='unmatched "' print ${(QX)foo} 1:${(QX)...} ?(eval):2: unmatched " # " deconfuse emacs array=(characters in an array) print ${(c)#array} 0:${(c)#...} >22 print ${(w)#array} str='colon::bolon::solon' print ${(ws.:.)#str} print ${(Ws.:.)#str} 0:${(w)...}, ${(W)...} >4 >3 >5 typeset -A assoc assoc=(key1 val1 key2 val2) print ${(o)assoc} print ${(ok)assoc} print ${(ov)assoc} print ${(okv)assoc} 0:${(k)...}, ${(v)...} >val1 val2 >key1 key2 >val1 val2 >key1 key2 val1 val2 word="obfuscatory" print !${(l.16.)word}! +${(r.16.)word}+ 0:simple padding >! obfuscatory! +obfuscatory + foo=(resulting words uproariously padded) print ${(pl.10..\x22..X.)foo} 0:${(pl...)...} >Xresulting """"Xwords roariously """Xpadded #" deconfuse emacs print ${(l.5..X.r.5..Y.)foo} print ${(l.6..X.r.4..Y.)foo} print ${(l.7..X.r.3..Y.)foo} print ${(l.6..X..A.r.6..Y..B.)foo} print ${(l.6..X..AROOGA.r.6..Y..BARSOOM.)foo} 0:simultaneous left and right padding >Xresulting XXXwordsYY proariousl XXpaddedYY >XXresultin XXXXwordsY uproarious XXXpaddedY >XXXresulti XXXXXwords Xuproariou XXXXpadded >XAresultingB XXXAwordsBYY uproariously XXApaddedBYY >GAresultingB OOGAwordsBAR uproariously OGApaddedBAR foo=(why in goodness name am I doing this) print ${(r.5..!..?.)foo} 0:${(r...)...} >why?! in?!! goodn name? am?!! I?!!! doing this? array=(I\'m simply putting a brave face on) print ${(j:--:)array} 0:${(j)...} >I'm--simply--putting--a--brave--face--on print ${(F)array} 0:${(F)...} >I'm >simply >putting >a >brave >face >on string='zometimez zis getz zplit on a z' print -l ${(s?z?)string} 0:${(s...)...} >ometime > >is get > >plit on a str=s arr=(a) typeset -A ass ass=(a a) integer i float f print ${(t)str} ${(t)arr} ${(t)ass} ${(t)i} ${(t)f} 0:${(t)...} >scalar array association-local integer-local float-local # it's not quite clear that these are actually right unless you know # the algorithm: search along the string for the point at which the # first (last) match occurs, for ## (%%), then take the shortest possible # version of that for # (%). it's as good a definition as anything. string='where is the white windmill, whispered walter wisely' print ${(S)string#h*e} print ${(S)string##h*e} print ${(S)string%h*e} print ${(S)string%%h*e} 0:${(S)...#...} etc. >wre is the white windmill, whispered walter wisely >wly >where is the white windmill, wred walter wisely >where is the white windmill, wly setopt extendedglob print ${(SI:1:)string##w[^[:space:]]# } print ${(SI:1+1:)string##w[^[:space:]]# } print ${(SI:1+1+1:)string##w[^[:space:]]# } print ${(SI:1+1+1+1:)string##w[^[:space:]]# } 0:${(I:...:)...} >is the white windmill, whispered walter wisely >where is the windmill, whispered walter wisely >where is the white whispered walter wisely >where is the white windmill, walter wisely print ${(MSI:1:)string##w[^[:space:]]# } 0:${(M...)...} >where print ${(R)string//w[a-z]# #} 0:${(R)...} >is the , # This (1) doesn't work with // or / # (2) perhaps ought to be 18, to be consistent with normal zsh # substring indexing and with backreferences. print ${(BES)string##white} 0:${(BE...)...} >14 19 print ${(NS)string##white} 0:${(N)...} >5 string='abcdefghijklmnopqrstuvwxyz' print ${${string%[aeiou]*}/(#m)?(#e)/${(U)MATCH}} 0:Rule 1: Nested substitutions >abcdefghijklmnopqrsT array=(et Swann avec cette muflerie intermittente) string="qui reparaissait chez lui" print ${array[4,5]} print ${array[4,5][1]} print ${array[4,5][1][2,3]} print ${string[4,5]} print ${string[4,5][1]} 0:Rule 2: Parameter subscripting >cette muflerie >cette >et > r > foo=stringalongamax print ${${(P)foo[1,6]}[1,3]} 0:Rule 3: Parameter Name Replacement >qui print "${array[5,6]}" print "${(j.:.)array[1,2]}" 0:Rule 4: Double-Quoted Joining >muflerie intermittente >et:Swann print "${${array}[5,7]}" print "${${(@)array}[1,2]}" 0:Rule 5: Nested Subscripting >wan >et Swann print "${${(@)array}[1,2]#?}" print "${(@)${(@)array}[1,2]#?}" 0:Rule 6: Modifiers >t Swann >t wann array=(she sells z shells by the z shore) (IFS='+'; print ${(s.s.)array}) 0:Rule 7: Forced Joining, and 8: Forced splitting >he+ ell +z+ hell +by+the+z+ hore setopt shwordsplit string='another poxy boring string' print -l ${${string}/o/ } unsetopt shwordsplit 0:Rule 9: Shell Word Splitting >an >ther >p >xy >b >ring >string setopt nonomatch foo='b* e*' print ${(e)~foo} print ${(e)~=foo} 0:Rule 10: Re-Evaluation >b* e* >boringfile evenmoreboringfile # ${bar} -> $bar here would yield "bad substitution". bar=confinement print ${(el.20..X.)${bar}} 0:Rule 11: Padding >XXXXXXXXXconfinement foo=(bar baz) bar=(ax1 bx1) print "${(@)${foo}[1]}" print "${${(@)foo}[1]}" print -l ${(s/x/)bar} print -l ${(j/x/s/x/)bar} print -l ${(s/x/)bar%%1*} 0:Examples in manual on parameter expansion >b >bar >a >1 b >1 >a >1 >b >1 >a > b set If "this test fails" "we have broken" the shell again print -l ${1+"$@"} 0:Regression test of ${1+"$@"} bug >If >this test fails >we have broken >the >shell >again set If "this test fails" "we have broken" the shell again print -l "${(A)foo::=$@}" print -l ${(t)foo} print -l $foo 0:Regression test of "${(A)foo=$@}" bug >If this test fails we have broken the shell again >array >If >this test fails >we have broken >the >shell >again local sure_that='sure that' varieties_of='varieties of' one=1 two=2 extra=(5 4 3) unset foo set Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace print -l ${=1+"$@"} print -l ${(A)=foo=Make $sure_that "this test keeps" on 'preserving all' "$varieties_of" quoted whitespace} print ${(t)foo} print -l ${=1+$one $two} print -l ${1+$extra$two$one} 0:Regression test of ${=1+"$@"} bug and some related expansions >Make >sure that >this test keeps >on >preserving all >varieties of >quoted >whitespace >Make >sure >that >this test keeps >on >preserving all >varieties of >quoted >whitespace >array >1 >2 >5 >4 >321 splitfn() { emulate -L sh local HOME="/differs from/bash" foo='1 2' bar='3 4' print -l ${1:-~} touch has\ space print -l ${1:-*[ ]*} print -l ${1:-*[\ ]*} print -l ${1:-*} print -l ${1:-"$foo" $bar} print -l ${==1:-$foo $bar} rm has\ space } splitfn 0:More bourne-shell-compatible nested word-splitting with wildcards and ~ >/differs from/bash >*[ >]* >has space >boringfile >evenmoreboringfile >has space >1 2 >3 >4 >1 2 3 4 splitfn() { local IFS=.- local foo=1-2.3-4 # print "Called with argument '$1'" print "No quotes" print -l ${=1:-1-2.3-4} ${=1:-$foo} print "With quotes on default argument only" print -l ${=1:-"1-2.3-4"} ${=1:-"$foo"} } print 'Using "="' splitfn splitfn 5.6-7.8 # splitfn() { emulate -L zsh setopt shwordsplit local IFS=.- local foo=1-2.3-4 # print "Called with argument '$1'" print "No quotes" print -l ${1:-1-2.3-4} ${1:-$foo} print "With quotes on default argument only" print -l ${1:-"1-2.3-4"} ${1:-"$foo"} } print Using shwordsplit splitfn splitfn 5.6-7.8 0:Test of nested word splitting with and without quotes >Using "=" >Called with argument '' >No quotes >1 >2 >3 >4 >1 >2 >3 >4 >With quotes on default argument only >1-2.3-4 >1-2.3-4 >Called with argument '5.6-7.8' >No quotes >5 >6 >7 >8 >5 >6 >7 >8 >With quotes on default argument only >5 >6 >7 >8 >5 >6 >7 >8 >Using shwordsplit >Called with argument '' >No quotes >1 >2 >3 >4 >1 >2 >3 >4 >With quotes on default argument only >1-2.3-4 >1-2.3-4 >Called with argument '5.6-7.8' >No quotes >5 >6 >7 >8 >5 >6 >7 >8 >With quotes on default argument only >5 >6 >7 >8 >5 >6 >7 >8 # Tests a long-standing bug with joining on metafied characters in IFS (array=(one two three) IFS=$'\0' foo="$array" for (( i = 1; i <= ${#foo}; i++ )); do char=${foo[i]} print $(( #char )) done) 0:Joining with NULL character from IFS >111 >110 >101 >0 >116 >119 >111 >0 >116 >104 >114 >101 >101 unset SHLVL (( SHLVL++ )) print $SHLVL 0:Unsetting and recreation of numerical special parameters >1 unset manpath print $+MANPATH manpath=(/here /there) print $MANPATH unset MANPATH print $+manpath MANPATH=/elsewhere:/somewhere print $manpath 0:Unsetting and recreation of tied special parameters >0 >/here:/there >0 >/elsewhere /somewhere local STRING=a:b typeset -T STRING string print $STRING $string unset STRING set -A string x y z print $STRING $string STRING=a:b typeset -T STRING string print $STRING $string unset STRING set -A string x y z print $STRING $string STRING=a:b typeset -T STRING string print $STRING $string unset string STRING=x:y:z print $STRING $string STRING=a:b typeset -T STRING string print $STRING $string unset string STRING=x:y:z print $STRING $string 0:Unsetting and recreation of tied normal parameters >a:b a b >x y z >a:b a b >x y z >a:b a b >x:y:z >a:b a b >x:y:z string='look for a match in here' if [[ ${string%%(#b)(match)*} = "look for a " ]]; then print $match[1] $mbegin[1] $mend[1] $string[$mbegin[1],$mend[1]] print $#match $#mbegin $#mend else print That didn\'t work. fi 0:Parameters associated with backreferences >match 12 16 match >1 1 1 #' deconfuse emacs string='and look for a MATCH in here' if [[ ${(S)string%%(#m)M*H} = "and look for a in here" ]]; then print $MATCH $MBEGIN $MEND $string[$MBEGIN,$MEND] print $#MATCH else print Oh, dear. Back to the drawing board. fi 0:Parameters associated with (#m) flag >MATCH 16 20 MATCH >5 string='this is a string' print ${string//(#m)s/$MATCH $MBEGIN $MEND} 0:(#m) flag with pure string >this 4 4 is 7 7 a s 11 11tring print ${${~:-*}//(#m)*/$MATCH=$MATCH} 0:(#m) flag with tokenized input >*=* print -l JAMES${(u)${=:-$(echo yes yes)}}JOYCE print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes)}}JOYCE 0:Bug with (u) flag reducing arrays to one element >JAMESyesJOYCE >JAMESyes >she >said >i >willJOYCE print -l JAMES${(u)${=:-$(echo yes yes she said yes i will yes she said she will and yes she did yes)}}JOYCE 0:New hash seive unique algorithm for arrays of more than 10 elements >JAMESyes >she >said >i >will >and >didJOYCE foo= print "${${foo}/?*/replacement}" 0:Quoted zero-length strings are handled properly > file=aleftkept print ${file//(#b)(*)left/${match/a/andsome}} print ${file//(#b)(*)left/${match//a/andsome}} 0:Substitutions where $match is itself substituted in the replacement >andsomekept >andsomekept file=/one/two/three/four print ${file:fh} print ${file:F.1.h} print ${file:F+2+h} print ${file:F(3)h} print ${file:F<4>h} print ${file:F{5}h} 0:Modifiers with repetition >/ >/one/two/three >/one/two >/one >/ >/ baz=foo/bar zab=oof+rab print ${baz:s/\//+/} print "${baz:s/\//+/}" print ${zab:s/+/\//} print "${zab:s/+/\//}" 0:Quoting of separator in substitution modifier >foo+bar >foo+bar >oof/rab >oof/rab bsbs='X\\\\Y' print -r -- ${bsbs:s/\\/\\/} print -r -- "${bsbs:s/\\/\\/}" print -r -- ${bsbs:s/\\\\/\\\\/} print -r -- "${bsbs:s/\\\\/\\\\/}" print -r -- ${bsbs:gs/\\/\\/} print -r -- "${bsbs:gs/\\/\\/}" print -r -- ${bsbs:gs/\\\\/\\\\/} print -r -- "${bsbs:gs/\\\\/\\\\/}" 0:Handling of backslashed backslashes in substitution modifier >X\\\\Y >X\\\\Y >X\\\\Y >X\\\\Y >X\\\\Y >X\\\\Y >X\\\\Y >X\\\\Y print -r ${${:-one/two}:s,/,X&Y,} print -r ${${:-one/two}:s,/,X\&Y,} print -r ${${:-one/two}:s,/,X\\&Y,} print -r "${${:-one/two}:s,/,X&Y,}" print -r "${${:-one/two}:s,/,X\&Y,}" print -r "${${:-one/two}:s,/,X\\&Y,}" 0:Quoting of ampersand in substitution modifier RHS >oneX/Ytwo >oneX&Ytwo >oneX\/Ytwo >oneX/Ytwo >oneX&Ytwo >oneX\/Ytwo nully=($'a\0c' $'a\0b\0b' $'a\0b\0a' $'a\0b\0' $'a\0b' $'a\0' $'a') for string in ${(o)nully}; do for (( i = 1; i <= ${#string}; i++ )); do foo=$string[i] printf "%02x" $(( #foo )) done print done 0:Sorting arrays with embedded nulls >61 >6100 >610062 >61006200 >6100620061 >6100620062 >610063 array=(X) patterns=("*X*" "spong" "a[b") for pat in $patterns; do print A${array[(r)$pat]}B C${array[(I)$pat]}D done 0:Bad patterns should never match array elements >AXB C1D >AB C0D >AB C0D foo=(a6 a117 a17 b6 b117 b17) print ${(n)foo} print ${(On)foo} 0:Numeric sorting >a6 a17 a117 b6 b17 b117 >b117 b17 b6 a117 a17 a6 x=sprodj x[-10]=scrumf print $x 0:Out of range negative scalar subscripts >scrumfsprodj a=(some sunny day) a[-10]=(we\'ll meet again) print -l $a 0:Out of range negative array subscripts >we'll >meet >again >some >sunny >day # ' emacs likes this close quote a=(sping spang spong bumble) print ${a[(i)spong]} print ${a[(i)spung]} print ${a[(ib.1.)spong]} print ${a[(ib.4.)spong]} print ${a[(ib.10.)spong]} 0:In and out of range reverse matched indices without and with b: arrays >3 >5 >3 >5 >5 a="thrimblewuddlefrong" print ${a[(i)w]} print ${a[(i)x]} print ${a[(ib.3.)w]} print ${a[(ib.10.)w]} print ${a[(ib.30.)w]} 0:In and out of range reverse matched indices without and with b: strings >9 >20 >9 >20 >20 foo="line:with::missing::fields:in:it" print -l ${(s.:.)foo} 0:Removal of empty fields in unquoted splitting >line >with >missing >fields >in >it foo="line:with::missing::fields:in:it" print -l "${(s.:.)foo}" 0:Hacky removal of empty fields in quoted splitting with no "@" >line >with >missing >fields >in >it foo="line:with::missing::fields:in:it:" print -l "${(@s.:.)foo}" 0:Retention of empty fields in quoted splitting with "@" >line >with > >missing > >fields >in >it > str=abcd print -l ${(s..)str} print -l "${(s..)str}" 0:splitting of strings into characters >a >b >c >d >a >b >c >d array=('%' '$' 'j' '*' '$foo') print ${array[(i)*]} "${array[(i)*]}" print ${array[(ie)*]} "${array[(ie)*]}" key='$foo' print ${array[(ie)$key]} "${array[(ie)$key]}" key='*' print ${array[(ie)$key]} "${array[(ie)$key]}" 0:Matching array indices with and without quoting >1 1 >4 4 >5 5 >4 4 # Ordering of associative arrays is arbitrary, so we need to use # patterns that only match one element. typeset -A assoc_r assoc_r=(star '*' of '*this*' and '!that!' or '(the|other)') print ${(kv)assoc_r[(re)*]} print ${(kv)assoc_r[(re)*this*]} print ${(kv)assoc_r[(re)!that!]} print ${(kv)assoc_r[(re)(the|other)]} print ${(kv)assoc_r[(r)*at*]} print ${(kv)assoc_r[(r)*(ywis|bliss|kiss|miss|this)*]} print ${(kv)assoc_r[(r)(this|that|\(the\|other\))]} 0:Reverse subscripting associative arrays with literal matching >star * >of *this* >and !that! >or (the|other) >and !that! >of *this* >or (the|other) print $ZSH_SUBSHELL (print $ZSH_SUBSHELL) ( (print $ZSH_SUBSHELL) ) ( (print $ZSH_SUBSHELL); print $ZSH_SUBSHELL ) print $(print $ZSH_SUBSHELL) cat =(print $ZSH_SUBSHELL) 0:ZSH_SUBSHELL >0 >1 >2 >2 >1 >1 >1 foo=("|" "?") [[ "|" = ${(j.|.)foo} ]] && print yes || print no [[ "|" = ${(j.|.)~foo} ]] && print yes || print no [[ "|" = ${(~j.|.)foo} ]] && print yes || print no [[ "|" = ${(~~j.|.)foo} ]] && print yes || print no [[ "|" = ${(j.|.~)foo} ]] && print yes || print no [[ "x" = ${(j.|.)foo} ]] && print yes || print no [[ "x" = ${(j.|.)~foo} ]] && print yes || print no [[ "x" = ${(~j.|.)foo} ]] && print yes || print no [[ "x" = ${(~~j.|.)foo} ]] && print yes || print no [[ "x" = ${(j.|.~)foo} ]] && print yes || print no 0:GLOBSUBST only on parameter substitution arguments >no >yes >yes >no >no >no >yes >no >no >no rcexbug() { emulate -L zsh setopt rcexpandparam local -A hash local -a full empty full=(X x) hash=(X x) print ORDINARY ARRAYS : The following behaves as documented in zshoptions print FULL expand=$full : Empty arrays remove the adjacent argument print EMPTY expand=$empty print ASSOCIATIVE ARRAY print Subscript flags returning many values print FOUND key=$hash[(I)X] val=$hash[(R)x] : This should behave like $empty, and does print LOST key=$hash[(I)y] val=$hash[(R)Y] print Subscript flags returning single values : Doc says "substitutes ... empty string" : so must not behave like an empty array print STRING key=$hash[(i)y] val=$hash[(r)Y] } rcexbug 0:Lookup failures on elements of arrays with RC_EXPAND_PARAM >ORDINARY ARRAYS >FULL expand=X expand=x >EMPTY >ASSOCIATIVE ARRAY >Subscript flags returning many values >FOUND key=X val=x >LOST >Subscript flags returning single values >STRING key= val= print $zsh_eval_context[1] [[ $ZSH_EVAL_CONTEXT = ${(j.:.)zsh_eval_context} ]] || print Not equal! (( icontext = ${#zsh_eval_context} + 1 )) contextfn() { print $(print $zsh_eval_context[icontext,-1]); } contextfn 0:$ZSH_EVAL_CONTEXT and $zsh_eval_context >toplevel >shfunc cmdsubst foo="123456789" print ${foo:3} print ${foo: 1 + 3} print ${foo:$(( 2 + 3))} print ${foo:$(echo 3 + 3)} print ${foo:3:1} print ${foo: 1 + 3:(4-2)/2} print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} print ${foo:$(echo 3 + 3):`echo 4 - 3`} print ${foo: -1} print ${foo: -10} print ${foo:5:-2} 0:Bash-style offsets, scalar >456789 >56789 >6789 >789 >4 >5 >6 >7 >9 >123456789 >67 foo=(1 2 3 4 5 6 7 8 9) print ${foo:3} print ${foo: 1 + 3} print ${foo:$(( 2 + 3))} print ${foo:$(echo 3 + 3)} print ${foo:3:1} print ${foo: 1 + 3:(4-2)/2} print ${foo:$(( 2 + 3)):$(( 7 - 6 ))} print ${foo:$(echo 3 + 3):`echo 4 - 3`} print ${foo: -1} print ${foo: -10} print ${foo:5:-2} 0:Bash-style offsets, array >4 5 6 7 8 9 >5 6 7 8 9 >6 7 8 9 >7 8 9 >4 >5 >6 >7 >9 >1 2 3 4 5 6 7 8 9 >6 7 testfn() { emulate -L sh set -A foo 1 2 3 set -- 1 2 3 str=abc echo ${foo[*]:0:1} echo ${foo[*]:1:1} echo ${foo[*]: -1:1} : echo ${*:0:1} echo ${*:1:1} echo ${*: -1:1} : echo ${str:0:1} echo ${str:1:1} echo ${str: -1:1} } testfn 0:Bash-style offsets, Bourne-style indexing >1 >2 >3 >testfn >1 >3 >a >b >c printf "%n" '[0]' 1:Regression test for identifier test ?(eval):1: not an identifier: [0] str=rts print ${str:0:} 1:Regression test for missing length after offset ?(eval):2: unrecognized modifier foo="123456789" print ${foo:5:-6} 1:Regression test for total length < 0 in string ?(eval):2: substring expression: 3 < 5 foo=(1 2 3 4 5 6 7 8 9) print ${foo:5:-6} 1:Regression test for total length < 0 in array ?(eval):2: substring expression: 3 < 5 foo=(${(0)"$(print -n)"}) print ${#foo} 0:Nularg removed from split empty string >0 (set -- a b c setopt shwordsplit IFS= print -rl "$*" unset IFS print -rl "$*") 0:Regression test for shwordsplit with null or unset IFS and quoted array >abc >a b c foo= print ${foo:wq} print ${:wq} 0:Empty parameter should not cause modifiers to crash the shell > > # This used to cause uncontrolled behaviour, but at best # you got the wrong output so the check is worth it. args() { print $#; } args ${:*} args ${:|} 0:Intersection and disjunction with empty parameters >0 >0 foo=(a b c) bar=(1 2 3) print ${foo:^bar} print ${foo:^^bar} foo=(a b c d) bar=(1 2) print ${foo:^bar} print ${foo:^^bar} foo=('a a' b) bar=(1 '2 2') print -l "${foo:^bar}" print -l "${(@)foo:^bar}" 0:Zipping arrays, correct output >a 1 b 2 c 3 >a 1 b 2 c 3 >a 1 b 2 >a 1 b 2 c 1 d 2 # maybe this should be changed to output "a a b 1" >a a b >1 >a a >1 >b >2 2 foo=(a b c) bar=() print ${foo:^bar} print ${foo:^^bar} print ${bar:^foo} print ${bar:^^foo} print ${bar:^bar} print ${bar:^^bar} 0:Zipping arrays, one or both inputs empty > >a b c > >a b c > > foo=text bar=() print ${foo:^bar} print ${bar:^^foo} bar=other print ${foo:^bar} bar=(array elements) print ${foo:^bar} print ${foo:^^bar} print ${bar:^foo} print ${bar:^^foo} 0:Zipping arrays, scalar input > >text >text other >text array >text array text elements >array text >array text elements text foo=(a b c) print ${foo:^^^bar} 1:Zipping arrays, parsing ?(eval):2: not an identifier: ^bar (setopt nounset print ${foo:^noexist}) 1:Zipping arrays, NO_UNSET part 1 ?(eval):2: noexist: parameter not set (setopt nounset print ${noexist:^foo}) 1:Zipping arrays, NO_UNSET part 2 ?(eval):2: noexist: parameter not set expr="a@b,c@d:e@f,g@h:i@j,k@l" for sep in : , @; do print -l ${(ps.$sep.)expr} done 0:Use of variable to get separator when splitting parameter >a@b,c@d >e@f,g@h >i@j,k@l >a@b >c@d:e@f >g@h:i@j >k@l >a >b,c >d:e >f,g >h:i >j,k >l SHLVL=1 $ZTST_testdir/../Src/zsh -fc 'echo $SHLVL' $ZTST_testdir/../Src/zsh -fc '(echo $SHLVL)' 0:SHLVL appears sensible when about to exit shell >2 >2 # The following tests the return behaviour of parsestr/parsestrnoerr alias param-test-alias='print $'\''\x45xpanded in substitution'\' param='$(param-test-alias)' print ${(e)param} 0:Alias expansion in command substitution in parameter evaluation >Expanded in substitution a=1 b=2 c=3 : One; function { : Two echo $_ print -l $argv } $_ Three print -l $_ Four; 0:$_ with anonymous function >Two >One >Three >Three >Four a=1 b=2 c=3 : One function { : Two echo $_ print -l $argv } print -l "$_" Four 0:$_ with anonymous function without arguments >Two > > >Four