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