1 ## compare_shells: bash
2 ## oils_failures_allowed: 7
3
4 #### Lower Case with , and ,,
5 x='ABC DEF'
6 echo ${x,}
7 echo ${x,,}
8 echo empty=${empty,}
9 echo empty=${empty,,}
10 ## STDOUT:
11 aBC DEF
12 abc def
13 empty=
14 empty=
15 ## END
16
17 #### Upper Case with ^ and ^^
18 x='abc def'
19 echo ${x^}
20 echo ${x^^}
21 echo empty=${empty^}
22 echo empty=${empty^^}
23 ## STDOUT:
24 Abc def
25 ABC DEF
26 empty=
27 empty=
28 ## END
29
30 #### Case folding - Unicode characters
31
32 # https://www.utf8-chartable.de/unicode-utf8-table.pl
33
34 x=$'\u00C0\u00C8' # upper grave
35 y=$'\u00E1\u00E9' # lower acute
36
37 echo u ${x^}
38 echo U ${x^^}
39
40 echo l ${x,}
41 echo L ${x,,}
42
43 echo u ${y^}
44 echo U ${y^^}
45
46 echo l ${y,}
47 echo L ${y,,}
48
49 ## STDOUT:
50 u ÀÈ
51 U ÀÈ
52 l àÈ
53 L àè
54 u Áé
55 U ÁÉ
56 l áé
57 L áé
58 ## END
59
60 #### Case folding - multi code point
61
62 echo shell
63 small=$'\u00DF'
64 echo u ${small^}
65 echo U ${small^^}
66
67 echo l ${small,}
68 echo L ${small,,}
69 echo
70
71 echo python2
72 python2 -c '
73 small = u"\u00DF"
74 print(small.upper().encode("utf-8"))
75 print(small.lower().encode("utf-8"))
76 '
77 echo
78
79 # Not in the container images, but python 3 DOES support it!
80 # This is moved to demo/survey-case-fold.sh
81
82 if false; then
83 echo python3
84 python3 -c '
85 import sys
86 small = u"\u00DF"
87 sys.stdout.buffer.write(small.upper().encode("utf-8") + b"\n")
88 sys.stdout.buffer.write(small.lower().encode("utf-8") + b"\n")
89 '
90 fi
91
92 if false; then
93 # Yes, supported
94 echo node.js
95
96 nodejs -e '
97 var small = "\u00DF"
98 console.log(small.toUpperCase())
99 console.log(small.toLowerCase())
100 '
101 fi
102
103 ## STDOUT:
104 ## END
105 ## BUG bash STDOUT:
106 shell
107 u ß
108 U ß
109 l ß
110 L ß
111
112 python2
113 ß
114 ß
115
116 ## END
117
118 #### Case folding that depends on locale (not enabled, requires Turkish locale)
119
120 # Hm this works in demo/survey-case-fold.sh
121 # Is this a bash 4.4 thing?
122
123 #export LANG='tr_TR.UTF-8'
124 #echo $LANG
125
126 x='i'
127
128 echo u ${x^}
129 echo U ${x^^}
130
131 echo l ${x,}
132 echo L ${x,,}
133
134 ## OK bash/osh STDOUT:
135 u I
136 U I
137 l i
138 L i
139 ## END
140
141 #### Lower Case with constant string (VERY WEIRD)
142 x='AAA ABC DEF'
143 echo ${x,A}
144 echo ${x,,A} # replaces every A only?
145 ## STDOUT:
146 aAA ABC DEF
147 aaa aBC DEF
148 ## END
149
150 #### Lower Case glob
151
152 # Hm with C.UTF-8, this does no case folding?
153 export LC_ALL=en_US.UTF-8
154
155 x='ABC DEF'
156 echo ${x,[d-f]}
157 echo ${x,,[d-f]} # bash 4.4 fixed in bash 5.2.21
158 ## STDOUT:
159 ABC DEF
160 ABC DEF
161 ## END
162
163 #### ${x@u} U L - upper / lower case (bash 5.1 feature)
164
165 # https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
166
167 x='abc DEF'
168
169 echo "${x@u}"
170
171 echo "${x@U}"
172
173 echo "${x@L}"
174
175 ## STDOUT:
176 Abc DEF
177 ABC DEF
178 abc def
179 ## END
180
181
182 #### ${x@Q}
183 x="FOO'BAR spam\"eggs"
184 eval "new=${x@Q}"
185 test "$x" = "$new" && echo OK
186 ## STDOUT:
187 OK
188 ## END
189
190 #### ${array@Q} and ${array[@]@Q}
191 array=(x 'y\nz')
192 echo ${array[@]@Q}
193 echo ${array@Q}
194 echo ${array@Q}
195 ## STDOUT:
196 'x' 'y\nz'
197 'x'
198 'x'
199 ## END
200 ## OK osh STDOUT:
201 x $'y\\nz'
202 x
203 x
204 ## END
205
206 #### ${!prefix@} ${!prefix*} yields sorted array of var names
207 ZOO=zoo
208 ZIP=zip
209 ZOOM='one two'
210 Z='three four'
211
212 z=lower
213
214 argv.py ${!Z*}
215 argv.py ${!Z@}
216 argv.py "${!Z*}"
217 argv.py "${!Z@}"
218 for i in 1 2; do argv.py ${!Z*} ; done
219 for i in 1 2; do argv.py ${!Z@} ; done
220 for i in 1 2; do argv.py "${!Z*}"; done
221 for i in 1 2; do argv.py "${!Z@}"; done
222 ## STDOUT:
223 ['Z', 'ZIP', 'ZOO', 'ZOOM']
224 ['Z', 'ZIP', 'ZOO', 'ZOOM']
225 ['Z ZIP ZOO ZOOM']
226 ['Z', 'ZIP', 'ZOO', 'ZOOM']
227 ['Z', 'ZIP', 'ZOO', 'ZOOM']
228 ['Z', 'ZIP', 'ZOO', 'ZOOM']
229 ['Z', 'ZIP', 'ZOO', 'ZOOM']
230 ['Z', 'ZIP', 'ZOO', 'ZOOM']
231 ['Z ZIP ZOO ZOOM']
232 ['Z ZIP ZOO ZOOM']
233 ['Z', 'ZIP', 'ZOO', 'ZOOM']
234 ['Z', 'ZIP', 'ZOO', 'ZOOM']
235 ## END
236
237 #### ${!prefix@} matches var name (regression)
238 hello1=1 hello2=2 hello3=3
239 echo ${!hello@}
240 hello=()
241 echo ${!hello@}
242 ## STDOUT:
243 hello1 hello2 hello3
244 hello hello1 hello2 hello3
245 ## END
246
247 #### ${var@a} for attributes
248 array=(one two)
249 echo ${array@a}
250 declare -r array=(one two)
251 echo ${array@a}
252 declare -rx PYTHONPATH=hi
253 echo ${PYTHONPATH@a}
254
255 # bash and osh differ here
256 #declare -rxn x=z
257 #echo ${x@a}
258 ## STDOUT:
259 a
260 ar
261 rx
262 ## END
263
264 #### ${var@a} error conditions
265 echo [${?@a}]
266 ## STDOUT:
267 []
268 ## END
269
270 #### undef and @P @Q @a
271 $SH -c 'echo ${undef@P}'
272 echo status=$?
273 $SH -c 'echo ${undef@Q}'
274 echo status=$?
275 $SH -c 'echo ${undef@a}'
276 echo status=$?
277 ## STDOUT:
278
279 status=0
280
281 status=0
282
283 status=0
284 ## END
285
286
287 #### argv array and @P @Q @a
288 $SH -c 'echo ${@@P}' dummy a b c
289 echo status=$?
290 $SH -c 'echo ${@@Q}' dummy a 'b\nc'
291 echo status=$?
292 $SH -c 'echo ${@@a}' dummy a b c
293 echo status=$?
294 ## STDOUT:
295 a b c
296 status=0
297 'a' 'b\nc'
298 status=0
299
300 status=0
301 ## END
302
303 #### assoc array and @P @Q @a
304
305 # note: "y z" causes a bug!
306 $SH -c 'declare -A A=(["x"]="y"); echo ${A@P} - ${A[@]@P}'
307 echo status=$?
308
309 # note: "y z" causes a bug!
310 $SH -c 'declare -A A=(["x"]="y"); echo ${A@Q} - ${A[@]@Q}' | sed 's/^- y$/- '\''y'\''/'
311 echo status=$?
312
313 $SH -c 'declare -A A=(["x"]=y); echo ${A@a} - ${A[@]@a}'
314 echo status=$?
315 ## STDOUT:
316 - y
317 status=0
318 - 'y'
319 status=0
320 A - A
321 status=0
322 ## END
323
324 #### ${!var[@]@X}
325 # note: "y z" causes a bug!
326 $SH -c 'declare -A A=(["x"]="y"); echo ${!A[@]@P}'
327 if test $? -ne 0; then echo fail; fi
328
329 # note: "y z" causes a bug!
330 $SH -c 'declare -A A=(["x y"]="y"); echo ${!A[@]@Q}'
331 if test $? -ne 0; then echo fail; fi
332
333 $SH -c 'declare -A A=(["x"]=y); echo ${!A[@]@a}'
334 if test $? -ne 0; then echo fail; fi
335 # STDOUT:
336
337
338
339 # END
340
341 #### ${#var@X} is a parse error
342 # note: "y z" causes a bug!
343 $SH -c 'declare -A A=(["x"]="y"); echo ${#A[@]@P}'
344 if test $? -ne 0; then echo fail; fi
345
346 # note: "y z" causes a bug!
347 $SH -c 'declare -A A=(["x"]="y"); echo ${#A[@]@Q}'
348 if test $? -ne 0; then echo fail; fi
349
350 $SH -c 'declare -A A=(["x"]=y); echo ${#A[@]@a}'
351 if test $? -ne 0; then echo fail; fi
352 ## STDOUT:
353 fail
354 fail
355 fail
356 ## END
357
358 #### ${!A@a} and ${!A[@]@a}
359 declare -A A=(["x"]=y)
360 echo x=${!A[@]@a}
361 echo invalid=${!A@a}
362
363 # OSH prints 'a' for indexed array because the AssocArray with ! turns into
364 # it. Disallowing it would be the other reasonable behavior.
365
366 ## status: 1
367 ## STDOUT:
368 x=
369 ## END
370
371 # Bash succeeds with ${!A@a}, which references the variable named as $A (i.e.,
372 # ''). This must be a Bash bug since the behavior is inconsistent with the
373 # fact that ${!undef@a} and ${!empty@a} fail.
374
375 ## BUG bash status: 0
376 ## BUG bash STDOUT:
377 x=
378 invalid=
379 ## END
380
381 #### undef vs. empty string in var ops
382
383 empty=''
384 x=x
385
386 echo ${x@Q} ${empty@Q} ${undef@Q} ${x@Q}
387
388 echo ${x@K} ${empty@K} ${undef@K} ${x@K}
389
390 echo ${x@k} ${empty@k} ${undef@k} ${x@k}
391
392 echo ${x@A} ${empty@A} ${undef@A} ${x@A}
393
394 declare -r x
395 echo ${x@a} ${empty@a} ${undef@a} ${x@a}
396
397 # x x
398 #echo ${x@E} ${empty@E} ${undef@E} ${x@E}
399 # x x
400 #echo ${x@P} ${empty@P} ${undef@P} ${x@P}
401
402 ## STDOUT:
403 'x' '' 'x'
404 'x' '' 'x'
405 'x' '' 'x'
406 x='x' empty='' x='x'
407 r r
408 ## END
409
410 #### -o nounset with var ops
411
412 set -u
413 (echo ${undef@Q}); echo "stat: $?"
414 (echo ${undef@P}); echo "stat: $?"
415 (echo ${undef@a}); echo "stat: $?"
416
417 ## STDOUT:
418 stat: 1
419 stat: 1
420 stat: 1
421 ## END
422
423
424 #### ${a[0]@a} and ${a@a}
425
426 a=(1 2 3)
427 echo "attr = '${a[0]@a}'"
428 echo "attr = '${a@a}'"
429
430 ## STDOUT:
431 attr = 'a'
432 attr = 'a'
433 ## END
434
435
436 #### ${!r@a} with r='a[0]' (attribute for indirect expansion of an array element)
437
438 a=(1 2 3)
439 r='a'
440 echo ${!r@a}
441 r='a[0]'
442 echo ${!r@a}
443
444 declare -A d=([0]=foo [1]=bar)
445 r='d'
446 echo ${!r@a}
447 r='d[0]'
448 echo ${!r@a}
449
450 ## STDOUT:
451 a
452 a
453 A
454 A
455 ## END
456
457
458 #### Array expansion with nullary var op @Q
459 declare -a a=({1..9})
460 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
461
462 argv.py "${a[@]@Q}"
463 argv.py "${a[*]@Q}"
464 argv.py "${A[@]@Q}"
465 argv.py "${A[*]@Q}"
466 argv.py "${u[@]@Q}"
467 argv.py "${u[*]@Q}"
468
469 ## STDOUT:
470 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
471 ['1 2 3 4 5 6 7 8 9']
472 ['hello', 'world', 'osh', 'ysh']
473 ['hello world osh ysh']
474 []
475 ['']
476 ## END
477
478 ## OK bash STDOUT:
479 ["'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'", "'8'", "'9'"]
480 ["'1' '2' '3' '4' '5' '6' '7' '8' '9'"]
481 ["'ysh'", "'osh'", "'world'", "'hello'"]
482 ["'ysh' 'osh' 'world' 'hello'"]
483 []
484 ['']
485 ## END
486
487
488 #### Array expansion with nullary var op @P
489 declare -a a=({1..9})
490 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
491
492 argv.py "${a[@]@P}"
493 argv.py "${a[*]@P}"
494 argv.py "${A[@]@P}"
495 argv.py "${A[*]@P}"
496 argv.py "${u[@]@P}"
497 argv.py "${u[*]@P}"
498
499 ## STDOUT:
500 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
501 ['1 2 3 4 5 6 7 8 9']
502 ['hello', 'world', 'osh', 'ysh']
503 ['hello world osh ysh']
504 []
505 ['']
506 ## END
507
508 ## OK bash STDOUT:
509 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
510 ['1 2 3 4 5 6 7 8 9']
511 ['ysh', 'osh', 'world', 'hello']
512 ['ysh osh world hello']
513 []
514 ['']
515 ## END
516
517
518 #### Array expansion with nullary var op @a
519 declare -a a=({1..9})
520 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
521
522 argv.py "${a[@]@a}"
523 argv.py "${a[*]@a}"
524 argv.py "${A[@]@a}"
525 argv.py "${A[*]@a}"
526 argv.py "${u[@]@a}"
527 argv.py "${u[*]@a}"
528
529 ## STDOUT:
530 ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']
531 ['a a a a a a a a a']
532 ['A', 'A', 'A', 'A']
533 ['A A A A']
534 []
535 ['']
536 ## END