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