1 # printf
2 # bash-completion uses this odd printf -v construction. It seems to mostly use
3 # %s and %q though.
4 #
5 # %s should just be
6 # declare $var='val'
7 #
8 # NOTE:
9 # /usr/bin/printf %q "'" seems wrong.
10 # $ /usr/bin/printf %q "'"
11 # ''\'''
12 #
13 # I suppose it is technically correct, but it looks very ugly.
14
15 #### printf with no args
16 printf
17 ## status: 2
18 ## OK mksh/zsh status: 1
19 ## stdout-json: ""
20
21 #### printf -v %s
22 var=foo
23 printf -v $var %s 'hello there'
24 argv.py "$foo"
25 ## STDOUT:
26 ['hello there']
27 ## END
28 ## N-I mksh/zsh/ash STDOUT:
29 -v['']
30 ## END
31 ## N-I dash STDOUT:
32 ['']
33 ## END
34
35 #### printf -v %q
36 val='"quoted" with spaces and \'
37
38 # quote 'val' and store it in foo
39 printf -v foo %q "$val"
40 # then round trip back to eval
41 eval "bar=$foo"
42
43 # debugging:
44 #echo foo="$foo"
45 #echo bar="$bar"
46 #echo val="$val"
47
48 test "$bar" = "$val" && echo OK
49 ## STDOUT:
50 OK
51 ## END
52 ## N-I mksh/zsh/ash stdout-json: "-v"
53 ## N-I mksh/zsh/ash status: 1
54 ## N-I dash stdout-json: ""
55 ## N-I dash status: 1
56
57 #### printf -v a[1]
58 shopt -s eval_unsafe_arith
59 a=(a b c)
60 printf -v 'a[1]' %s 'foo'
61 echo status=$?
62 argv.py "${a[@]}"
63 ## STDOUT:
64 status=0
65 ['a', 'foo', 'c']
66 ## END
67 ## N-I mksh/zsh STDOUT:
68 -vstatus=0
69 ['a', 'b', 'c']
70 ## END
71 ## N-I dash/ash stdout-json: ""
72 ## N-I dash/ash status: 2
73
74 #### printf -v syntax error
75 shopt -s eval_unsafe_arith
76 printf -v 'a[' %s 'foo'
77 echo status=$?
78 ## STDOUT:
79 status=2
80 ## END
81 ## N-I ash/mksh/zsh stdout: -vstatus=0
82
83 #### dynamic declare instead of %s
84 var=foo
85 declare $var='hello there'
86 argv.py "$foo"
87 ## STDOUT:
88 ['hello there']
89 ## END
90 ## N-I dash/mksh/ash STDOUT:
91 ['']
92 ## END
93
94 #### dynamic declare instead of %q
95 var=foo
96 val='"quoted" with spaces and \'
97 # I think this is bash 4.4 only.
98 declare $var="${val@Q}"
99 echo "$foo"
100 ## STDOUT:
101 '"quoted" with spaces and \'
102 ## END
103 ## OK osh STDOUT:
104 $'"quoted" with spaces and \\'
105 ## END
106 ## N-I dash/ash stdout-json: ""
107 ## N-I dash/ash status: 2
108 ## N-I mksh stdout-json: "\n"
109 ## N-I zsh stdout-json: ""
110 ## N-I zsh status: 1
111
112 #### printf -v dynamic scope
113 case $SH in mksh|zsh|dash|ash) echo not implemented; exit ;; esac
114 # OK so printf is like assigning to a var.
115 # printf -v foo %q "$bar" is like
116 # foo=${bar@Q}
117 dollar='dollar'
118 f() {
119 local mylocal=foo
120 printf -v dollar %q '$' # assign foo to a quoted dollar
121 printf -v mylocal %q 'mylocal'
122 echo dollar=$dollar
123 echo mylocal=$mylocal
124 }
125 echo dollar=$dollar
126 echo --
127 f
128 echo --
129 echo dollar=$dollar
130 echo mylocal=$mylocal
131 ## STDOUT:
132 dollar=dollar
133 --
134 dollar=\$
135 mylocal=mylocal
136 --
137 dollar=\$
138 mylocal=
139 ## END
140 ## OK osh STDOUT:
141 dollar=dollar
142 --
143 dollar='$'
144 mylocal=mylocal
145 --
146 dollar='$'
147 mylocal=
148 ## END
149 ## N-I dash/ash/mksh/zsh STDOUT:
150 not implemented
151 ## END
152
153 #### printf with too few arguments
154 printf -- '-%s-%s-%s-\n' 'a b' 'x y'
155 ## STDOUT:
156 -a b-x y--
157 ## END
158
159 #### printf with too many arguments
160 printf -- '-%s-%s-\n' a b c d e
161 ## STDOUT:
162 -a-b-
163 -c-d-
164 -e--
165 ## END
166
167 #### printf width strings
168 printf '[%5s]\n' abc
169 printf '[%-5s]\n' abc
170 ## STDOUT:
171 [ abc]
172 [abc ]
173 ## END
174
175 #### printf integer
176 printf '%d\n' 42
177 printf '%i\n' 42 # synonym
178 printf '%d\n' \'a # if first character is a quote, use character code
179 printf '%d\n' \"a # double quotes work too
180 printf '[%5d]\n' 42
181 printf '[%-5d]\n' 42
182 printf '[%05d]\n' 42
183 #printf '[%-05d]\n' 42 # the leading 0 is meaningless
184 #[42 ]
185 ## STDOUT:
186 42
187 42
188 97
189 97
190 [ 42]
191 [42 ]
192 [00042]
193 ## END
194
195 #### printf %6.4d -- "precision" does padding for integers
196 printf '[%6.4d]\n' 42
197 printf '[%.4d]\n' 42
198 printf '[%6.d]\n' 42
199 echo --
200 printf '[%6.4d]\n' -42
201 printf '[%.4d]\n' -42
202 printf '[%6.d]\n' -42
203 ## STDOUT:
204 [ 0042]
205 [0042]
206 [ 42]
207 --
208 [ -0042]
209 [-0042]
210 [ -42]
211 ## END
212
213 #### printf %6.4x X o
214 printf '[%6.4x]\n' 42
215 printf '[%.4x]\n' 42
216 printf '[%6.x]\n' 42
217 echo --
218 printf '[%6.4X]\n' 42
219 printf '[%.4X]\n' 42
220 printf '[%6.X]\n' 42
221 echo --
222 printf '[%6.4o]\n' 42
223 printf '[%.4o]\n' 42
224 printf '[%6.o]\n' 42
225 ## STDOUT:
226 [ 002a]
227 [002a]
228 [ 2a]
229 --
230 [ 002A]
231 [002A]
232 [ 2A]
233 --
234 [ 0052]
235 [0052]
236 [ 52]
237 ## END
238
239 #### %06d zero padding vs. %6.6d
240 printf '[%06d]\n' 42
241 printf '[%06d]\n' -42 # 6 TOTAL
242 echo --
243 printf '[%6.6d]\n' 42
244 printf '[%6.6d]\n' -42 # 6 + 1 for the - sign!!!
245 ## STDOUT:
246 [000042]
247 [-00042]
248 --
249 [000042]
250 [-000042]
251 ## END
252
253 #### %06x %06X %06o
254 printf '[%06x]\n' 42
255 printf '[%06X]\n' 42
256 printf '[%06o]\n' 42
257 ## STDOUT:
258 [00002a]
259 [00002A]
260 [000052]
261 ## END
262
263 #### %06s is no-op
264 printf '(%6s)\n' 42
265 printf '(%6s)\n' -42
266 printf '(%06s)\n' 42
267 printf '(%06s)\n' -42
268 echo status=$?
269 ## STDOUT:
270 ( 42)
271 ( -42)
272 ( 42)
273 ( -42)
274 status=0
275 ## END
276 # mksh is stricter
277 ## OK mksh STDOUT:
278 ( 42)
279 ( -42)
280 ((status=1
281 ## END
282
283 #### printf %6.4s does both truncation and padding
284 printf '[%6s]\n' foo
285 printf '[%6.4s]\n' foo
286 printf '[%-6.4s]\n' foo
287 printf '[%6s]\n' spam-eggs
288 printf '[%6.4s]\n' spam-eggs
289 printf '[%-6.4s]\n' spam-eggs
290 ## STDOUT:
291 [ foo]
292 [ foo]
293 [foo ]
294 [spam-eggs]
295 [ spam]
296 [spam ]
297 ## END
298
299 #### printf %6.0s and %0.0s
300 printf '[%6.0s]\n' foo
301 printf '[%0.0s]\n' foo
302 ## STDOUT:
303 [ ]
304 []
305 ## END
306 ## N-I mksh stdout-json: "[ ]\n["
307 ## N-I mksh status: 1
308
309 #### printf %6.s and %0.s
310 printf '[%6.s]\n' foo
311 printf '[%0.s]\n' foo
312 ## STDOUT:
313 [ ]
314 []
315 ## END
316 ## BUG zsh STDOUT:
317 [ foo]
318 [foo]
319 ## END
320 ## N-I mksh stdout-json: "[ ]\n["
321 ## N-I mksh status: 1
322
323 #### printf %*.*s (width/precision from args)
324 printf '[%*s]\n' 9 hello
325 printf '[%.*s]\n' 3 hello
326 printf '[%*.3s]\n' 9 hello
327 printf '[%9.*s]\n' 3 hello
328 printf '[%*.*s]\n' 9 3 hello
329 ## STDOUT:
330 [ hello]
331 [hel]
332 [ hel]
333 [ hel]
334 [ hel]
335 ## END
336
337 #### unsigned / octal / hex
338 printf '[%u]\n' 42
339 printf '[%o]\n' 42
340 printf '[%x]\n' 42
341 printf '[%X]\n' 42
342 printf '[%X]\n' \'a # if first character is a quote, use character code
343 printf '[%X]\n' \'ab # extra chars ignored
344 ## STDOUT:
345 [42]
346 [52]
347 [2a]
348 [2A]
349 [61]
350 [61]
351 ## END
352
353 #### empty string (osh is more strict)
354 printf '%d\n' ''
355 ## OK osh stdout-json: ""
356 ## OK osh status: 1
357 ## OK ash status: 1
358 ## STDOUT:
359 0
360 ## END
361
362 #### No char after ' (osh is more strict)
363
364 # most shells use 0 here
365 printf '%d\n' \'
366 printf '%d\n' \"
367
368 ## OK mksh status: 1
369 ## STDOUT:
370 0
371 0
372 ## END
373
374 #### Unicode char with ' (osh is more strict)
375
376 # the mu character is U+03BC
377
378 printf '%x\n' \'μ
379
380 ## STDOUT:
381 3bc
382 ## END
383 ## BUG dash/mksh/ash STDOUT:
384 ce
385 ## END
386
387 #### negative numbers with unsigned / octal / hex
388 printf '[%u]\n' -42
389 printf '[%o]\n' -42
390 printf '[%x]\n' -42
391 printf '[%X]\n' -42
392 ## STDOUT:
393 [18446744073709551574]
394 [1777777777777777777726]
395 [ffffffffffffffd6]
396 [FFFFFFFFFFFFFFD6]
397 ## END
398
399 # osh DISALLOWS this because the output depends on the machine architecture.
400 ## N-I osh stdout-json: ""
401 ## N-I osh status: 1
402
403 #### printf floating point (not required, but they all implement it)
404 printf '[%f]\n' 3.14159
405 printf '[%.2f]\n' 3.14159
406 printf '[%8.2f]\n' 3.14159
407 printf '[%-8.2f]\n' 3.14159
408 printf '[%-f]\n' 3.14159
409 printf '[%-f]\n' 3.14
410 ## STDOUT:
411 [3.141590]
412 [3.14]
413 [ 3.14]
414 [3.14 ]
415 [3.141590]
416 [3.140000]
417 ## END
418 ## N-I osh stdout-json: ""
419 ## N-I osh status: 2
420
421 #### printf floating point with - and 0
422 printf '[%8.4f]\n' 3.14
423 printf '[%08.4f]\n' 3.14
424 printf '[%8.04f]\n' 3.14 # meaning less 0
425 printf '[%08.04f]\n' 3.14
426 echo ---
427 # these all boil down to the same thing. The -, 8, and 4 are respected, but
428 # none of the 0 are.
429 printf '[%-8.4f]\n' 3.14
430 printf '[%-08.4f]\n' 3.14
431 printf '[%-8.04f]\n' 3.14
432 printf '[%-08.04f]\n' 3.14
433 ## STDOUT:
434 [ 3.1400]
435 [003.1400]
436 [ 3.1400]
437 [003.1400]
438 ---
439 [3.1400 ]
440 [3.1400 ]
441 [3.1400 ]
442 [3.1400 ]
443 ## END
444 ## N-I osh STDOUT:
445 ---
446 ## END
447 ## N-I osh status: 2
448
449 #### printf eE fF gG
450 printf '[%e]\n' 3.14
451 printf '[%E]\n' 3.14
452 printf '[%f]\n' 3.14
453 # bash is the only one that implements %F? Is it a synonym?
454 #printf '[%F]\n' 3.14
455 printf '[%g]\n' 3.14
456 printf '[%G]\n' 3.14
457 ## STDOUT:
458 [3.140000e+00]
459 [3.140000E+00]
460 [3.140000]
461 [3.14]
462 [3.14]
463 ## END
464 ## N-I osh stdout-json: ""
465 ## N-I osh status: 2
466
467 #### printf backslash escapes
468 argv.py "$(printf 'a\tb')"
469 argv.py "$(printf '\xE2\x98\xA0')"
470 argv.py "$(printf '\044e')"
471 argv.py "$(printf '\0377')" # out of range
472 ## STDOUT:
473 ['a\tb']
474 ['\xe2\x98\xa0']
475 ['$e']
476 ['\x1f7']
477 ## END
478 ## N-I dash STDOUT:
479 ['a\tb']
480 ['\\xE2\\x98\\xA0']
481 ['$e']
482 ['\x1f7']
483 ## END
484
485 #### printf octal backslash escapes
486 argv.py "$(printf '\0377')"
487 argv.py "$(printf '\377')"
488 ## STDOUT:
489 ['\x1f7']
490 ['\xff']
491 ## END
492
493 #### printf unicode backslash escapes
494 argv.py "$(printf '\u2620')"
495 argv.py "$(printf '\U0000065f')"
496 ## STDOUT:
497 ['\xe2\x98\xa0']
498 ['\xd9\x9f']
499 ## END
500 ## N-I dash/ash STDOUT:
501 ['\\u2620']
502 ['\\U0000065f']
503 ## END
504
505 #### printf invalid backslash escape (is ignored)
506 printf '[\Z]\n'
507 ## STDOUT:
508 [\Z]
509 ## END
510
511 #### printf % escapes
512 printf '[%%]\n'
513 ## STDOUT:
514 [%]
515 ## END
516
517 #### printf %b backslash escaping
518 printf '[%s]\n' '\044' # escapes not evaluated
519 printf '[%b]\n' '\044' # YES, escapes evaluated
520 echo status=$?
521 ## STDOUT:
522 [\044]
523 [$]
524 status=0
525 ## END
526
527 #### printf %b with \c early return
528 printf '[%b]\n' 'ab\ncd\cxy'
529 echo $?
530 ## STDOUT:
531 [ab
532 cd0
533 ## END
534
535 #### printf %c -- doesn't respect UTF-8! Bad.
536 twomu=$'\u03bc\u03bc'
537 printf '[%s]\n' "$twomu"
538 printf '%c' "$twomu" | wc --bytes
539 ## STDOUT:
540 [μμ]
541 1
542 ## END
543 ## N-I dash STDOUT:
544 [$\u03bc\u03bc]
545 1
546 ## END
547 ## N-I ash STDOUT:
548 [\u03bc\u03bc]
549 1
550 ## END
551 ## N-I osh STDOUT:
552 [μμ]
553 0
554 ## END
555
556 #### printf invalid format
557 printf '%z' 42
558 echo status=$?
559 printf '%-z' 42
560 echo status=$?
561 ## STDOUT:
562 status=1
563 status=1
564 ## END
565 # osh emits parse errors
566 ## OK dash/osh STDOUT:
567 status=2
568 status=2
569 ## END
570
571 #### printf %q
572 x='a b'
573 printf '[%q]\n' "$x"
574 ## STDOUT:
575 ['a b']
576 ## END
577 ## OK bash/zsh STDOUT:
578 [a\ b]
579 ## END
580 ## N-I ash/dash stdout-json: "["
581 ## N-I ash status: 1
582 ## N-I dash status: 2
583
584 #### printf %6q (width)
585 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
586 x='a b'
587 printf '[%6q]\n' "$x"
588 printf '[%1q]\n' "$x"
589 ## STDOUT:
590 [ 'a b']
591 ['a b']
592 ## END
593 ## OK bash/zsh STDOUT:
594 [ a\ b]
595 [a\ b]
596 ## END
597 ## N-I mksh/ash/dash stdout-json: "[["
598 ## N-I mksh/ash status: 1
599 ## N-I dash status: 2
600
601 #### printf negative numbers
602 printf '[%d] ' -42
603 echo status=$?
604 printf '[%i] ' -42
605 echo status=$?
606
607 # extra LEADING space too
608 printf '[%d] ' ' -42'
609 echo status=$?
610 printf '[%i] ' ' -42'
611 echo status=$?
612
613 # extra TRAILING space too
614 printf '[%d] ' ' -42 '
615 echo status=$?
616 printf '[%i] ' ' -42 '
617 echo status=$?
618
619 # extra TRAILING chars
620 printf '[%d] ' ' -42z'
621 echo status=$?
622 printf '[%i] ' ' -42z'
623 echo status=$?
624
625 exit 0 # ok
626
627 ## STDOUT:
628 [-42] status=0
629 [-42] status=0
630 [-42] status=0
631 [-42] status=0
632 [-42] status=1
633 [-42] status=1
634 [-42] status=1
635 [-42] status=1
636 ## END
637 # zsh is LESS STRICT
638 ## OK zsh STDOUT:
639 [-42] status=0
640 [-42] status=0
641 [-42] status=0
642 [-42] status=0
643 [-42] status=0
644 [-42] status=0
645 [0] status=1
646 [0] status=1
647 ## END
648
649 # osh is like zsh but has a hard failure (TODO: could be an option?)
650 ## OK osh STDOUT:
651 [-42] status=0
652 [-42] status=0
653 [-42] status=0
654 [-42] status=0
655 [-42] status=0
656 [-42] status=0
657 status=1
658 status=1
659 ## END
660
661 # ash is MORE STRICT
662 ## OK ash STDOUT:
663 [-42] status=0
664 [-42] status=0
665 [-42] status=0
666 [-42] status=0
667 [0] status=1
668 [0] status=1
669 [0] status=1
670 [0] status=1
671 ## END
672
673
674 #### printf + and space flags
675 # I didn't know these existed -- I only knew about - and 0 !
676 printf '[%+d]\n' 42
677 printf '[%+d]\n' -42
678 printf '[% d]\n' 42
679 printf '[% d]\n' -42
680 ## STDOUT:
681 [+42]
682 [-42]
683 [ 42]
684 [-42]
685 ## END
686 ## N-I osh stdout-json: ""
687 ## N-I osh status: 2
688
689 #### printf # flag
690 # I didn't know these existed -- I only knew about - and 0 !
691 # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
692 printf '[%#o][%#o]\n' 0 42
693 printf '[%#x][%#x]\n' 0 42
694 printf '[%#X][%#X]\n' 0 42
695 echo ---
696 # Note: '#' flag for %f, %g always outputs the decimal point.
697 printf '[%.0f][%#.0f]\n' 3 3
698 # Note: In addition, '#' flag for %g does not omit zeroes in fraction
699 printf '[%g][%#g]\n' 3 3
700 ## STDOUT:
701 [0][052]
702 [0][0x2a]
703 [0][0X2A]
704 ---
705 [3][3.]
706 [3][3.00000]
707 ## END
708 ## N-I osh STDOUT:
709 ---
710 ## END
711 ## N-I osh status: 2
712
713 #### Runtime error for invalid integer
714 x=3abc
715 printf '%d\n' $x
716 echo status=$?
717 printf '%d\n' xyz
718 echo status=$?
719 ## STDOUT:
720 3
721 status=1
722 0
723 status=1
724 ## END
725 # zsh should exit 1 in both cases
726 ## BUG zsh STDOUT:
727 0
728 status=1
729 0
730 status=0
731 ## END
732 # fails but also prints 0 instead of 3abc
733 ## BUG ash STDOUT:
734 0
735 status=1
736 0
737 status=1
738 ## END
739 # osh doesn't print anything invalid
740 ## OK osh STDOUT:
741 status=1
742 status=1
743 ## END
744
745 #### %(strftime format)T
746 # The result depends on timezone
747 export TZ=Asia/Tokyo
748 printf '%(%Y-%m-%d)T\n' 1557978599
749 export TZ=US/Eastern
750 printf '%(%Y-%m-%d)T\n' 1557978599
751 echo status=$?
752 ## STDOUT:
753 2019-05-16
754 2019-05-15
755 status=0
756 ## END
757 ## N-I mksh/zsh/ash STDOUT:
758 status=1
759 ## END
760 ## N-I dash STDOUT:
761 status=2
762 ## END
763
764 #### %(strftime format)T doesn't respect TZ if not exported
765
766 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
767
768 TZ=Portugal # NOT exported
769 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
770
771 # TZ is respected
772 export TZ=Portugal
773 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
774
775 #echo $localtime
776 #echo $tz
777
778 if ! test "$localtime" = "$tz"; then
779 echo 'not equal'
780 fi
781 ## STDOUT:
782 not equal
783 ## END
784 ## N-I mksh/zsh/ash/dash stdout-json: ""
785
786 #### %(strftime format)T TZ in environ but not in shell's memory
787
788 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
789
790 # TZ is respected
791 export TZ=Portugal
792 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
793
794 unset TZ # unset in the shell, but still in the environment
795
796 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
797
798 if ! test "$localtime" = "$tz"; then
799 echo 'not equal'
800 fi
801
802 ## STDOUT:
803 not equal
804 ## END
805 ## N-I mksh/zsh/ash/dash stdout-json: ""
806
807 #### %10.5(strftime format)T
808 # The result depends on timezone
809 export TZ=Asia/Tokyo
810 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
811 export TZ=US/Eastern
812 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
813 echo status=$?
814 ## STDOUT:
815 [ 2019-]
816 [ 2019-]
817 status=0
818 ## END
819 ## N-I dash/mksh/zsh/ash STDOUT:
820 [[status=1
821 ## END
822 ## N-I dash STDOUT:
823 [[status=2
824 ## END
825
826 #### Regression for 'printf x y'
827 printf x y
828 printf '%s\n' z
829 ## STDOUT:
830 xz
831 ## END
832
833 #### bash truncates long strftime string at 128
834
835 case $SH in (ash|dash|mksh|zsh) exit ;; esac
836
837 strftime-format() {
838 local n=$1
839
840 # Prints increasingly long format strings:
841 # %(%Y)T %(%Y)T %(%Y%Y)T ...
842
843 echo -n '%('
844 for i in $(seq $n); do
845 echo -n '%Y'
846 done
847 echo -n ')T'
848 }
849
850 printf $(strftime-format 1) | wc --bytes
851 printf $(strftime-format 10) | wc --bytes
852 printf $(strftime-format 30) | wc --bytes
853 printf $(strftime-format 31) | wc --bytes
854 printf $(strftime-format 32) | wc --bytes
855
856 case $SH in
857 (*/_bin/cxx-dbg/*)
858 # Ensure that oil-native detects the truncation of a fixed buffer.
859 # bash has a buffer of 128.
860
861 set +o errexit
862 (
863 printf $(strftime-format 1000)
864 )
865 status=$?
866 if test $status -ne 1; then
867 echo FAIL
868 fi
869 ;;
870 esac
871
872 ## STDOUT:
873 4
874 40
875 120
876 124
877 0
878 ## END
879 ## OK osh STDOUT:
880 4
881 40
882 120
883 124
884 128
885 ## END
886
887 ## N-I ash/dash/mksh/zsh STDOUT:
888 ## END
889
890
891 #### printf with explicit NUL byte
892 case $SH in (dash|ash) return ;; esac
893
894 printf $'x\U0z'
895
896 printf $'\U0z'
897
898 ## stdout-json: "x"
899 ## OK zsh stdout-repr: "x\0z\0z"
900 ## N-I dash/ash stdout-json: ""