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