1 ## compare_shells: bash
2 ## oils_failures_allowed: 8
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 ## OK osh STDOUT:
341 fail
342 'x y'
343 a
344 ## END
345
346 #### ${#var@X} is a parse error
347 # note: "y z" causes a bug!
348 $SH -c 'declare -A A=(["x"]="y"); echo ${#A[@]@P}'
349 if test $? -ne 0; then echo fail; fi
350
351 # note: "y z" causes a bug!
352 $SH -c 'declare -A A=(["x"]="y"); echo ${#A[@]@Q}'
353 if test $? -ne 0; then echo fail; fi
354
355 $SH -c 'declare -A A=(["x"]=y); echo ${#A[@]@a}'
356 if test $? -ne 0; then echo fail; fi
357 ## STDOUT:
358 fail
359 fail
360 fail
361 ## END
362
363 #### ${!A@a} and ${!A[@]@a}
364 declare -A A=(["x"]=y)
365 echo x=${!A[@]@a}
366 echo invalid=${!A@a}
367
368 # OSH prints 'a' for indexed array because the AssocArray with ! turns into
369 # it. Disallowing it would be the other reasonable behavior.
370
371 ## status: 1
372 ## STDOUT:
373 x=
374 ## END
375
376 # Bash succeeds with ${!A@a}, which references the variable named as $A (i.e.,
377 # ''). This must be a Bash bug since the behavior is inconsistent with the
378 # fact that ${!undef@a} and ${!empty@a} fail.
379
380 ## BUG bash status: 0
381 ## BUG bash STDOUT:
382 x=
383 invalid=
384 ## END
385
386 #### undef vs. empty string in var ops
387
388 empty=''
389 x=x
390
391 echo ${x@Q} ${empty@Q} ${undef@Q} ${x@Q}
392
393 echo ${x@K} ${empty@K} ${undef@K} ${x@K}
394
395 echo ${x@k} ${empty@k} ${undef@k} ${x@k}
396
397 echo ${x@A} ${empty@A} ${undef@A} ${x@A}
398
399 declare -r x
400 echo ${x@a} ${empty@a} ${undef@a} ${x@a}
401
402 # x x
403 #echo ${x@E} ${empty@E} ${undef@E} ${x@E}
404 # x x
405 #echo ${x@P} ${empty@P} ${undef@P} ${x@P}
406
407 ## STDOUT:
408 'x' '' 'x'
409 'x' '' 'x'
410 'x' '' 'x'
411 x='x' empty='' x='x'
412 r r
413 ## END
414
415 #### -o nounset with var ops
416
417 set -u
418 (echo ${undef@Q}); echo "stat: $?"
419 (echo ${undef@P}); echo "stat: $?"
420 (echo ${undef@a}); echo "stat: $?"
421
422 ## STDOUT:
423 stat: 1
424 stat: 1
425 stat: 1
426 ## END
427
428
429 #### ${a[0]@a} and ${a@a}
430
431 a=(1 2 3)
432 echo "attr = '${a[0]@a}'"
433 echo "attr = '${a@a}'"
434
435 ## STDOUT:
436 attr = 'a'
437 attr = 'a'
438 ## END
439
440
441 #### ${!r@a} with r='a[0]' (attribute for indirect expansion of an array element)
442
443 a=(1 2 3)
444 r='a'
445 echo ${!r@a}
446 r='a[0]'
447 echo ${!r@a}
448
449 declare -A d=([0]=foo [1]=bar)
450 r='d'
451 echo ${!r@a}
452 r='d[0]'
453 echo ${!r@a}
454
455 ## STDOUT:
456 a
457 a
458 A
459 A
460 ## END
461
462
463 #### Array expansion with nullary var op @Q
464 declare -a a=({1..9})
465 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
466
467 argv.py "${a[@]@Q}"
468 argv.py "${a[*]@Q}"
469 argv.py "${A[@]@Q}"
470 argv.py "${A[*]@Q}"
471 argv.py "${u[@]@Q}"
472 argv.py "${u[*]@Q}"
473
474 ## STDOUT:
475 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
476 ['1 2 3 4 5 6 7 8 9']
477 ['hello', 'world', 'osh', 'ysh']
478 ['hello world osh ysh']
479 []
480 ['']
481 ## END
482
483 ## OK bash STDOUT:
484 ["'1'", "'2'", "'3'", "'4'", "'5'", "'6'", "'7'", "'8'", "'9'"]
485 ["'1' '2' '3' '4' '5' '6' '7' '8' '9'"]
486 ["'ysh'", "'osh'", "'world'", "'hello'"]
487 ["'ysh' 'osh' 'world' 'hello'"]
488 []
489 ['']
490 ## END
491
492
493 #### Array expansion with nullary var op @P
494 declare -a a=({1..9})
495 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
496
497 argv.py "${a[@]@P}"
498 argv.py "${a[*]@P}"
499 argv.py "${A[@]@P}"
500 argv.py "${A[*]@P}"
501 argv.py "${u[@]@P}"
502 argv.py "${u[*]@P}"
503
504 ## STDOUT:
505 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
506 ['1 2 3 4 5 6 7 8 9']
507 ['hello', 'world', 'osh', 'ysh']
508 ['hello world osh ysh']
509 []
510 ['']
511 ## END
512
513 ## OK bash STDOUT:
514 ['1', '2', '3', '4', '5', '6', '7', '8', '9']
515 ['1 2 3 4 5 6 7 8 9']
516 ['ysh', 'osh', 'world', 'hello']
517 ['ysh osh world hello']
518 []
519 ['']
520 ## END
521
522
523 #### Array expansion with nullary var op @a
524 declare -a a=({1..9})
525 declare -A A=(['a']=hello ['b']=world ['c']=osh ['d']=ysh)
526
527 argv.py "${a[@]@a}"
528 argv.py "${a[*]@a}"
529 argv.py "${A[@]@a}"
530 argv.py "${A[*]@a}"
531 argv.py "${u[@]@a}"
532 argv.py "${u[*]@a}"
533
534 ## STDOUT:
535 ['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']
536 ['a a a a a a a a a']
537 ['A', 'A', 'A', 'A']
538 ['A A A A']
539 []
540 ['']
541 ## END