1 #!/bin/bash
2
3 ### String length
4 v=foo
5 echo ${#v}
6 # stdout: 3
7
8 ### Substring
9 v=abcde
10 echo ${v:1:3}
11 # stdout: bcd
12 # N-I dash status: 2
13 # N-I dash stdout-json: ""
14
15 ### Cannot take length of substring
16 # These are runtime errors, but we could make them parse time errors.
17 v=abcde
18 echo ${#v:1:3}
19 # status: 1
20 # N-I dash status: 0
21 # N-I dash stdout: 5
22
23 ### Pattern replacement
24 v=abcde
25 echo ${v/c*/XX}
26 # stdout: abXX
27 # N-I dash status: 2
28 # N-I dash stdout-json: ""
29
30 ### Remove smallest suffix
31 v=aabbccdd
32 echo ${v%c*}
33 # stdout: aabbc
34
35 ### Remove longest suffix
36 v=aabbccdd
37 echo ${v%%c*}
38 # stdout: aabb
39
40 ### Remove smallest prefix
41 v=aabbccdd
42 echo ${v#*b}
43 # stdout: bccdd
44
45 ### Remove longest prefix
46 v=aabbccdd
47 echo ${v##*b}
48 # stdout: ccdd
49
50 ### Default value when empty
51 empty=''
52 echo ${empty:-is empty}
53 # stdout: is empty
54
55 ### Default value when unset
56 echo ${unset-is unset}
57 # stdout: is unset
58
59 ### Assign default value when empty
60 empty=''
61 ${empty:=is empty}
62 echo $empty
63 # stdout: is empty
64
65 ### Assign default value when unset
66 ${unset=is unset}
67 echo $unset
68 # stdout: is unset
69
70 ### Alternative value when empty
71 v=foo
72 empty=''
73 echo ${v:+v is not empty} ${empty:+is not empty}
74 # stdout: v is not empty
75
76 ### Alternative value when unset
77 v=foo
78 echo ${v+v is not unset} ${unset:+is not unset}
79 # stdout: v is not unset
80
81 ### Error when empty
82 empty=''
83 ${empty:?is empty}
84 # status: 1
85 # OK dash status: 2
86
87 ### Error when unset
88 ${unset?is empty}
89 # status: 1
90 # OK dash status: 2
91
92 ### Error when unset
93 v=foo
94 echo ${v+v is not unset} ${unset:+is not unset}
95 # stdout: v is not unset
96
97 ### String slice
98 foo=abcdefg
99 echo ${foo:1:3}
100 # stdout: bcd
101 # N-I dash status: 2
102 # N-I dash stdout-json: ""
103
104 ### Negative string slice
105 foo=abcdefg
106 echo ${foo: -4:3}
107 # stdout: def
108 # N-I dash status: 2
109 # N-I dash stdout-json: ""
110
111 ### String slice with math
112 # I think this is the $(()) language inside?
113 i=1
114 foo=abcdefg
115 echo ${foo: i-3-2 : i + 2}
116 # stdout: def
117 # N-I dash status: 2
118 # N-I dash stdout-json: ""
119
120 ### Var ref with ${!a}
121 a=b
122 b=c
123 echo ref ${!a}
124 # Woah mksh has a completely different behavior -- var name, not var ref.
125 # stdout: ref c
126 # BUG mksh stdout: ref a
127 # N-I dash/zsh stdout-json: ""
128
129 ### Bad var ref with ${!a}
130 #set -o nounset
131 a='bad var name'
132 echo ref ${!a}
133 # Woah even dash implements this!
134 # stdout-json: "ref\n"
135 # BUG mksh stdout: ref a
136 # N-I dash/zsh stdout-json: ""
137
138 ### Local Var
139 # Oh this is interesting. Local vars in a function are visible to the function
140 # it calls. That is not how functions work! Functions are supposed to take
141 # params.
142 f() {
143 local f_var=5
144 g
145 }
146 g() {
147 local g_var=6
148 echo INNER $f_var $g_var
149 }
150 f
151 echo "OUTER" $f_var $g_var
152 # stdout-json: "INNER 5 6\nOUTER\n"
153
154 ### Nested ${}
155 bar=ZZ
156 echo ${foo:-${bar}}
157 # stdout: ZZ
158
159 ### Braced block inside ${}
160 # NOTE: This doesn't work in bash. The nested {} aren't parsed. It works in
161 # dash though!
162 # bash - line 1: syntax error near unexpected token `)'
163 # bash - line 1: `echo ${foo:-$({ which ls; })}'
164 # tag: bash-bug
165 echo ${foo:-$({ which ls; })}
166 # stdout: /bin/ls
167 # BUG bash stdout-json: ""
168 # BUG bash status: 2
169
170 ### Assigning $@ to var
171 # dash doesn't like this -- says '2' bad variable name.
172 # NOTE: bash and mksh support array variables! This is probably the
173 # difference. Need to test array semantics!
174 func() {
175 local v=$@
176 argv.py $v
177 }
178 func 1 2 3
179 # stdout: ['1', '2', '3']
180 # BUG dash status: 2
181 # BUG dash stdout-json: ""
182
183 ### Assigning "$@" to var
184 # dash doesn't like this -- says '2 3' bad variable name.
185 func() {
186 local v="$@"
187 argv.py $v
188 }
189 func 1 '2 3'
190 # stdout: ['1', '2', '3']
191 # BUG dash status: 2
192 # BUG dash stdout-json: ""
193
194 ### Assigning "$@" to var, then showing it quoted
195 # dash doesn't like this -- says '2 3' bad variable name.
196 func() {
197 local v="$@"
198 argv.py "$v"
199 }
200 func 1 '2 3'
201 # stdout: ['1 2 3']
202 # BUG dash status: 2
203 # BUG dash stdout-json: ""
204
205 ### Filename redirect with "$@"
206 # bash - ambiguous redirect -- yeah I want this error
207 # - But I want it at PARSE time? So is there a special DollarAtPart?
208 # MultipleArgsPart?
209 # mksh - tries to create '_tmp/var-sub1 _tmp/var-sub2'
210 # dash - tries to create '_tmp/var-sub1 _tmp/var-sub2'
211 func() {
212 echo hi > "$@"
213 }
214 func _tmp/var-sub1 _tmp/var-sub2
215 # status: 1
216 # OK dash status: 2
217
218 ### Filename redirect with split word
219 # bash - runtime error, ambiguous redirect
220 # mksh and dash - they will NOT apply word splitting after redirect, and write
221 # to '_tmp/1 2'
222 # Stricter behavior seems fine.
223 foo='_tmp/1 2'
224 rm '_tmp/1 2'
225 echo hi > $foo
226 test -f '_tmp/1 2' && cat '_tmp/1 2'
227 # status: 1
228 # OK dash/mksh status: 0
229 # OK dash/mksh stdout: hi
230
231 ### Descriptor redirect to bad "$@"
232 # All of them give errors:
233 # dash - bad fd number, parse error?
234 # bash - ambiguous redirect
235 # mksh - illegal file scriptor name
236 set -- '2 3' 'c d'
237 echo hi 1>& "$@"
238 # status: 2
239 # OK bash/mksh status: 1
240
241 ### Here doc with bad "$@" delimiter
242 # bash - syntax error
243 # dash - syntax error: end of file unexpected
244 # mksh - runtime error: here document unclosed
245 #
246 # What I want is syntax error: bad delimiter!
247 #
248 # This means that "$@" should be part of the parse tree then? Anything that
249 # involves more than one token.
250 func() {
251 cat << "$@"
252 hi
253 1 2
254 }
255 func 1 2
256 # status: 2
257 # stdout-json: ""
258 # OK mksh status: 1
259
260