1 ## compare_shells: dash bash mksh zsh ash
2 ## oils_failures_allowed: 2
3 ## oils_cpp_failures_allowed: 1
4
5 #### NUL bytes with echo -e
6 case $SH in dash) exit ;; esac
7
8 show_hex() { od -A n -t c -t x1; }
9
10 echo -e '\0-' | show_hex
11 #echo -e '\x00-'
12 #echo -e '\000-'
13
14 ## STDOUT:
15 \0 - \n
16 00 2d 0a
17 ## END
18
19 ## BUG zsh STDOUT:
20 \0 \n
21 00 0a
22 ## END
23
24 ## N-I dash STDOUT:
25 ## END
26
27 #### printf - literal NUL in format string
28 case $SH in dash|ash) return ;; esac
29
30 # Show both printable and hex
31 show_hex() { od -A n -t c -t x1; }
32
33 printf $'x\U0z' | show_hex
34 echo ---
35
36 printf $'x\U00z' | show_hex
37 echo ---
38
39 printf $'\U0z' | show_hex
40
41 ## STDOUT:
42 x
43 78
44 ---
45 x
46 78
47 ---
48 ## END
49 ## BUG zsh STDOUT:
50 x \0 z
51 78 00 7a
52 ---
53 x \0 z
54 78 00 7a
55 ---
56 \0 z
57 00 7a
58 ## END
59 ## N-I dash/ash STDOUT:
60 ## END
61
62 #### printf - \0 escape shows NUL byte
63 show_hex() { od -A n -t c -t x1; }
64
65 printf '\0\n' | show_hex
66 ## STDOUT:
67 \0 \n
68 00 0a
69 ## END
70
71 #### printf - NUL byte in value (OSH and zsh agree)
72 case $SH in dash) exit ;; esac
73 show_hex() { od -A n -t c -t x1; }
74
75 nul=$'\0'
76 echo "$nul" | show_hex
77 printf '%s\n' "$nul" | show_hex
78
79 ## STDOUT:
80 \n
81 0a
82 \n
83 0a
84 ## END
85
86 ## OK osh/zsh STDOUT:
87 \0 \n
88 00 0a
89 \0 \n
90 00 0a
91 ## END
92 ## N-I dash stdout-json: ""
93
94 #### NUL bytes with echo $'\0' (OSH and zsh agree)
95 case $SH in dash) exit ;; esac
96 show_hex() { od -A n -t c -t x1; }
97
98 # OSH agrees with ZSH -- so you have the ability to print NUL bytes without
99 # legacy echo -e
100
101 echo $'\0' | show_hex
102
103 ## STDOUT:
104 \n
105 0a
106 ## END
107 ## OK osh/zsh STDOUT:
108 \0 \n
109 00 0a
110 ## END
111
112
113 ## N-I dash stdout-json: ""
114
115
116 #### NUL bytes and IFS splitting
117 case $SH in dash) exit ;; esac
118
119 argv.py $(echo -e '\0')
120 argv.py "$(echo -e '\0')"
121 argv.py $(echo -e 'a\0b')
122 argv.py "$(echo -e 'a\0b')"
123
124 ## STDOUT:
125 []
126 ['']
127 ['ab']
128 ['ab']
129 ## END
130 ## BUG zsh STDOUT:
131 ['', '']
132 ['']
133 ['a', 'b']
134 ['a']
135 ## END
136
137 ## N-I dash STDOUT:
138 ## END
139
140 #### NUL bytes with test -n
141
142 case $SH in dash) exit ;; esac
143
144 # zsh is buggy here, weird
145 test -n $''
146 echo status=$?
147
148 test -n $'\0'
149 echo status=$?
150
151
152 ## STDOUT:
153 status=1
154 status=1
155 ## END
156 ## OK osh STDOUT:
157 status=1
158 status=0
159 ## END
160 ## BUG zsh STDOUT:
161 status=0
162 status=0
163 ## END
164
165 ## N-I dash STDOUT:
166 ## END
167
168
169 #### NUL bytes with test -f
170
171 case $SH in dash) exit ;; esac
172
173
174 test -f $'\0'
175 echo status=$?
176
177 touch foo
178 test -f $'foo\0'
179 echo status=$?
180
181 test -f $'foo\0bar'
182 echo status=$?
183
184 test -f $'foobar'
185 echo status=$?
186
187
188 ## STDOUT:
189 status=1
190 status=0
191 status=0
192 status=1
193 ## END
194
195 ## OK ash STDOUT:
196 status=1
197 status=0
198 status=1
199 status=1
200 ## END
201
202 ## N-I dash STDOUT:
203 ## END
204
205
206 #### NUL bytes with ${#s} (OSH and zsh agree)
207
208 case $SH in dash) exit ;; esac
209
210 empty=$''
211 nul=$'\0'
212
213 echo empty=${#empty}
214 echo nul=${#nul}
215
216
217 ## STDOUT:
218 empty=0
219 nul=0
220 ## END
221
222 ## OK osh/zsh STDOUT:
223 empty=0
224 nul=1
225 ## END
226
227 ## N-I dash STDOUT:
228 ## END
229
230 #### Compare \x00 byte versus \x01 byte - command sub
231
232 # https://stackoverflow.com/questions/32722007/is-skipping-ignoring-nul-bytes-on-process-substitution-standardized
233 # bash contains a warning!
234
235 show_bytes() {
236 echo -n "$1" | od -A n -t x1
237 }
238
239 s=$(printf '.\001.')
240 echo len=${#s}
241 show_bytes "$s"
242
243 s=$(printf '.\000.')
244 echo len=${#s}
245 show_bytes "$s"
246
247 s=$(printf '\000')
248 echo len=${#s}
249 show_bytes "$s"
250
251 ## STDOUT:
252 len=3
253 2e 01 2e
254 len=2
255 2e 2e
256 len=0
257 ## END
258
259 ## BUG zsh STDOUT:
260 len=3
261 2e 01 2e
262 len=3
263 2e 00 2e
264 len=1
265 00
266 ## END
267
268 #### Compare \x00 byte versus \x01 byte - read builtin
269
270 # Hm same odd behavior
271
272 show_string() {
273 read s
274 echo len=${#s}
275 echo -n "$s" | od -A n -t x1
276 }
277
278 printf '.\001.' | show_string
279
280 printf '.\000.' | show_string
281
282 printf '\000' | show_string
283
284 ## STDOUT:
285 len=3
286 2e 01 2e
287 len=2
288 2e 2e
289 len=0
290 ## END
291
292 ## BUG zsh STDOUT:
293 len=3
294 2e 01 2e
295 len=3
296 2e 00 2e
297 len=1
298 00
299 ## END
300
301 #### Compare \x00 byte versus \x01 byte - read -n
302 case $SH in dash) exit ;; esac
303
304 show_string() {
305 read -n 3 s
306 echo len=${#s}
307 echo -n "$s" | od -A n -t x1
308 }
309
310
311 printf '.\001.' | show_string
312
313 printf '.\000.' | show_string
314
315 printf '\000' | show_string
316
317 ## STDOUT:
318 len=3
319 2e 01 2e
320 len=2
321 2e 2e
322 len=0
323 ## END
324
325 ## BUG-2 mksh STDOUT:
326 len=3
327 2e 01 2e
328 len=1
329 2e
330 len=0
331 ## END
332
333 ## BUG zsh STDOUT:
334 len=0
335 len=1
336 2e
337 len=0
338 ## END
339
340 ## N-I dash STDOUT:
341 ## END
342
343
344 #### Compare \x00 byte versus \x01 byte - mapfile builtin
345 case $SH in dash|mksh|zsh|ash) exit ;; esac
346
347 {
348 printf '.\000.\n'
349 printf '.\000.\n'
350 } |
351 { mapfile LINES
352 echo len=${#LINES[@]}
353 for line in ${LINES[@]}; do
354 echo -n "$line" | od -A n -t x1
355 done
356 }
357
358 # bash is INCONSISTENT:
359 # - it TRUNCATES at \0, with 'mapfile'
360 # - rather than just IGNORING \0, with 'read'
361
362 ## STDOUT:
363 len=2
364 2e
365 2e
366 ## END
367
368 ## N-I dash/mksh/zsh/ash STDOUT:
369 ## END
370
371 #### Strip ops # ## % %% with NUL bytes
372
373 show_bytes() {
374 echo -n "$1" | od -A n -t x1
375 }
376
377 s=$(printf '\000.\000')
378 echo len=${#s}
379 show_bytes "$s"
380
381 echo ---
382
383 t=${s#?}
384 echo len=${#t}
385 show_bytes "$t"
386
387 t=${s##?}
388 echo len=${#t}
389 show_bytes "$t"
390
391 t=${s%?}
392 echo len=${#t}
393 show_bytes "$t"
394
395 t=${s%%?}
396 echo len=${#t}
397 show_bytes "$t"
398
399 ## STDOUT:
400 len=1
401 2e
402 ---
403 len=0
404 len=0
405 len=0
406 len=0
407 ## END
408
409 ## BUG zsh STDOUT:
410 len=3
411 00 2e 00
412 ---
413 len=2
414 2e 00
415 len=2
416 2e 00
417 len=2
418 00 2e
419 len=2
420 00 2e
421 ## END
422
423 #### Issue 2269 Reduction
424
425 show_bytes() {
426 echo -n "$1" | od -A n -t x1
427 }
428
429 s=$(printf '\000x')
430 echo len=${#s}
431 show_bytes "$s"
432
433 # strip one char from the front
434 s=${s#?}
435 echo len=${#s}
436 show_bytes "$s"
437
438 echo ---
439
440 s=$(printf '\001x')
441 echo len=${#s}
442 show_bytes "$s"
443
444 # strip one char from the front
445 s=${s#?}
446 echo len=${#s}
447 show_bytes "$s"
448
449 ## STDOUT:
450 len=1
451 78
452 len=0
453 ---
454 len=2
455 01 78
456 len=1
457 78
458 ## END
459
460 ## BUG zsh STDOUT:
461 len=2
462 00 78
463 len=1
464 78
465 ---
466 len=2
467 01 78
468 len=1
469 78
470 ## END
471
472 #### Issue 2269 - Do NUL bytes match ? in ${a#?}
473
474 # https://github.com/oils-for-unix/oils/issues/2269
475
476 escape_arg() {
477 a="$1"
478 until [ -z "$a" ]; do
479 case "$a" in
480 (\'*) printf "'\"'\"'";;
481 (*) printf %.1s "$a";;
482 esac
483 a="${a#?}"
484 echo len=${#a} >&2
485 done
486 }
487
488 # encode
489 phrase="$(escape_arg "that's it!")"
490 echo escaped "$phrase"
491
492 # decode
493 eval "printf '%s\\n' '$phrase'"
494
495 echo ---
496
497 # harder input: NUL surrounded with ::
498 arg="$(printf ':\000:')"
499 #echo "arg=$arg"
500
501 case $SH in
502 zsh) echo 'writes binary data' ;;
503 *) echo escaped "$(escape_arg "$arg")" ;;
504 esac
505 #echo "arg=$arg"
506
507 ## STDOUT:
508 escaped that'"'"'s it!
509 that's it!
510 ---
511 escaped ::
512 ## END
513
514 ## OK zsh STDOUT:
515 escaped that'"'"'s it!
516 that's it!
517 ---
518 writes binary data
519 ## END