1 | ## oils_failures_allowed: 2
|
2 | ## compare_shells: bash dash mksh
|
3 |
|
4 | #### >& and <& are the same
|
5 |
|
6 | echo one 1>&2
|
7 |
|
8 | echo two 1<&2
|
9 |
|
10 | ## STDERR:
|
11 | one
|
12 | two
|
13 | ## END
|
14 |
|
15 |
|
16 | #### <&
|
17 | # Is there a simpler test case for this?
|
18 | echo foo > $TMP/lessamp.txt
|
19 |
|
20 | exec 6< $TMP/lessamp.txt
|
21 | read line <&6
|
22 |
|
23 | echo "[$line]"
|
24 | ## stdout: [foo]
|
25 |
|
26 | #### 2>&1 with no command
|
27 | ( exit 42 ) # status is reset after this
|
28 | echo status=$?
|
29 | 2>&1
|
30 | echo status=$?
|
31 | ## STDOUT:
|
32 | status=42
|
33 | status=0
|
34 | ## END
|
35 | ## stderr-json: ""
|
36 |
|
37 |
|
38 | #### 2&>1 (is it a redirect or is it like a&>1)
|
39 | 2&>1
|
40 | echo status=$?
|
41 | ## STDOUT:
|
42 | status=127
|
43 | ## END
|
44 | ## OK mksh/dash STDOUT:
|
45 | status=0
|
46 | ## END
|
47 |
|
48 |
|
49 | #### Nonexistent file
|
50 | cat <$TMP/nonexistent.txt
|
51 | echo status=$?
|
52 | ## stdout: status=1
|
53 | ## OK dash stdout: status=2
|
54 |
|
55 | #### Descriptor redirect with spaces
|
56 | # Hm this seems like a failure of lookahead! The second thing should look to a
|
57 | # file-like thing.
|
58 | # I think this is a posix issue.
|
59 | # tag: posix-issue
|
60 | echo one 1>&2
|
61 | echo two 1 >&2
|
62 | echo three 1>& 2
|
63 | ## stderr-json: "one\ntwo 1\nthree\n"
|
64 |
|
65 | #### Filename redirect with spaces
|
66 | # This time 1 *is* a descriptor, not a word. If you add a space between 1 and
|
67 | # >, it doesn't work.
|
68 | echo two 1> $TMP/file-redir1.txt
|
69 | cat $TMP/file-redir1.txt
|
70 | ## stdout: two
|
71 |
|
72 | #### Quoted filename redirect with spaces
|
73 | # POSIX makes node of this
|
74 | echo two \1 > $TMP/file-redir2.txt
|
75 | cat $TMP/file-redir2.txt
|
76 | ## stdout: two 1
|
77 |
|
78 | #### Descriptor redirect with filename
|
79 | # bash/mksh treat this like a filename, not a descriptor.
|
80 | # dash aborts.
|
81 | echo one 1>&$TMP/nonexistent-filename__
|
82 | echo "status=$?"
|
83 | ## stdout: status=1
|
84 | ## BUG bash stdout: status=0
|
85 | ## OK dash stdout-json: ""
|
86 | ## OK dash status: 2
|
87 |
|
88 | #### Redirect echo to stderr, and then redirect all of stdout somewhere.
|
89 | { echo foo 1>&2; echo 012345789; } > $TMP/block-stdout.txt
|
90 | cat $TMP/block-stdout.txt | wc -c
|
91 | ## stderr: foo
|
92 | ## stdout: 10
|
93 |
|
94 | #### Named file descriptor
|
95 | exec {myfd}> $TMP/named-fd.txt
|
96 | echo named-fd-contents >& $myfd
|
97 | cat $TMP/named-fd.txt
|
98 | ## stdout: named-fd-contents
|
99 | ## status: 0
|
100 | ## N-I dash/mksh stdout-json: ""
|
101 | ## N-I dash/mksh status: 127
|
102 |
|
103 | #### Double digit fd (20> file)
|
104 | exec 20> "$TMP/double-digit-fd.txt"
|
105 | echo hello20 >&20
|
106 | cat "$TMP/double-digit-fd.txt"
|
107 | ## stdout: hello20
|
108 | ## BUG dash stdout-json: ""
|
109 | ## BUG dash status: 127
|
110 |
|
111 | #### : 9> fdleak (OSH regression)
|
112 | true 9> "$TMP/fd.txt"
|
113 | ( echo world >&9 )
|
114 | cat "$TMP/fd.txt"
|
115 | ## stdout-json: ""
|
116 |
|
117 | #### : 3>&3 (OSH regression)
|
118 |
|
119 | # mksh started being flaky on the continuous build and during release. We
|
120 | # don't care! Related to issue #330.
|
121 | case $SH in (mksh) exit ;; esac
|
122 |
|
123 | : 3>&3
|
124 | echo hello
|
125 | ## stdout: hello
|
126 | ## BUG mksh stdout-json: ""
|
127 | ## BUG mksh status: 0
|
128 |
|
129 | #### : 3>&3-
|
130 | : 3>&3-
|
131 | echo hello
|
132 | ## stdout: hello
|
133 | ## N-I dash/mksh stdout-json: ""
|
134 | ## N-I mksh status: 1
|
135 | ## N-I dash status: 2
|
136 |
|
137 | #### 3>&- << EOF (OSH regression: fail to restore fds)
|
138 | exec 3> "$TMP/fd.txt"
|
139 | echo hello 3>&- << EOF
|
140 | EOF
|
141 | echo world >&3
|
142 | exec 3>&- # close
|
143 | cat "$TMP/fd.txt"
|
144 | ## STDOUT:
|
145 | hello
|
146 | world
|
147 | ## END
|
148 |
|
149 | #### Open file on descriptor 3 and write to it many times
|
150 |
|
151 | # different than case below because 3 is the likely first FD of open()
|
152 |
|
153 | exec 3> "$TMP/fd3.txt"
|
154 | echo hello >&3
|
155 | echo world >&3
|
156 | exec 3>&- # close
|
157 | cat "$TMP/fd3.txt"
|
158 | ## STDOUT:
|
159 | hello
|
160 | world
|
161 | ## END
|
162 |
|
163 | #### Open file on descriptor 4 and write to it many times
|
164 |
|
165 | # different than the case above because because 4 isn't the likely first FD
|
166 |
|
167 | exec 4> "$TMP/fd4.txt"
|
168 | echo hello >&4
|
169 | echo world >&4
|
170 | exec 4>&- # close
|
171 | cat "$TMP/fd4.txt"
|
172 | ## STDOUT:
|
173 | hello
|
174 | world
|
175 | ## END
|
176 |
|
177 | #### Redirect to empty string
|
178 | f=''
|
179 | echo s > "$f"
|
180 | echo "result=$?"
|
181 | set -o errexit
|
182 | echo s > "$f"
|
183 | echo DONE
|
184 | ## stdout: result=1
|
185 | ## status: 1
|
186 | ## OK dash stdout: result=2
|
187 | ## OK dash status: 2
|
188 |
|
189 | #### Redirect to file descriptor that's not open
|
190 | # Notes:
|
191 | # - 7/2021: descriptor 7 seems to work on all CI systems. The process state
|
192 | # isn't clean, but we could probably close it in OSH?
|
193 | # - dash doesn't allow file descriptors greater than 9. (This is a good
|
194 | # thing, because the bash chapter in AOSA book mentions that juggling user
|
195 | # vs. system file descriptors is a huge pain.)
|
196 | # - But somehow running in parallel under spec-runner.sh changes whether
|
197 | # descriptor 3 is open. e.g. 'echo hi 1>&3'. Possibly because of
|
198 | # /usr/bin/time. The _tmp/spec/*.task.txt file gets corrupted!
|
199 | # - Oh this is because I use time --output-file. That opens descriptor 3. And
|
200 | # then time forks the shell script. The file descriptor table is inherited.
|
201 | # - You actually have to set the file descriptor to something. What do
|
202 | # configure and debootstrap too?
|
203 |
|
204 | opened=$(ls /proc/$$/fd)
|
205 | if echo "$opened" | egrep '^7$'; then
|
206 | echo "FD 7 shouldn't be open"
|
207 | echo "OPENED:"
|
208 | echo "$opened"
|
209 | fi
|
210 |
|
211 | echo hi 1>&7
|
212 | ## stdout-json: ""
|
213 | ## status: 1
|
214 | ## OK dash status: 2
|
215 |
|
216 | #### Open descriptor with exec
|
217 | # What is the point of this? ./configure scripts and debootstrap use it.
|
218 | exec 3>&1
|
219 | echo hi 1>&3
|
220 | ## stdout: hi
|
221 | ## status: 0
|
222 |
|
223 | #### Open multiple descriptors with exec
|
224 | # What is the point of this? ./configure scripts and debootstrap use it.
|
225 | exec 3>&1
|
226 | exec 4>&1
|
227 | echo three 1>&3
|
228 | echo four 1>&4
|
229 | ## stdout-json: "three\nfour\n"
|
230 | ## status: 0
|
231 |
|
232 | #### >| to clobber
|
233 | echo XX >| $TMP/c.txt
|
234 |
|
235 | set -o noclobber
|
236 |
|
237 | echo YY > $TMP/c.txt # not clobber
|
238 | echo status=$?
|
239 |
|
240 | cat $TMP/c.txt
|
241 | echo ZZ >| $TMP/c.txt
|
242 |
|
243 | cat $TMP/c.txt
|
244 | ## STDOUT:
|
245 | status=1
|
246 | XX
|
247 | ZZ
|
248 | ## END
|
249 | ## OK dash STDOUT:
|
250 | status=2
|
251 | XX
|
252 | ZZ
|
253 | ## END
|
254 |
|
255 | #### &> redirects stdout and stderr
|
256 | tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
|
257 | #echo $tmp
|
258 |
|
259 | stdout_stderr.py &> $tmp
|
260 |
|
261 | # order is indeterminate
|
262 | grep STDOUT $tmp
|
263 | grep STDERR $tmp
|
264 |
|
265 | ## STDOUT:
|
266 | STDOUT
|
267 | STDERR
|
268 | ## END
|
269 | ## N-I dash stdout: STDOUT
|
270 | ## N-I dash stderr: STDERR
|
271 | ## N-I dash status: 1
|
272 |
|
273 | #### >&word redirects stdout and stderr when word is not a number or -
|
274 |
|
275 | # dash, mksh don't implement this bash behaviour.
|
276 | case $SH in (dash|mksh) exit 1 ;; esac
|
277 |
|
278 | tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
|
279 |
|
280 | stdout_stderr.py >&$tmp
|
281 |
|
282 | # order is indeterminate
|
283 | grep STDOUT $tmp
|
284 | grep STDERR $tmp
|
285 |
|
286 | ## STDOUT:
|
287 | STDOUT
|
288 | STDERR
|
289 | ## END
|
290 | ## N-I dash/mksh status: 1
|
291 | ## N-I dash/mksh stdout-json: ""
|
292 |
|
293 | #### 1>&- to close file descriptor
|
294 | exec 5> "$TMP/f.txt"
|
295 | echo hello >&5
|
296 | exec 5>&-
|
297 | echo world >&5
|
298 | cat "$TMP/f.txt"
|
299 | ## stdout-json: "hello\n"
|
300 |
|
301 | #### 1>&2- to move file descriptor
|
302 | exec 5> "$TMP/f.txt"
|
303 | echo hello5 >&5
|
304 | exec 6>&5-
|
305 | echo world5 >&5
|
306 | echo world6 >&6
|
307 | exec 6>&-
|
308 | cat "$TMP/f.txt"
|
309 | ## stdout-json: "hello5\nworld6\n"
|
310 | ## N-I dash status: 2
|
311 | ## N-I dash stdout-json: ""
|
312 | ## N-I mksh status: 1
|
313 | ## N-I mksh stdout-json: ""
|
314 |
|
315 | #### 1>&2- (Bash bug: fail to restore closed fd)
|
316 |
|
317 | # 7/2021: descriptor 8 is open on Github Actions, so use descriptor 6 instead
|
318 |
|
319 | # Fix for CI systems where process state isn't clean: Close descriptors 6 and 7.
|
320 | exec 6>&- 7>&-
|
321 |
|
322 | opened=$(ls /proc/$$/fd)
|
323 | if echo "$opened" | egrep '^7$'; then
|
324 | echo "FD 7 shouldn't be open"
|
325 | echo "OPENED:"
|
326 | echo "$opened"
|
327 | fi
|
328 | if echo "$opened" | egrep '^6$'; then
|
329 | echo "FD 6 shouldn't be open"
|
330 | echo "OPENED:"
|
331 | echo "$opened"
|
332 | fi
|
333 |
|
334 | exec 7> "$TMP/f.txt"
|
335 | : 6>&7 7>&-
|
336 | echo hello >&7
|
337 | : 6>&7-
|
338 | echo world >&7
|
339 | exec 7>&-
|
340 | cat "$TMP/f.txt"
|
341 |
|
342 | ## status: 1
|
343 | ## stdout-json: ""
|
344 |
|
345 | ## OK dash status: 2
|
346 |
|
347 | ## BUG bash status: 0
|
348 | ## BUG bash stdout: hello
|
349 |
|
350 | #### <> for read/write
|
351 | echo first >$TMP/rw.txt
|
352 | exec 8<>$TMP/rw.txt
|
353 | read line <&8
|
354 | echo line=$line
|
355 | echo second 1>&8
|
356 | echo CONTENTS
|
357 | cat $TMP/rw.txt
|
358 | ## stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n"
|
359 |
|
360 | #### <> for read/write named pipes
|
361 | rm -f "$TMP/f.pipe"
|
362 | mkfifo "$TMP/f.pipe"
|
363 | exec 8<> "$TMP/f.pipe"
|
364 | echo first >&8
|
365 | echo second >&8
|
366 | read line1 <&8
|
367 | read line2 <&8
|
368 | exec 8<&-
|
369 | echo line1=$line1 line2=$line2
|
370 | ## stdout: line1=first line2=second
|
371 |
|
372 | #### &>> appends stdout and stderr
|
373 |
|
374 | # Fix for flaky tests: dash behaves non-deterministically under load! It
|
375 | # doesn't implement the behavior anyway so I don't care why.
|
376 | case $SH in
|
377 | *dash)
|
378 | exit 1
|
379 | ;;
|
380 | esac
|
381 |
|
382 | echo "ok" > $TMP/f.txt
|
383 | stdout_stderr.py &>> $TMP/f.txt
|
384 | grep ok $TMP/f.txt >/dev/null && echo 'ok'
|
385 | grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
|
386 | grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
|
387 | ## STDOUT:
|
388 | ok
|
389 | ok
|
390 | ok
|
391 | ## END
|
392 | ## N-I dash stdout-json: ""
|
393 | ## N-I dash status: 1
|
394 |
|
395 | #### exec redirect then various builtins
|
396 | exec 5>$TMP/log.txt
|
397 | echo hi >&5
|
398 | set -o >&5
|
399 | echo done
|
400 | ## STDOUT:
|
401 | done
|
402 | ## END
|
403 |
|
404 | #### can't mention big file descriptor
|
405 | echo hi 9>&1
|
406 | # trivia: 23 is the max descriptor for mksh
|
407 | #echo hi 24>&1
|
408 | echo hi 99>&1
|
409 | echo hi 100>&1
|
410 | ## OK osh STDOUT:
|
411 | hi
|
412 | hi
|
413 | hi 100
|
414 | ## END
|
415 | ## STDOUT:
|
416 | hi
|
417 | hi 99
|
418 | hi 100
|
419 | ## END
|
420 | ## BUG bash STDOUT:
|
421 | hi
|
422 | hi
|
423 | hi
|
424 | ## END
|
425 |
|
426 | #### : >/dev/null 2> / (OSH regression: fail to pop fd frame)
|
427 | # oil 0.8.pre4 fails to restore fds after redirection failure. In the
|
428 | # following case, the fd frame remains after the redirection failure
|
429 | # "2> /" so that the effect of redirection ">/dev/null" remains after
|
430 | # the completion of the command.
|
431 | : >/dev/null 2> /
|
432 | echo hello
|
433 | ## stdout: hello
|
434 | ## OK dash stdout-json: ""
|
435 | ## OK dash status: 2
|
436 | ## OK mksh stdout-json: ""
|
437 | ## OK mksh status: 1
|
438 | # dash/mksh terminates the execution of script on the redirection.
|
439 |
|
440 | #### echo foo >&100 (OSH regression: does not fail with invalid fd 100)
|
441 | # oil 0.8.pre4 does not fail with non-existent fd 100.
|
442 | fd=100
|
443 | echo foo >&$fd
|
444 | ## stdout-json: ""
|
445 | ## status: 1
|
446 | ## OK dash status: 2
|
447 |
|
448 | #### echo foo >&N where N is first unused fd
|
449 | # 1. prepare default fd for internal uses
|
450 | minfd=10
|
451 | case ${SH##*/} in
|
452 | (mksh) minfd=24 ;;
|
453 | (osh) minfd=100 ;;
|
454 | esac
|
455 |
|
456 | # 2. prepare first unused fd
|
457 | fd=$minfd
|
458 | is-fd-open() { : >&$1; }
|
459 | while is-fd-open "$fd"; do
|
460 | : $((fd+=1))
|
461 |
|
462 | # prevent infinite loop for broken oils-for-unix
|
463 | if test $fd -gt 1000; then
|
464 | break
|
465 | fi
|
466 | done
|
467 |
|
468 | # 3. test
|
469 | echo foo >&$fd
|
470 | ## stdout-json: ""
|
471 | ## status: 1
|
472 | ## OK dash status: 2
|
473 |
|
474 | #### exec {fd}>&- (OSH regression: fails to close fd)
|
475 | # mksh, dash do not implement {fd} redirections.
|
476 | case $SH in (mksh|dash) exit 1 ;; esac
|
477 | # oil 0.8.pre4 fails to close fd by {fd}&-.
|
478 | exec {fd}>file1
|
479 | echo foo >&$fd
|
480 | exec {fd}>&-
|
481 | echo bar >&$fd
|
482 | cat file1
|
483 | ## stdout: foo
|
484 | ## N-I mksh/dash stdout-json: ""
|
485 | ## N-I mksh/dash status: 1
|