1 #!/usr/bin/env bash
2 #
3 # printf
4 # bash-completion uses this odd printf -v construction. It seems to mostly use
5 # %s and %q though.
6 #
7 # %s should just be
8 # declare $var='val'
9 #
10 # NOTE:
11 # /usr/bin/printf %q "'" seems wrong.
12 # $ /usr/bin/printf %q "'"
13 # ''\'''
14 #
15 # I suppose it is technically correct, but it looks very ugly.
16
17 #### printf with no args
18 printf
19 ## status: 2
20 ## OK dash/mksh/zsh status: 1
21 ## stdout-json: ""
22
23 #### printf -v %s
24 var=foo
25 printf -v $var %s 'hello there'
26 argv.py "$foo"
27 ## STDOUT:
28 ['hello there']
29 ## END
30 ## N-I mksh/zsh/ash STDOUT:
31 -v['']
32 ## END
33 ## N-I dash STDOUT:
34 ['']
35 ## END
36
37 #### printf -v %q
38 val='"quoted" with spaces and \'
39
40 # quote 'val' and store it in foo
41 printf -v foo %q "$val"
42 # then round trip back to eval
43 eval "bar=$foo"
44
45 # debugging:
46 #echo foo="$foo"
47 #echo bar="$bar"
48 #echo val="$val"
49
50 test "$bar" = "$val" && echo OK
51 ## STDOUT:
52 OK
53 ## END
54 ## N-I mksh/zsh/ash stdout-json: "-v"
55 ## N-I mksh/zsh/ash status: 1
56 ## N-I dash stdout-json: ""
57 ## N-I dash status: 1
58
59 #### printf -v a[1]
60 a=(a b c)
61 printf -v 'a[1]' %s 'foo'
62 echo status=$?
63 argv.py "${a[@]}"
64 ## STDOUT:
65 status=0
66 ['a', 'foo', 'c']
67 ## END
68 ## N-I mksh/zsh STDOUT:
69 -vstatus=0
70 ['a', 'b', 'c']
71 ## END
72 ## N-I dash/ash stdout-json: ""
73 ## N-I dash/ash status: 2
74 ## N-I osh STDOUT:
75 status=2
76 ['a', 'b', 'c']
77 ## END
78
79 #### dynamic declare instead of %s
80 var=foo
81 declare $var='hello there'
82 argv.py "$foo"
83 ## STDOUT:
84 ['hello there']
85 ## END
86 ## N-I dash/mksh/ash STDOUT:
87 ['']
88 ## END
89
90 #### dynamic declare instead of %q
91 var=foo
92 val='"quoted" with spaces and \'
93 # I think this is bash 4.4 only.
94 declare $var="${val@Q}"
95 echo "$foo"
96 ## STDOUT:
97 '"quoted" with spaces and \'
98 ## END
99 ## N-I dash/ash stdout-json: ""
100 ## N-I dash/ash status: 2
101 ## N-I mksh stdout-json: "\n"
102 ## N-I zsh stdout-json: ""
103 ## N-I zsh status: 1
104
105 #### printf -v dynamic scope
106 case $SH in *mksh|*zsh|*dash|*/ash) echo not implemented; exit ;; esac
107 # OK so printf is like assigning to a var.
108 # printf -v foo %q "$bar" is like
109 # foo=${bar@Q}
110 dollar='dollar'
111 f() {
112 local mylocal=foo
113 printf -v dollar %q '$' # assign foo to a quoted dollar
114 printf -v mylocal %q 'mylocal'
115 echo dollar=$dollar
116 echo mylocal=$mylocal
117 }
118 echo dollar=$dollar
119 echo --
120 f
121 echo --
122 echo dollar=$dollar
123 echo mylocal=$mylocal
124 ## STDOUT:
125 dollar=dollar
126 --
127 dollar=\$
128 mylocal=mylocal
129 --
130 dollar=\$
131 mylocal=
132 ## END
133 ## OK osh STDOUT:
134 dollar=dollar
135 --
136 dollar='$'
137 mylocal='mylocal'
138 --
139 dollar='$'
140 mylocal=
141 ## END
142 ## N-I dash/ash/mksh/zsh STDOUT:
143 not implemented
144 ## END
145
146 #### printf with too few arguments
147 printf -- '-%s-%s-%s-\n' 'a b' 'x y'
148 ## STDOUT:
149 -a b-x y--
150 ## END
151
152 #### printf with too many arguments
153 printf -- '-%s-%s-\n' a b c d e
154 ## STDOUT:
155 -a-b-
156 -c-d-
157 -e--
158 ## END
159
160 #### printf width strings
161 printf '[%5s]\n' abc
162 printf '[%-5s]\n' abc
163 ## STDOUT:
164 [ abc]
165 [abc ]
166 ## END
167
168 #### printf integer
169 printf '%d\n' 42
170 printf '%i\n' 42 # synonym
171 printf '[%5d]\n' 42
172 printf '[%-5d]\n' 42
173 printf '[%05d]\n' 42
174 #printf '[%-05d]\n' 42 # the leading 0 is meaningless
175 #[42 ]
176 ## STDOUT:
177 42
178 42
179 [ 42]
180 [42 ]
181 [00042]
182 ## END
183
184 #### printf %6.4d -- precision means something different for integers !?
185 printf '[%6.4d]\n' 42
186 ## STDOUT:
187 [ 0042]
188 ## END
189 ## N-I osh stdout-json: ""
190 ## N-I osh status: 2
191
192 #### printf %6.4s does both truncation and padding
193 printf '[%6s]\n' foo
194 printf '[%6.4s]\n' foo
195 printf '[%-6.4s]\n' foo
196 printf '[%6s]\n' spam-eggs
197 printf '[%6.4s]\n' spam-eggs
198 printf '[%-6.4s]\n' spam-eggs
199 ## STDOUT:
200 [ foo]
201 [ foo]
202 [foo ]
203 [spam-eggs]
204 [ spam]
205 [spam ]
206 ## END
207
208 #### printf %6.0s and %0.0s
209 printf '[%6.0s]\n' foo
210 printf '[%0.0s]\n' foo
211 ## STDOUT:
212 [ ]
213 []
214 ## END
215 ## N-I mksh stdout-json: "[ ]\n["
216 ## N-I mksh status: 1
217
218 #### unsigned / octal / hex
219 printf '[%u]\n' 42
220 printf '[%o]\n' 42
221 printf '[%x]\n' 42
222 printf '[%X]\n' 42
223 ## STDOUT:
224 [42]
225 [52]
226 [2a]
227 [2A]
228 ## END
229
230 #### negative numbers with unsigned / octal / hex
231 printf '[%u]\n' -42
232 printf '[%o]\n' -42
233 printf '[%x]\n' -42
234 printf '[%X]\n' -42
235 ## STDOUT:
236 [18446744073709551574]
237 [1777777777777777777726]
238 [ffffffffffffffd6]
239 [FFFFFFFFFFFFFFD6]
240 ## END
241
242 # osh DISALLOWS this because the output depends on the machine architecture.
243 ## N-I osh stdout-json: ""
244 ## N-I osh status: 1
245
246 #### printf floating point (not required, but they all implement it)
247 printf '[%f]\n' 3.14159
248 printf '[%.2f]\n' 3.14159
249 printf '[%8.2f]\n' 3.14159
250 printf '[%-8.2f]\n' 3.14159
251 printf '[%-f]\n' 3.14159
252 printf '[%-f]\n' 3.14
253 ## STDOUT:
254 [3.141590]
255 [3.14]
256 [ 3.14]
257 [3.14 ]
258 [3.141590]
259 [3.140000]
260 ## END
261 ## N-I osh stdout-json: ""
262 ## N-I osh status: 2
263
264 #### printf floating point with - and 0
265 printf '[%8.4f]\n' 3.14
266 printf '[%08.4f]\n' 3.14
267 printf '[%8.04f]\n' 3.14 # meaning less 0
268 printf '[%08.04f]\n' 3.14
269 echo ---
270 # these all boil down to the same thing. The -, 8, and 4 are respected, but
271 # none of the 0 are.
272 printf '[%-8.4f]\n' 3.14
273 printf '[%-08.4f]\n' 3.14
274 printf '[%-8.04f]\n' 3.14
275 printf '[%-08.04f]\n' 3.14
276 ## STDOUT:
277 [ 3.1400]
278 [003.1400]
279 [ 3.1400]
280 [003.1400]
281 ---
282 [3.1400 ]
283 [3.1400 ]
284 [3.1400 ]
285 [3.1400 ]
286 ## END
287 ## N-I osh STDOUT:
288 ---
289 ## END
290 ## N-I osh status: 2
291
292 #### printf eE fF gG
293 printf '[%e]\n' 3.14
294 printf '[%E]\n' 3.14
295 printf '[%f]\n' 3.14
296 # bash is the only one that implements %F? Is it a synonym?
297 #printf '[%F]\n' 3.14
298 printf '[%g]\n' 3.14
299 printf '[%G]\n' 3.14
300 ## STDOUT:
301 [3.140000e+00]
302 [3.140000E+00]
303 [3.140000]
304 [3.14]
305 [3.14]
306 ## END
307 ## N-I osh stdout-json: ""
308 ## N-I osh status: 2
309
310 #### printf backslash escapes
311 argv.py "$(printf 'a\tb')"
312 argv.py "$(printf '\xE2\x98\xA0')"
313 argv.py "$(printf '\044e')"
314 argv.py "$(printf '\0377')" # out of range
315 ## STDOUT:
316 ['a\tb']
317 ['\xe2\x98\xa0']
318 ['$e']
319 ['\x1f7']
320 ## END
321 ## N-I dash STDOUT:
322 ['a\tb']
323 ['\\xE2\\x98\\xA0']
324 ['$e']
325 ['\x1f7']
326 ## END
327
328 #### printf octal backslash escapes
329 argv.py "$(printf '\0377')"
330 argv.py "$(printf '\377')"
331 ## STDOUT:
332 ['\x1f7']
333 ['\xff']
334 ## END
335
336 #### printf unicode backslash escapes
337 argv.py "$(printf '\u2620')"
338 argv.py "$(printf '\U0000065f')"
339 ## STDOUT:
340 ['\xe2\x98\xa0']
341 ['\xd9\x9f']
342 ## END
343 ## N-I dash/ash STDOUT:
344 ['\\u2620']
345 ['\\U0000065f']
346 ## END
347
348 #### printf invalid backslash escape (is ignored)
349 printf '[\Z]\n'
350 ## STDOUT:
351 [\Z]
352 ## END
353
354 #### printf % escapes
355 printf '[%%]\n'
356 ## STDOUT:
357 [%]
358 ## END
359
360 #### printf %b backslash escaping
361 printf '[%s]\n' '\044' # escapes not evaluated
362 printf '[%b]\n' '\044' # YES, escapes evaluated
363 echo status=$?
364 ## STDOUT:
365 [\044]
366 [$]
367 status=0
368 ## END
369 ## N-I osh STDOUT:
370 [\044]
371 status=2
372 ## END
373
374 #### printf %c -- doesn't respect UTF-8! Bad.
375 twomu=$'\u03bc\u03bc'
376 printf '[%s]\n' "$twomu"
377 printf '%c' "$twomu" | wc --bytes
378 ## STDOUT:
379 [μμ]
380 1
381 ## END
382 ## N-I dash STDOUT:
383 [$\u03bc\u03bc]
384 1
385 ## END
386 ## N-I ash STDOUT:
387 [\u03bc\u03bc]
388 1
389 ## END
390 ## N-I osh STDOUT:
391 [μμ]
392 0
393 ## END
394
395 #### printf invalid format
396 printf '%z' 42
397 echo status=$?
398 printf '%-z' 42
399 echo status=$?
400 ## STDOUT:
401 status=1
402 status=1
403 ## END
404 # osh emits parse errors
405 ## OK osh STDOUT:
406 status=2
407 status=2
408 ## END
409 ## BUG ash STDOUT:
410 status=0
411 status=0
412 ## END
413
414 #### printf %q
415 x='a b'
416 printf '[%q]\n' "$x"
417 ## STDOUT:
418 ['a b']
419 ## END
420 ## OK bash/zsh STDOUT:
421 [a\ b]
422 ## END
423 ## N-I ash/dash stdout-json: "["
424 ## N-I ash/dash status: 1
425
426 #### printf %6q (width)
427 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
428 x='a b'
429 printf '[%6q]\n' "$x"
430 ## STDOUT:
431 [ 'a b']
432 ## END
433 ## OK bash/zsh STDOUT:
434 [ a\ b]
435 ## END
436 ## N-I mksh/ash/dash stdout-json: "["
437 ## N-I mksh/ash/dash status: 1
438
439 #### printf + and space flags
440 # I didn't know these existed -- I only knew about - and 0 !
441 printf '[%+d]\n' 42
442 printf '[%+d]\n' -42
443 printf '[% d]\n' 42
444 printf '[% d]\n' -42
445 ## STDOUT:
446 [+42]
447 [-42]
448 [ 42]
449 [-42]
450 ## END
451 ## N-I osh stdout-json: ""
452 ## N-I osh status: 2
453
454 #### printf # flag
455 # I didn't know these existed -- I only knew about - and 0 !
456 printf '[%#o]\n' 42
457 printf '[%#x]\n' 42
458 printf '[%#X]\n' 42
459 echo ---
460 printf '[%#f]\n' 3
461 ## STDOUT:
462 [052]
463 [0x2a]
464 [0X2A]
465 ---
466 [3.000000]
467 ## END
468 ## N-I osh STDOUT:
469 ---
470 ## END
471 ## N-I osh status: 2
472
473 #### Runtime error for invalid integer
474 x=3abc
475 printf '%d\n' $x
476 echo status=$?
477 printf '%d\n' xyz
478 echo status=$?
479 ## STDOUT:
480 3
481 status=1
482 0
483 status=1
484 ## END
485 # zsh should exit 1 in both cases
486 ## BUG zsh STDOUT:
487 0
488 status=1
489 0
490 status=0
491 ## END
492 # fails but also prints 0 instead of 3abc
493 ## BUG ash STDOUT:
494 0
495 status=1
496 0
497 status=1
498 ## END
499 # osh doesn't print anything invalid
500 ## OK osh STDOUT:
501 status=1
502 status=1
503 ## END
504
505 #### %(strftime format)T
506 printf '%(%Y-%m-%d)T\n' 1557978599
507 echo status=$?
508 ## STDOUT:
509 2019-05-15
510 status=0
511 ## END
512 ## N-I dash/mksh/zsh/ash STDOUT:
513 status=1
514 ## END
515 ## N-I osh STDOUT:
516 status=2
517 ## END