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