1 | ## oils_failures_allowed: 0
|
2 | ## compare_shells: dash bash mksh zsh ash
|
3 |
|
4 | # printf
|
5 | # bash-completion uses this odd printf -v construction. It seems to mostly use
|
6 | # %s and %q though.
|
7 | #
|
8 | # %s should just be
|
9 | # declare $var='val'
|
10 | #
|
11 | # NOTE:
|
12 | # /usr/bin/printf %q "'" seems wrong.
|
13 | # $ /usr/bin/printf %q "'"
|
14 | # ''\'''
|
15 | #
|
16 | # I suppose it is technically correct, but it looks very ugly.
|
17 |
|
18 | #### printf with no args
|
19 | printf
|
20 | ## status: 2
|
21 | ## OK mksh/zsh status: 1
|
22 | ## stdout-json: ""
|
23 |
|
24 | #### printf -v %s
|
25 | var=foo
|
26 | printf -v $var %s 'hello there'
|
27 | argv.py "$foo"
|
28 | ## STDOUT:
|
29 | ['hello there']
|
30 | ## END
|
31 | ## N-I mksh/zsh/ash STDOUT:
|
32 | -v['']
|
33 | ## END
|
34 | ## N-I dash STDOUT:
|
35 | ['']
|
36 | ## END
|
37 |
|
38 | #### printf -v %q
|
39 | val='"quoted" with spaces and \'
|
40 |
|
41 | # quote 'val' and store it in foo
|
42 | printf -v foo %q "$val"
|
43 | # then round trip back to eval
|
44 | eval "bar=$foo"
|
45 |
|
46 | # debugging:
|
47 | #echo foo="$foo"
|
48 | #echo bar="$bar"
|
49 | #echo val="$val"
|
50 |
|
51 | test "$bar" = "$val" && echo OK
|
52 | ## STDOUT:
|
53 | OK
|
54 | ## END
|
55 | ## N-I mksh/zsh/ash stdout-json: "-v"
|
56 | ## N-I mksh/zsh/ash status: 1
|
57 | ## N-I dash stdout-json: ""
|
58 | ## N-I dash status: 1
|
59 |
|
60 | #### printf -v a[1]
|
61 | a=(a b c)
|
62 | printf -v 'a[1]' %s 'foo'
|
63 | echo status=$?
|
64 | argv.py "${a[@]}"
|
65 | ## STDOUT:
|
66 | status=0
|
67 | ['a', 'foo', 'c']
|
68 | ## END
|
69 | ## N-I mksh/zsh STDOUT:
|
70 | -vstatus=0
|
71 | ['a', 'b', 'c']
|
72 | ## END
|
73 | ## N-I dash/ash stdout-json: ""
|
74 | ## N-I dash/ash status: 2
|
75 |
|
76 | #### printf -v syntax error
|
77 | printf -v 'a[' %s 'foo'
|
78 | echo status=$?
|
79 | ## STDOUT:
|
80 | status=2
|
81 | ## END
|
82 | ## N-I ash/mksh/zsh stdout: -vstatus=0
|
83 |
|
84 | #### dynamic declare instead of %s
|
85 | var=foo
|
86 | declare $var='hello there'
|
87 | argv.py "$foo"
|
88 | ## STDOUT:
|
89 | ['hello there']
|
90 | ## END
|
91 | ## N-I dash/mksh/ash STDOUT:
|
92 | ['']
|
93 | ## END
|
94 |
|
95 | #### dynamic declare instead of %q
|
96 | var=foo
|
97 | val='"quoted" with spaces and \'
|
98 | # I think this is bash 4.4 only.
|
99 | declare $var="${val@Q}"
|
100 | echo "$foo"
|
101 | ## STDOUT:
|
102 | '"quoted" with spaces and \'
|
103 | ## END
|
104 | ## OK osh STDOUT:
|
105 | $'"quoted" with spaces and \\'
|
106 | ## END
|
107 | ## N-I dash/ash stdout-json: ""
|
108 | ## N-I dash/ash status: 2
|
109 | ## N-I mksh stdout-json: "\n"
|
110 | ## N-I zsh stdout-json: ""
|
111 | ## N-I zsh status: 1
|
112 |
|
113 | #### printf -v dynamic scope
|
114 | case $SH in mksh|zsh|dash|ash) echo not implemented; exit ;; esac
|
115 | # OK so printf is like assigning to a var.
|
116 | # printf -v foo %q "$bar" is like
|
117 | # foo=${bar@Q}
|
118 | dollar='dollar'
|
119 | f() {
|
120 | local mylocal=foo
|
121 | printf -v dollar %q '$' # assign foo to a quoted dollar
|
122 | printf -v mylocal %q 'mylocal'
|
123 | echo dollar=$dollar
|
124 | echo mylocal=$mylocal
|
125 | }
|
126 | echo dollar=$dollar
|
127 | echo --
|
128 | f
|
129 | echo --
|
130 | echo dollar=$dollar
|
131 | echo mylocal=$mylocal
|
132 | ## STDOUT:
|
133 | dollar=dollar
|
134 | --
|
135 | dollar=\$
|
136 | mylocal=mylocal
|
137 | --
|
138 | dollar=\$
|
139 | mylocal=
|
140 | ## END
|
141 | ## OK osh STDOUT:
|
142 | dollar=dollar
|
143 | --
|
144 | dollar='$'
|
145 | mylocal=mylocal
|
146 | --
|
147 | dollar='$'
|
148 | mylocal=
|
149 | ## END
|
150 | ## N-I dash/ash/mksh/zsh STDOUT:
|
151 | not implemented
|
152 | ## END
|
153 |
|
154 | #### printf with too few arguments
|
155 | printf -- '-%s-%s-%s-\n' 'a b' 'x y'
|
156 | ## STDOUT:
|
157 | -a b-x y--
|
158 | ## END
|
159 |
|
160 | #### printf with too many arguments
|
161 | printf -- '-%s-%s-\n' a b c d e
|
162 | ## STDOUT:
|
163 | -a-b-
|
164 | -c-d-
|
165 | -e--
|
166 | ## END
|
167 |
|
168 | #### printf width strings
|
169 | printf '[%5s]\n' abc
|
170 | printf '[%-5s]\n' abc
|
171 | ## STDOUT:
|
172 | [ abc]
|
173 | [abc ]
|
174 | ## END
|
175 |
|
176 | #### printf integer
|
177 | printf '%d\n' 42
|
178 | printf '%i\n' 42 # synonym
|
179 | printf '%d\n' \'a # if first character is a quote, use character code
|
180 | printf '%d\n' \"a # double quotes work too
|
181 | printf '[%5d]\n' 42
|
182 | printf '[%-5d]\n' 42
|
183 | printf '[%05d]\n' 42
|
184 | #printf '[%-05d]\n' 42 # the leading 0 is meaningless
|
185 | #[42 ]
|
186 | ## STDOUT:
|
187 | 42
|
188 | 42
|
189 | 97
|
190 | 97
|
191 | [ 42]
|
192 | [42 ]
|
193 | [00042]
|
194 | ## END
|
195 |
|
196 | #### printf %6.4d -- "precision" does padding for integers
|
197 | printf '[%6.4d]\n' 42
|
198 | printf '[%.4d]\n' 42
|
199 | printf '[%6.d]\n' 42
|
200 | echo --
|
201 | printf '[%6.4d]\n' -42
|
202 | printf '[%.4d]\n' -42
|
203 | printf '[%6.d]\n' -42
|
204 | ## STDOUT:
|
205 | [ 0042]
|
206 | [0042]
|
207 | [ 42]
|
208 | --
|
209 | [ -0042]
|
210 | [-0042]
|
211 | [ -42]
|
212 | ## END
|
213 |
|
214 | #### printf %6.4x X o
|
215 | printf '[%6.4x]\n' 42
|
216 | printf '[%.4x]\n' 42
|
217 | printf '[%6.x]\n' 42
|
218 | echo --
|
219 | printf '[%6.4X]\n' 42
|
220 | printf '[%.4X]\n' 42
|
221 | printf '[%6.X]\n' 42
|
222 | echo --
|
223 | printf '[%6.4o]\n' 42
|
224 | printf '[%.4o]\n' 42
|
225 | printf '[%6.o]\n' 42
|
226 | ## STDOUT:
|
227 | [ 002a]
|
228 | [002a]
|
229 | [ 2a]
|
230 | --
|
231 | [ 002A]
|
232 | [002A]
|
233 | [ 2A]
|
234 | --
|
235 | [ 0052]
|
236 | [0052]
|
237 | [ 52]
|
238 | ## END
|
239 |
|
240 | #### %06d zero padding vs. %6.6d
|
241 | printf '[%06d]\n' 42
|
242 | printf '[%06d]\n' -42 # 6 TOTAL
|
243 | echo --
|
244 | printf '[%6.6d]\n' 42
|
245 | printf '[%6.6d]\n' -42 # 6 + 1 for the - sign!!!
|
246 | ## STDOUT:
|
247 | [000042]
|
248 | [-00042]
|
249 | --
|
250 | [000042]
|
251 | [-000042]
|
252 | ## END
|
253 |
|
254 | #### %06x %06X %06o
|
255 | printf '[%06x]\n' 42
|
256 | printf '[%06X]\n' 42
|
257 | printf '[%06o]\n' 42
|
258 | ## STDOUT:
|
259 | [00002a]
|
260 | [00002A]
|
261 | [000052]
|
262 | ## END
|
263 |
|
264 | #### %06s is no-op
|
265 | printf '(%6s)\n' 42
|
266 | printf '(%6s)\n' -42
|
267 | printf '(%06s)\n' 42
|
268 | printf '(%06s)\n' -42
|
269 | echo status=$?
|
270 | ## STDOUT:
|
271 | ( 42)
|
272 | ( -42)
|
273 | ( 42)
|
274 | ( -42)
|
275 | status=0
|
276 | ## END
|
277 | # mksh is stricter
|
278 | ## OK mksh STDOUT:
|
279 | ( 42)
|
280 | ( -42)
|
281 | ((status=1
|
282 | ## END
|
283 |
|
284 | #### printf %6.4s does both truncation and padding
|
285 | printf '[%6s]\n' foo
|
286 | printf '[%6.4s]\n' foo
|
287 | printf '[%-6.4s]\n' foo
|
288 | printf '[%6s]\n' spam-eggs
|
289 | printf '[%6.4s]\n' spam-eggs
|
290 | printf '[%-6.4s]\n' spam-eggs
|
291 | ## STDOUT:
|
292 | [ foo]
|
293 | [ foo]
|
294 | [foo ]
|
295 | [spam-eggs]
|
296 | [ spam]
|
297 | [spam ]
|
298 | ## END
|
299 |
|
300 | #### printf %6.0s and %0.0s
|
301 | printf '[%6.0s]\n' foo
|
302 | printf '[%0.0s]\n' foo
|
303 | ## STDOUT:
|
304 | [ ]
|
305 | []
|
306 | ## END
|
307 | ## N-I mksh stdout-json: "[ ]\n["
|
308 | ## N-I mksh status: 1
|
309 |
|
310 | #### printf %6.s and %0.s
|
311 | printf '[%6.s]\n' foo
|
312 | printf '[%0.s]\n' foo
|
313 | ## STDOUT:
|
314 | [ ]
|
315 | []
|
316 | ## END
|
317 | ## BUG zsh STDOUT:
|
318 | [ foo]
|
319 | [foo]
|
320 | ## END
|
321 | ## N-I mksh stdout-json: "[ ]\n["
|
322 | ## N-I mksh status: 1
|
323 |
|
324 | #### printf %*.*s (width/precision from args)
|
325 | printf '[%*s]\n' 9 hello
|
326 | printf '[%.*s]\n' 3 hello
|
327 | printf '[%*.3s]\n' 9 hello
|
328 | printf '[%9.*s]\n' 3 hello
|
329 | printf '[%*.*s]\n' 9 3 hello
|
330 | ## STDOUT:
|
331 | [ hello]
|
332 | [hel]
|
333 | [ hel]
|
334 | [ hel]
|
335 | [ hel]
|
336 | ## END
|
337 |
|
338 | #### unsigned / octal / hex
|
339 | printf '[%u]\n' 42
|
340 | printf '[%o]\n' 42
|
341 | printf '[%x]\n' 42
|
342 | printf '[%X]\n' 42
|
343 | echo
|
344 |
|
345 | printf '[%X]\n' \'a # if first character is a quote, use character code
|
346 | printf '[%X]\n' \'ab # extra chars ignored
|
347 |
|
348 | ## STDOUT:
|
349 | [42]
|
350 | [52]
|
351 | [2a]
|
352 | [2A]
|
353 |
|
354 | [61]
|
355 | [61]
|
356 | ## END
|
357 |
|
358 | #### unsigned / octal / hex big
|
359 |
|
360 | for big in $(( 1 << 32 )) $(( (1 << 63) - 1 )); do
|
361 | printf '[%u]\n' $big
|
362 | printf '[%o]\n' $big
|
363 | printf '[%x]\n' $big
|
364 | printf '[%X]\n' $big
|
365 | echo
|
366 | done
|
367 |
|
368 | ## STDOUT:
|
369 | [4294967296]
|
370 | [40000000000]
|
371 | [100000000]
|
372 | [100000000]
|
373 |
|
374 | [9223372036854775807]
|
375 | [777777777777777777777]
|
376 | [7fffffffffffffff]
|
377 | [7FFFFFFFFFFFFFFF]
|
378 |
|
379 | ## END
|
380 |
|
381 | ## BUG mksh STDOUT:
|
382 | [1]
|
383 | [1]
|
384 | [1]
|
385 | [1]
|
386 |
|
387 | [2147483647]
|
388 | [17777777777]
|
389 | [7fffffff]
|
390 | [7FFFFFFF]
|
391 |
|
392 | ## END
|
393 |
|
394 | #### empty string (osh is more strict)
|
395 | printf '%d\n' ''
|
396 | ## OK osh stdout-json: ""
|
397 | ## OK osh status: 1
|
398 | ## OK ash status: 1
|
399 | ## STDOUT:
|
400 | 0
|
401 | ## END
|
402 |
|
403 | #### No char after ' => zero code point
|
404 |
|
405 | # most shells use 0 here
|
406 | printf '%d\n' \'
|
407 | printf '%d\n' \"
|
408 |
|
409 | ## OK mksh status: 1
|
410 | ## STDOUT:
|
411 | 0
|
412 | 0
|
413 | ## END
|
414 |
|
415 | #### Unicode char with '
|
416 | case $SH in mksh) echo 'weird bug'; exit ;; esac
|
417 |
|
418 | # the mu character is U+03BC
|
419 |
|
420 | printf '%x\n' \'μ
|
421 | printf '%u\n' \'μ
|
422 | printf '%o\n' \'μ
|
423 | echo
|
424 |
|
425 | u3=三
|
426 | # u4=😘
|
427 |
|
428 | printf '%x\n' \'$u3
|
429 | printf '%u\n' \'$u3
|
430 | printf '%o\n' \'$u3
|
431 | echo
|
432 |
|
433 | # mksh DOES respect unicode on the new Debian bookworm.
|
434 | # but even building the SAME SOURCE from scratch, somehow it doesn't on Ubuntu 8.
|
435 | # TBH I should probably just upgrade the mksh version.
|
436 | #
|
437 | # $ ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
438 | # printf: warning: : character(s) following character constant have been ignored
|
439 | # 206
|
440 | #
|
441 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ cat /etc/os-release
|
442 | # NAME="Ubuntu"
|
443 | # VERSION="18.04.5 LTS (Bionic Beaver)"
|
444 | # ID=ubuntu
|
445 | # ID_LIKE=debian
|
446 | # PRETTY_NAME="Ubuntu 18.04.5 LTS"
|
447 | # VERSION_ID="18.04"
|
448 | # HOME_URL="https://www.ubuntu.com/"
|
449 | # SUPPORT_URL="https://help.ubuntu.com/"
|
450 | # BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
451 | # PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
452 | # VERSION_CODENAME=bionic
|
453 | # UBUNTU_CODENAME=bionic
|
454 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ env|egrep 'LC|LANG'
|
455 | # LANG=en_US.UTF-8
|
456 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_CTYPE=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
457 | # printf: warning: : character(s) following character constant have been ignored
|
458 | # 206
|
459 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LANG=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
460 | # printf: warning: : character(s) following character constant have been ignored
|
461 | # 206
|
462 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
463 | # printf: warning: : character(s) following character constant have been ignored
|
464 | # 206
|
465 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=en_US.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
466 | # printf: warning: : character(s) following character constant have been ignored
|
467 | # 206
|
468 | # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=en_US.utf-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
|
469 | # printf: warning: : character(s) following character constant have been ignored
|
470 | # 206
|
471 |
|
472 |
|
473 | ## STDOUT:
|
474 | 3bc
|
475 | 956
|
476 | 1674
|
477 |
|
478 | 4e09
|
479 | 19977
|
480 | 47011
|
481 |
|
482 | ## END
|
483 | ## BUG dash/ash STDOUT:
|
484 | ce
|
485 | 206
|
486 | 316
|
487 |
|
488 | e4
|
489 | 228
|
490 | 344
|
491 |
|
492 | ## END
|
493 |
|
494 | ## BUG mksh STDOUT:
|
495 | weird bug
|
496 | ## END
|
497 |
|
498 | #### Invalid UTF-8
|
499 |
|
500 | echo bytes1
|
501 | not_utf8=$(python2 -c 'print("\xce\xce")')
|
502 |
|
503 | printf '%x\n' \'$not_utf8
|
504 | printf '%u\n' \'$not_utf8
|
505 | printf '%o\n' \'$not_utf8
|
506 | echo
|
507 |
|
508 | echo bytes2
|
509 | not_utf8=$(python2 -c 'print("\xbc\xbc")')
|
510 | printf '%x\n' \'$not_utf8
|
511 | printf '%u\n' \'$not_utf8
|
512 | printf '%o\n' \'$not_utf8
|
513 | echo
|
514 |
|
515 | # Copied from data_lang/utf8_test.cc
|
516 |
|
517 | echo overlong2
|
518 | overlong2=$(python2 -c 'print("\xC1\x81")')
|
519 | printf '%x\n' \'$overlong2
|
520 | printf '%u\n' \'$overlong2
|
521 | printf '%o\n' \'$overlong2
|
522 | echo
|
523 |
|
524 | echo overlong3
|
525 | overlong3=$(python2 -c 'print("\xE0\x81\x81")')
|
526 | printf '%x\n' \'$overlong3
|
527 | printf '%u\n' \'$overlong3
|
528 | printf '%o\n' \'$overlong3
|
529 | echo
|
530 |
|
531 | ## STDOUT:
|
532 | bytes1
|
533 | ce
|
534 | 206
|
535 | 316
|
536 |
|
537 | bytes2
|
538 | bc
|
539 | 188
|
540 | 274
|
541 |
|
542 | overlong2
|
543 | c1
|
544 | 193
|
545 | 301
|
546 |
|
547 | overlong3
|
548 | e0
|
549 | 224
|
550 | 340
|
551 |
|
552 | ## END
|
553 |
|
554 |
|
555 | #### Too large
|
556 | case $SH in mksh) echo 'weird bug'; exit ;; esac
|
557 |
|
558 | echo too large
|
559 | too_large=$(python2 -c 'print("\xF4\x91\x84\x91")')
|
560 | printf '%x\n' \'$too_large
|
561 | printf '%u\n' \'$too_large
|
562 | printf '%o\n' \'$too_large
|
563 | echo
|
564 |
|
565 | ## STDOUT:
|
566 | too large
|
567 | 111111
|
568 | 1118481
|
569 | 4210421
|
570 |
|
571 | ## END
|
572 |
|
573 | ## BUG dash/ash STDOUT:
|
574 | too large
|
575 | f4
|
576 | 244
|
577 | 364
|
578 |
|
579 | ## END
|
580 |
|
581 | ## BUG mksh STDOUT:
|
582 | weird bug
|
583 | ## END
|
584 |
|
585 | # osh rejects code points that are too large for a DIFFERENT reason
|
586 |
|
587 | ## OK osh STDOUT:
|
588 | too large
|
589 | f4
|
590 | 244
|
591 | 364
|
592 |
|
593 | ## END
|
594 |
|
595 |
|
596 | #### negative numbers with unsigned / octal / hex
|
597 | printf '[%u]\n' -42
|
598 | printf '[%o]\n' -42
|
599 | printf '[%x]\n' -42
|
600 | printf '[%X]\n' -42
|
601 | ## STDOUT:
|
602 | [18446744073709551574]
|
603 | [1777777777777777777726]
|
604 | [ffffffffffffffd6]
|
605 | [FFFFFFFFFFFFFFD6]
|
606 | ## END
|
607 |
|
608 | # osh DISALLOWS this because the output depends on the machine architecture.
|
609 | ## N-I osh stdout-json: ""
|
610 | ## N-I osh status: 1
|
611 |
|
612 | #### printf floating point (not required, but they all implement it)
|
613 | printf '[%f]\n' 3.14159
|
614 | printf '[%.2f]\n' 3.14159
|
615 | printf '[%8.2f]\n' 3.14159
|
616 | printf '[%-8.2f]\n' 3.14159
|
617 | printf '[%-f]\n' 3.14159
|
618 | printf '[%-f]\n' 3.14
|
619 | ## STDOUT:
|
620 | [3.141590]
|
621 | [3.14]
|
622 | [ 3.14]
|
623 | [3.14 ]
|
624 | [3.141590]
|
625 | [3.140000]
|
626 | ## END
|
627 | ## N-I osh stdout-json: ""
|
628 | ## N-I osh status: 2
|
629 |
|
630 | #### printf floating point with - and 0
|
631 | printf '[%8.4f]\n' 3.14
|
632 | printf '[%08.4f]\n' 3.14
|
633 | printf '[%8.04f]\n' 3.14 # meaning less 0
|
634 | printf '[%08.04f]\n' 3.14
|
635 | echo ---
|
636 | # these all boil down to the same thing. The -, 8, and 4 are respected, but
|
637 | # none of the 0 are.
|
638 | printf '[%-8.4f]\n' 3.14
|
639 | printf '[%-08.4f]\n' 3.14
|
640 | printf '[%-8.04f]\n' 3.14
|
641 | printf '[%-08.04f]\n' 3.14
|
642 | ## STDOUT:
|
643 | [ 3.1400]
|
644 | [003.1400]
|
645 | [ 3.1400]
|
646 | [003.1400]
|
647 | ---
|
648 | [3.1400 ]
|
649 | [3.1400 ]
|
650 | [3.1400 ]
|
651 | [3.1400 ]
|
652 | ## END
|
653 | ## N-I osh STDOUT:
|
654 | ---
|
655 | ## END
|
656 | ## N-I osh status: 2
|
657 |
|
658 | #### printf eE fF gG
|
659 | printf '[%e]\n' 3.14
|
660 | printf '[%E]\n' 3.14
|
661 | printf '[%f]\n' 3.14
|
662 | # bash is the only one that implements %F? Is it a synonym?
|
663 | #printf '[%F]\n' 3.14
|
664 | printf '[%g]\n' 3.14
|
665 | printf '[%G]\n' 3.14
|
666 | ## STDOUT:
|
667 | [3.140000e+00]
|
668 | [3.140000E+00]
|
669 | [3.140000]
|
670 | [3.14]
|
671 | [3.14]
|
672 | ## END
|
673 | ## N-I osh stdout-json: ""
|
674 | ## N-I osh status: 2
|
675 |
|
676 | #### printf backslash escapes
|
677 | argv.py "$(printf 'a\tb')"
|
678 | argv.py "$(printf '\xE2\x98\xA0')"
|
679 | argv.py "$(printf '\044e')"
|
680 | argv.py "$(printf '\0377')" # out of range
|
681 | ## STDOUT:
|
682 | ['a\tb']
|
683 | ['\xe2\x98\xa0']
|
684 | ['$e']
|
685 | ['\x1f7']
|
686 | ## END
|
687 | ## N-I dash STDOUT:
|
688 | ['a\tb']
|
689 | ['\\xE2\\x98\\xA0']
|
690 | ['$e']
|
691 | ['\x1f7']
|
692 | ## END
|
693 |
|
694 | #### printf octal backslash escapes
|
695 | argv.py "$(printf '\0377')"
|
696 | argv.py "$(printf '\377')"
|
697 | ## STDOUT:
|
698 | ['\x1f7']
|
699 | ['\xff']
|
700 | ## END
|
701 |
|
702 | #### printf unicode backslash escapes
|
703 | argv.py "$(printf '\u2620')"
|
704 | argv.py "$(printf '\U0000065f')"
|
705 | ## STDOUT:
|
706 | ['\xe2\x98\xa0']
|
707 | ['\xd9\x9f']
|
708 | ## END
|
709 | ## N-I dash/ash STDOUT:
|
710 | ['\\u2620']
|
711 | ['\\U0000065f']
|
712 | ## END
|
713 |
|
714 | #### printf invalid backslash escape (is ignored)
|
715 | printf '[\Z]\n'
|
716 | ## STDOUT:
|
717 | [\Z]
|
718 | ## END
|
719 |
|
720 | #### printf % escapes
|
721 | printf '[%%]\n'
|
722 | ## STDOUT:
|
723 | [%]
|
724 | ## END
|
725 |
|
726 | #### printf %b backslash escaping
|
727 | printf '[%s]\n' '\044' # escapes not evaluated
|
728 | printf '[%b]\n' '\044' # YES, escapes evaluated
|
729 | echo status=$?
|
730 | ## STDOUT:
|
731 | [\044]
|
732 | [$]
|
733 | status=0
|
734 | ## END
|
735 |
|
736 | #### printf %b with \c early return
|
737 | printf '[%b]\n' 'ab\ncd\cxy'
|
738 | echo $?
|
739 | ## STDOUT:
|
740 | [ab
|
741 | cd0
|
742 | ## END
|
743 |
|
744 | #### printf %c -- doesn't respect UTF-8! Bad.
|
745 | twomu=$'\u03bc\u03bc'
|
746 | printf '[%s]\n' "$twomu"
|
747 | printf '%c' "$twomu" | wc --bytes
|
748 | ## STDOUT:
|
749 | [μμ]
|
750 | 1
|
751 | ## END
|
752 | ## N-I dash STDOUT:
|
753 | [$\u03bc\u03bc]
|
754 | 1
|
755 | ## END
|
756 | ## N-I ash STDOUT:
|
757 | [\u03bc\u03bc]
|
758 | 1
|
759 | ## END
|
760 | ## N-I osh STDOUT:
|
761 | [μμ]
|
762 | 0
|
763 | ## END
|
764 |
|
765 | #### printf invalid format
|
766 | printf '%z' 42
|
767 | echo status=$?
|
768 | printf '%-z' 42
|
769 | echo status=$?
|
770 | ## STDOUT:
|
771 | status=1
|
772 | status=1
|
773 | ## END
|
774 | # osh emits parse errors
|
775 | ## OK dash/osh STDOUT:
|
776 | status=2
|
777 | status=2
|
778 | ## END
|
779 |
|
780 | #### printf %q
|
781 | x='a b'
|
782 | printf '[%q]\n' "$x"
|
783 | ## STDOUT:
|
784 | ['a b']
|
785 | ## END
|
786 | ## OK bash/zsh STDOUT:
|
787 | [a\ b]
|
788 | ## END
|
789 | ## N-I ash/dash stdout-json: "["
|
790 | ## N-I ash status: 1
|
791 | ## N-I dash status: 2
|
792 |
|
793 | #### printf %6q (width)
|
794 | # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
|
795 | x='a b'
|
796 | printf '[%6q]\n' "$x"
|
797 | printf '[%1q]\n' "$x"
|
798 | ## STDOUT:
|
799 | [ 'a b']
|
800 | ['a b']
|
801 | ## END
|
802 | ## OK bash/zsh STDOUT:
|
803 | [ a\ b]
|
804 | [a\ b]
|
805 | ## END
|
806 | ## N-I mksh/ash/dash stdout-json: "[["
|
807 | ## N-I mksh/ash status: 1
|
808 | ## N-I dash status: 2
|
809 |
|
810 | #### printf negative numbers
|
811 | printf '[%d] ' -42
|
812 | echo status=$?
|
813 | printf '[%i] ' -42
|
814 | echo status=$?
|
815 |
|
816 | # extra LEADING space too
|
817 | printf '[%d] ' ' -42'
|
818 | echo status=$?
|
819 | printf '[%i] ' ' -42'
|
820 | echo status=$?
|
821 |
|
822 | # extra TRAILING space too
|
823 | printf '[%d] ' ' -42 '
|
824 | echo status=$?
|
825 | printf '[%i] ' ' -42 '
|
826 | echo status=$?
|
827 |
|
828 | # extra TRAILING chars
|
829 | printf '[%d] ' ' -42z'
|
830 | echo status=$?
|
831 | printf '[%i] ' ' -42z'
|
832 | echo status=$?
|
833 |
|
834 | exit 0 # ok
|
835 |
|
836 | ## STDOUT:
|
837 | [-42] status=0
|
838 | [-42] status=0
|
839 | [-42] status=0
|
840 | [-42] status=0
|
841 | [-42] status=1
|
842 | [-42] status=1
|
843 | [-42] status=1
|
844 | [-42] status=1
|
845 | ## END
|
846 | # zsh is LESS STRICT
|
847 | ## OK zsh STDOUT:
|
848 | [-42] status=0
|
849 | [-42] status=0
|
850 | [-42] status=0
|
851 | [-42] status=0
|
852 | [-42] status=0
|
853 | [-42] status=0
|
854 | [0] status=1
|
855 | [0] status=1
|
856 | ## END
|
857 |
|
858 | # osh is like zsh but has a hard failure (TODO: could be an option?)
|
859 | ## OK osh STDOUT:
|
860 | [-42] status=0
|
861 | [-42] status=0
|
862 | [-42] status=0
|
863 | [-42] status=0
|
864 | [-42] status=0
|
865 | [-42] status=0
|
866 | status=1
|
867 | status=1
|
868 | ## END
|
869 |
|
870 | # ash is MORE STRICT
|
871 | ## OK ash STDOUT:
|
872 | [-42] status=0
|
873 | [-42] status=0
|
874 | [-42] status=0
|
875 | [-42] status=0
|
876 | [0] status=1
|
877 | [0] status=1
|
878 | [0] status=1
|
879 | [0] status=1
|
880 | ## END
|
881 |
|
882 |
|
883 | #### printf + and space flags
|
884 | # I didn't know these existed -- I only knew about - and 0 !
|
885 | printf '[%+d]\n' 42
|
886 | printf '[%+d]\n' -42
|
887 | printf '[% d]\n' 42
|
888 | printf '[% d]\n' -42
|
889 | ## STDOUT:
|
890 | [+42]
|
891 | [-42]
|
892 | [ 42]
|
893 | [-42]
|
894 | ## END
|
895 | ## N-I osh stdout-json: ""
|
896 | ## N-I osh status: 2
|
897 |
|
898 | #### printf # flag
|
899 | # I didn't know these existed -- I only knew about - and 0 !
|
900 | # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
|
901 | printf '[%#o][%#o]\n' 0 42
|
902 | printf '[%#x][%#x]\n' 0 42
|
903 | printf '[%#X][%#X]\n' 0 42
|
904 | echo ---
|
905 | # Note: '#' flag for %f, %g always outputs the decimal point.
|
906 | printf '[%.0f][%#.0f]\n' 3 3
|
907 | # Note: In addition, '#' flag for %g does not omit zeroes in fraction
|
908 | printf '[%g][%#g]\n' 3 3
|
909 | ## STDOUT:
|
910 | [0][052]
|
911 | [0][0x2a]
|
912 | [0][0X2A]
|
913 | ---
|
914 | [3][3.]
|
915 | [3][3.00000]
|
916 | ## END
|
917 | ## N-I osh STDOUT:
|
918 | ---
|
919 | ## END
|
920 | ## N-I osh status: 2
|
921 |
|
922 | #### Runtime error for invalid integer
|
923 | x=3abc
|
924 | printf '%d\n' $x
|
925 | echo status=$?
|
926 | printf '%d\n' xyz
|
927 | echo status=$?
|
928 | ## STDOUT:
|
929 | 3
|
930 | status=1
|
931 | 0
|
932 | status=1
|
933 | ## END
|
934 | # zsh should exit 1 in both cases
|
935 | ## BUG zsh STDOUT:
|
936 | 0
|
937 | status=1
|
938 | 0
|
939 | status=0
|
940 | ## END
|
941 | # fails but also prints 0 instead of 3abc
|
942 | ## BUG ash STDOUT:
|
943 | 0
|
944 | status=1
|
945 | 0
|
946 | status=1
|
947 | ## END
|
948 | # osh doesn't print anything invalid
|
949 | ## OK osh STDOUT:
|
950 | status=1
|
951 | status=1
|
952 | ## END
|
953 |
|
954 | #### %(strftime format)T
|
955 | # The result depends on timezone
|
956 | export TZ=Asia/Tokyo
|
957 | printf '%(%Y-%m-%d)T\n' 1557978599
|
958 | export TZ=US/Eastern
|
959 | printf '%(%Y-%m-%d)T\n' 1557978599
|
960 | echo status=$?
|
961 | ## STDOUT:
|
962 | 2019-05-16
|
963 | 2019-05-15
|
964 | status=0
|
965 | ## END
|
966 | ## N-I mksh/zsh/ash STDOUT:
|
967 | status=1
|
968 | ## END
|
969 | ## N-I dash STDOUT:
|
970 | status=2
|
971 | ## END
|
972 |
|
973 | #### %(strftime format)T doesn't respect TZ if not exported
|
974 |
|
975 | # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
|
976 |
|
977 | TZ=Portugal # NOT exported
|
978 | localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
|
979 |
|
980 | # TZ is respected
|
981 | export TZ=Portugal
|
982 | tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
|
983 |
|
984 | #echo $localtime
|
985 | #echo $tz
|
986 |
|
987 | if ! test "$localtime" = "$tz"; then
|
988 | echo 'not equal'
|
989 | fi
|
990 | ## STDOUT:
|
991 | not equal
|
992 | ## END
|
993 | ## N-I mksh/zsh/ash/dash stdout-json: ""
|
994 |
|
995 | #### %(strftime format)T TZ in environ but not in shell's memory
|
996 |
|
997 | # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
|
998 |
|
999 | # TZ is respected
|
1000 | export TZ=Portugal
|
1001 | tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
|
1002 |
|
1003 | unset TZ # unset in the shell, but still in the environment
|
1004 |
|
1005 | localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
|
1006 |
|
1007 | if ! test "$localtime" = "$tz"; then
|
1008 | echo 'not equal'
|
1009 | fi
|
1010 |
|
1011 | ## STDOUT:
|
1012 | not equal
|
1013 | ## END
|
1014 | ## N-I mksh/zsh/ash/dash stdout-json: ""
|
1015 |
|
1016 | #### %10.5(strftime format)T
|
1017 | # The result depends on timezone
|
1018 | export TZ=Asia/Tokyo
|
1019 | printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
|
1020 | export TZ=US/Eastern
|
1021 | printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
|
1022 | echo status=$?
|
1023 | ## STDOUT:
|
1024 | [ 2019-]
|
1025 | [ 2019-]
|
1026 | status=0
|
1027 | ## END
|
1028 | ## N-I dash/mksh/zsh/ash STDOUT:
|
1029 | [[status=1
|
1030 | ## END
|
1031 | ## N-I dash STDOUT:
|
1032 | [[status=2
|
1033 | ## END
|
1034 |
|
1035 | #### Regression for 'printf x y'
|
1036 | printf x y
|
1037 | printf '%s\n' z
|
1038 | ## STDOUT:
|
1039 | xz
|
1040 | ## END
|
1041 |
|
1042 | #### bash truncates long strftime string at 128
|
1043 |
|
1044 | case $SH in (ash|dash|mksh|zsh) exit ;; esac
|
1045 |
|
1046 | strftime-format() {
|
1047 | local n=$1
|
1048 |
|
1049 | # Prints increasingly long format strings:
|
1050 | # %(%Y)T %(%Y)T %(%Y%Y)T ...
|
1051 |
|
1052 | echo -n '%('
|
1053 | for i in $(seq $n); do
|
1054 | echo -n '%Y'
|
1055 | done
|
1056 | echo -n ')T'
|
1057 | }
|
1058 |
|
1059 | printf $(strftime-format 1) | wc --bytes
|
1060 | printf $(strftime-format 10) | wc --bytes
|
1061 | printf $(strftime-format 30) | wc --bytes
|
1062 | printf $(strftime-format 31) | wc --bytes
|
1063 | printf $(strftime-format 32) | wc --bytes
|
1064 |
|
1065 | case $SH in
|
1066 | (*/_bin/cxx-dbg/*)
|
1067 | # Ensure that oils-for-unix detects the truncation of a fixed buffer.
|
1068 | # bash has a buffer of 128.
|
1069 |
|
1070 | set +o errexit
|
1071 | (
|
1072 | printf $(strftime-format 1000)
|
1073 | )
|
1074 | status=$?
|
1075 | if test $status -ne 1; then
|
1076 | echo FAIL
|
1077 | fi
|
1078 | ;;
|
1079 | esac
|
1080 |
|
1081 | ## STDOUT:
|
1082 | 4
|
1083 | 40
|
1084 | 120
|
1085 | 124
|
1086 | 0
|
1087 | ## END
|
1088 | ## OK osh STDOUT:
|
1089 | 4
|
1090 | 40
|
1091 | 120
|
1092 | 124
|
1093 | 128
|
1094 | ## END
|
1095 |
|
1096 | ## N-I ash/dash/mksh/zsh STDOUT:
|
1097 | ## END
|
1098 |
|
1099 |
|
1100 | #### printf with explicit NUL byte
|
1101 | case $SH in (dash|ash) return ;; esac
|
1102 |
|
1103 | printf $'x\U0z'
|
1104 |
|
1105 | printf $'\U0z'
|
1106 |
|
1107 | ## stdout-json: "x"
|
1108 | ## OK zsh stdout-repr: "x\0z\0z"
|
1109 | ## N-I dash/ash stdout-json: ""
|
1110 |
|
1111 | #### printf positive integer overflow
|
1112 |
|
1113 | # %i seems like a synonym for %d
|
1114 |
|
1115 | for fmt in '%u\n' '%d\n'; do
|
1116 | # bash considers this in range for %u
|
1117 | # same with mksh
|
1118 | # zsh cuts everything off after 19 digits
|
1119 | # ash truncates everything
|
1120 | printf "$fmt" '18446744073709551615'
|
1121 | printf "$fmt" '18446744073709551616'
|
1122 | echo
|
1123 | done
|
1124 |
|
1125 | ## status: 1
|
1126 | ## STDOUT:
|
1127 | ## END
|
1128 |
|
1129 | ## OK bash/dash/mksh status: 0
|
1130 | ## OK bash/dash/mksh STDOUT:
|
1131 | 18446744073709551615
|
1132 | 18446744073709551615
|
1133 |
|
1134 | 9223372036854775807
|
1135 | 9223372036854775807
|
1136 |
|
1137 | ## END
|
1138 |
|
1139 | ## BUG ash status: 0
|
1140 | ## BUG ash STDOUT:
|
1141 | 18446744073709551615
|
1142 | 0
|
1143 |
|
1144 | 0
|
1145 | 0
|
1146 |
|
1147 | ## END
|
1148 |
|
1149 | ## BUG zsh status: 0
|
1150 | ## BUG zsh STDOUT:
|
1151 | 1844674407370955161
|
1152 | 1844674407370955161
|
1153 |
|
1154 | 1844674407370955161
|
1155 | 1844674407370955161
|
1156 |
|
1157 | ## END
|
1158 |
|
1159 | #### printf negative integer overflow
|
1160 |
|
1161 | # %i seems like a synonym for %d
|
1162 |
|
1163 | for fmt in '%u\n' '%d\n'; do
|
1164 |
|
1165 | #printf "$fmt" '-9223372036854775806'
|
1166 | #printf "$fmt" '-9223372036854775807'
|
1167 | #printf "$fmt" '-9223372036854775808'
|
1168 |
|
1169 | printf "$fmt" '-18446744073709551615'
|
1170 | printf "$fmt" '-18446744073709551616'
|
1171 | echo
|
1172 | done
|
1173 |
|
1174 | ## status: 1
|
1175 | ## STDOUT:
|
1176 | ## END
|
1177 |
|
1178 | ## OK bash/dash/mksh status: 0
|
1179 | ## OK bash/dash/mksh STDOUT:
|
1180 | 1
|
1181 | 18446744073709551615
|
1182 |
|
1183 | -9223372036854775808
|
1184 | -9223372036854775808
|
1185 |
|
1186 | ## END
|
1187 |
|
1188 | ## BUG zsh status: 0
|
1189 | ## BUG zsh STDOUT:
|
1190 | 16602069666338596455
|
1191 | 16602069666338596455
|
1192 |
|
1193 | -1844674407370955161
|
1194 | -1844674407370955161
|
1195 |
|
1196 | ## END
|
1197 |
|
1198 | ## BUG ash status: 0
|
1199 | ## BUG ash STDOUT:
|
1200 | 0
|
1201 | 0
|
1202 |
|
1203 | 0
|
1204 | 0
|
1205 |
|
1206 | ## END
|
1207 |
|