1
2 #### >&
3 echo hi 1>&2
4 ## stderr: hi
5
6 #### <&
7 # Is there a simpler test case for this?
8 echo foo > $TMP/lessamp.txt
9 exec 6< $TMP/lessamp.txt
10 read line <&6
11 echo "[$line]"
12 ## stdout: [foo]
13
14 #### Leading redirect
15 echo hello >$TMP/hello.txt # temporary fix
16 <$TMP/hello.txt cat
17 ## stdout: hello
18
19 #### Nonexistent file
20 cat <$TMP/nonexistent.txt
21 echo status=$?
22 ## stdout: status=1
23 ## OK dash stdout: status=2
24
25 #### Redirect in command sub
26 FOO=$(echo foo 1>&2)
27 echo $FOO
28 ## stdout:
29 ## stderr: foo
30
31 #### Redirect in assignment
32 # dash captures stderr to a file here, which seems correct. Bash doesn't and
33 # just lets it go to actual stderr.
34 # For now we agree with dash/mksh, since it involves fewer special cases in the
35 # code.
36
37 FOO=$(echo foo 1>&2) 2>$TMP/no-command.txt
38 echo FILE=
39 cat $TMP/no-command.txt
40 echo "FOO=$FOO"
41 ## STDOUT:
42 FILE=
43 foo
44 FOO=
45 ## END
46 ## BUG bash STDOUT:
47 FILE=
48 FOO=
49 ## END
50
51 #### Redirect in function body.
52 fun() { echo hi; } 1>&2
53 fun
54 ## stdout-json: ""
55 ## stderr-json: "hi\n"
56
57 #### Bad redirects in function body
58 empty=''
59 fun() { echo hi; } > $empty
60 fun
61 echo status=$?
62 ## stdout: status=1
63 ## OK dash stdout: status=2
64
65 #### Redirect in function body is evaluated multiple times
66 i=0
67 fun() { echo "file $i"; } 1> "$TMP/file$((i++))"
68 fun
69 fun
70 echo i=$i
71 echo __
72 cat $TMP/file0
73 echo __
74 cat $TMP/file1
75 ## STDOUT:
76 i=2
77 __
78 file 1
79 __
80 file 2
81 ## END
82 ## N-I dash stdout-json: ""
83 ## N-I dash status: 2
84
85 #### Redirect in function body AND function call
86 fun() { echo hi; } 1>&2
87 fun 2>&1
88 ## stdout-json: "hi\n"
89 ## stderr-json: ""
90
91 #### Descriptor redirect with spaces
92 # Hm this seems like a failure of lookahead! The second thing should look to a
93 # file-like thing.
94 # I think this is a posix issue.
95 # tag: posix-issue
96 echo one 1>&2
97 echo two 1 >&2
98 echo three 1>& 2
99 ## stderr-json: "one\ntwo 1\nthree\n"
100
101 #### Filename redirect with spaces
102 # This time 1 *is* a descriptor, not a word. If you add a space between 1 and
103 # >, it doesn't work.
104 echo two 1> $TMP/file-redir1.txt
105 cat $TMP/file-redir1.txt
106 ## stdout: two
107
108 #### Quoted filename redirect with spaces
109 # POSIX makes node of this
110 echo two \1 > $TMP/file-redir2.txt
111 cat $TMP/file-redir2.txt
112 ## stdout: two 1
113
114 #### Descriptor redirect with filename
115 # bash/mksh treat this like a filename, not a descriptor.
116 # dash aborts.
117 echo one 1>&$TMP/nonexistent-filename__
118 echo "status=$?"
119 ## stdout: status=1
120 ## BUG bash stdout: status=0
121 ## OK dash stdout-json: ""
122 ## OK dash status: 2
123
124 #### redirect for loop
125 for i in $(seq 3)
126 do
127 echo $i
128 done > $TMP/redirect-for-loop.txt
129 cat $TMP/redirect-for-loop.txt
130 ## stdout-json: "1\n2\n3\n"
131
132 #### redirect subshell
133 ( echo foo ) 1>&2
134 ## stderr: foo
135 ## stdout-json: ""
136
137 #### Prefix redirect for loop -- not allowed
138 >$TMP/redirect2.txt for i in $(seq 3)
139 do
140 echo $i
141 done
142 cat $TMP/redirect2.txt
143 ## status: 2
144 ## OK mksh status: 1
145
146 #### Brace group redirect
147 # Suffix works, but prefix does NOT work.
148 # That comes from '| compound_command redirect_list' in the grammar!
149 { echo block-redirect; } > $TMP/br.txt
150 cat $TMP/br.txt | wc -c
151 ## stdout: 15
152
153 #### Redirect echo to stderr, and then redirect all of stdout somewhere.
154 { echo foo 1>&2; echo 012345789; } > $TMP/block-stdout.txt
155 cat $TMP/block-stdout.txt | wc -c
156 ## stderr: foo
157 ## stdout: 10
158
159 #### Redirect in the middle of two assignments
160 FOO=foo >$TMP/out.txt BAR=bar printenv.py FOO BAR
161 tac $TMP/out.txt
162 ## stdout-json: "bar\nfoo\n"
163
164 #### Redirect in the middle of a command
165 f=$TMP/out
166 echo -n 1 2 '3 ' > $f
167 echo -n 4 5 >> $f '6 '
168 echo -n 7 >> $f 8 '9 '
169 echo -n >> $f 1 2 '3 '
170 echo >> $f -n 4 5 '6 '
171 cat $f
172 ## stdout-json: "1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 "
173
174 #### Named file descriptor
175 exec {myfd}> $TMP/named-fd.txt
176 echo named-fd-contents >& $myfd
177 cat $TMP/named-fd.txt
178 ## stdout: named-fd-contents
179 ## status: 0
180 ## N-I dash/mksh stdout-json: ""
181 ## N-I dash/mksh status: 127
182
183 #### Double digit fd (20> file)
184 exec 20> "$TMP/double-digit-fd.txt"
185 echo hello20 >&20
186 cat "$TMP/double-digit-fd.txt"
187 ## stdout: hello20
188 ## BUG dash stdout-json: ""
189 ## BUG dash status: 127
190
191 #### : 9> fdleak (OSH regression)
192 true 9> "$TMP/fd.txt"
193 ( echo world >&9 )
194 cat "$TMP/fd.txt"
195 ## stdout-json: ""
196
197 #### : 4>&4 (OSH regression)
198
199 # Note: This used to be 3>&3 except on the 0.8.5 release it was flaky under
200 # mksh only? Reproducible with test/spec.sh osh-all, but not test/spec.sh
201 # redirect. Very weird.
202
203 : 4>&4
204 echo hello
205 ## stdout: hello
206 ## BUG mksh stdout-json: ""
207 ## BUG mksh status: 1
208
209 #### : 3>&3-
210 : 3>&3-
211 echo hello
212 ## stdout: hello
213 ## N-I dash/mksh stdout-json: ""
214 ## N-I mksh status: 1
215 ## N-I dash status: 2
216
217 #### 3>&- << EOF (OSH regression: fail to restore fds)
218 exec 3> "$TMP/fd.txt"
219 echo hello 3>&- << EOF
220 EOF
221 echo world >&3
222 exec 3>&- # close
223 cat "$TMP/fd.txt"
224 ## STDOUT:
225 hello
226 world
227 ## END
228
229 #### Open file on descriptor 3 and write to it many times
230
231 # different than case below because 3 is the likely first FD of open()
232
233 exec 3> "$TMP/fd3.txt"
234 echo hello >&3
235 echo world >&3
236 exec 3>&- # close
237 cat "$TMP/fd3.txt"
238 ## STDOUT:
239 hello
240 world
241 ## END
242
243 #### Open file on descriptor 4 and write to it many times
244
245 # different than the case above because because 4 isn't the likely first FD
246
247 exec 4> "$TMP/fd4.txt"
248 echo hello >&4
249 echo world >&4
250 exec 4>&- # close
251 cat "$TMP/fd4.txt"
252 ## STDOUT:
253 hello
254 world
255 ## END
256
257 #### Redirect function stdout
258 f() { echo one; echo two; }
259 f > $TMP/redirect-func.txt
260 cat $TMP/redirect-func.txt
261 ## stdout-json: "one\ntwo\n"
262
263 #### Nested function stdout redirect
264 # Shows that a stack is necessary.
265 inner() {
266 echo i1
267 echo i2
268 }
269 outer() {
270 echo o1
271 inner > $TMP/inner.txt
272 echo o2
273 }
274 outer > $TMP/outer.txt
275 cat $TMP/inner.txt
276 echo --
277 cat $TMP/outer.txt
278 ## stdout-json: "i1\ni2\n--\no1\no2\n"
279
280 #### Redirect to empty string
281 f=''
282 echo s > "$f"
283 echo "result=$?"
284 set -o errexit
285 echo s > "$f"
286 echo DONE
287 ## stdout: result=1
288 ## status: 1
289 ## OK dash stdout: result=2
290 ## OK dash status: 2
291
292 #### Redirect to file descriptor that's not open
293 # Notes:
294 # - dash doesn't allow file descriptors greater than 9. (This is a good
295 # thing, because the bash chapter in AOSA book mentions that juggling user
296 # vs. system file descriptors is a huge pain.)
297 # - But somehow running in parallel under spec-runner.sh changes whether
298 # descriptor 3 is open. e.g. 'echo hi 1>&3'. Possibly because of
299 # /usr/bin/time. The _tmp/spec/*.task.txt file gets corrupted!
300 # - Oh this is because I use time --output-file. That opens descriptor 3. And
301 # then time forks the shell script. The file descriptor table is inherited.
302 # - You actually have to set the file descriptor to something. What do
303 # configure and debootstrap too?
304
305 # 3/2020 note: file descriptor 9 failed on Travis, so I changed it to 8. The
306 # process state isn't necessarly clean. TODO: Close the descriptor when OSH
307 # supports it?
308
309 echo hi 1>&8
310 ## status: 1
311 ## OK dash status: 2
312
313 #### Open descriptor with exec
314 # What is the point of this? ./configure scripts and debootstrap use it.
315 exec 3>&1
316 echo hi 1>&3
317 ## stdout: hi
318 ## status: 0
319
320 #### Open multiple descriptors with exec
321 # What is the point of this? ./configure scripts and debootstrap use it.
322 exec 3>&1
323 exec 4>&1
324 echo three 1>&3
325 echo four 1>&4
326 ## stdout-json: "three\nfour\n"
327 ## status: 0
328
329 #### >| to clobber
330 echo XX >| $TMP/c.txt
331
332 set -o noclobber
333
334 echo YY > $TMP/c.txt # not globber
335 echo status=$?
336
337 cat $TMP/c.txt
338 echo ZZ >| $TMP/c.txt
339
340 cat $TMP/c.txt
341 ## STDOUT:
342 status=1
343 XX
344 ZZ
345 ## END
346 ## OK dash STDOUT:
347 status=2
348 XX
349 ZZ
350 ## END
351
352 #### &> redirects stdout and stderr
353 stdout_stderr.py &> $TMP/f.txt
354 # order is indeterminate
355 grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
356 grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
357 ## STDOUT:
358 ok
359 ok
360 ## END
361 ## N-I dash stdout: STDOUT
362 ## N-I dash stderr: STDERR
363 ## N-I dash status: 1
364
365 #### 1>&- to close file descriptor
366 exec 5> "$TMP/f.txt"
367 echo hello >&5
368 exec 5>&-
369 echo world >&5
370 cat "$TMP/f.txt"
371 ## stdout-json: "hello\n"
372
373 #### 1>&2- to move file descriptor
374 exec 5> "$TMP/f.txt"
375 echo hello5 >&5
376 exec 6>&5-
377 echo world5 >&5
378 echo world6 >&6
379 exec 6>&-
380 cat "$TMP/f.txt"
381 ## stdout-json: "hello5\nworld6\n"
382 ## N-I dash status: 2
383 ## N-I dash stdout-json: ""
384 ## N-I mksh status: 1
385 ## N-I mksh stdout-json: ""
386
387 #### 1>&2- (Bash bug: fail to restore closed fd)
388 exec 7> "$TMP/f.txt"
389 : 8>&7 7>&-
390 echo hello >&7
391 : 8>&7-
392 echo world >&7
393 exec 7>&-
394 cat "$TMP/f.txt"
395 ## status: 2
396 ## stdout-json: ""
397 ## OK mksh status: 1
398 ## BUG bash status: 0
399 ## BUG bash stdout: hello
400
401 #### <> for read/write
402 echo first >$TMP/rw.txt
403 exec 8<>$TMP/rw.txt
404 read line <&8
405 echo line=$line
406 echo second 1>&8
407 echo CONTENTS
408 cat $TMP/rw.txt
409 ## stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n"
410
411 #### <> for read/write named pipes
412 rm -f "$TMP/f.pipe"
413 mkfifo "$TMP/f.pipe"
414 exec 8<> "$TMP/f.pipe"
415 echo first >&8
416 echo second >&8
417 read line1 <&8
418 read line2 <&8
419 exec 8<&-
420 echo line1=$line1 line2=$line2
421 ## stdout: line1=first line2=second
422
423 #### &>> appends stdout and stderr
424
425 # Fix for flaky tests: dash behaves non-deterministically under load! It
426 # doesn't implement the behavior anyway so I don't care why.
427 case $SH in
428 *dash)
429 exit 1
430 ;;
431 esac
432
433 echo "ok" > $TMP/f.txt
434 stdout_stderr.py &>> $TMP/f.txt
435 grep ok $TMP/f.txt >/dev/null && echo 'ok'
436 grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
437 grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
438 ## STDOUT:
439 ok
440 ok
441 ok
442 ## END
443 ## N-I dash stdout-json: ""
444 ## N-I dash status: 1
445
446 #### exec redirect then various builtins
447 exec 5>$TMP/log.txt
448 echo hi >&5
449 set -o >&5
450 echo done
451 ## STDOUT:
452 done
453 ## END
454
455 #### >$file touches a file
456 rm -f myfile
457 test -f myfile
458 echo status=$?
459 >myfile
460 test -f myfile
461 echo status=$?
462 ## STDOUT:
463 status=1
464 status=0
465 ## END
466 # regression for OSH
467 ## stderr-json: ""
468
469 #### $(< $file) yields the contents of the file
470
471 echo FOO > myfile
472 foo=$(< myfile)
473 echo $foo
474 ## STDOUT:
475 FOO
476 ## END
477 ## N-I dash/ash/yash stdout-json: "\n"
478
479 #### $(< file) with more statements
480
481 # note that it doesn't do this without a command sub!
482 # It's apparently a special case in bash, mksh, and zsh?
483 foo=$(echo begin; < myfile)
484 echo $foo
485 echo ---
486
487 foo=$(< myfile; echo end)
488 echo $foo
489 echo ---
490
491 foo=$(< myfile; <myfile)
492 echo $foo
493 echo ---
494
495 ## STDOUT:
496 begin
497 ---
498 end
499 ---
500
501 ---
502 ## END
503 # weird, zsh behaves differently
504 ## OK zsh STDOUT:
505 begin
506 FOO
507 ---
508 FOO
509 end
510 ---
511 FOO
512 FOO
513 ---
514 ## END
515
516
517 #### < file in pipeline and subshell doesn't work
518 echo FOO > file2
519
520 # This only happens in command subs, which is weird
521 < file2 | tr A-Z a-z
522 ( < file2 )
523 echo end
524 ## STDOUT:
525 end
526 ## END
527
528 #### 2>&1 with no command
529 ( exit 42 ) # status is reset after this
530 echo status=$?
531 2>&1
532 echo status=$?
533 ## STDOUT:
534 status=42
535 status=0
536 ## END
537 ## stderr-json: ""
538
539 #### 2&>1 (is it a redirect or is it like a&>1)
540 2&>1
541 echo status=$?
542 ## STDOUT:
543 status=127
544 ## END
545 ## OK mksh/dash STDOUT:
546 status=0
547 ## END
548
549 #### can't mention big file descriptor
550 echo hi 9>&1
551 # 23 is the max descriptor fo rmksh
552 #echo hi 24>&1
553 echo hi 99>&1
554 echo hi 100>&1
555 ## OK osh STDOUT:
556 hi
557 hi
558 hi 100
559 ## END
560 ## STDOUT:
561 hi
562 hi 99
563 hi 100
564 ## END
565 ## BUG bash STDOUT:
566 hi
567 hi
568 hi
569 ## END
570
571 #### : >/dev/null 2> / (OSH regression: fail to pop fd frame)
572 # oil 0.8.pre4 fails to restore fds after redirection failure. In the
573 # following case, the fd frame remains after the redirection failure
574 # "2> /" so that the effect of redirection ">/dev/null" remains after
575 # the completion of the command.
576 : >/dev/null 2> /
577 echo hello
578 ## stdout: hello
579 ## OK dash stdout-json: ""
580 ## OK dash status: 2
581 ## OK mksh stdout-json: ""
582 ## OK mksh status: 1
583 # dash/mksh terminates the execution of script on the redirection.
584
585 #### echo foo >&100 (OSH regression: does not fail with invalid fd 100)
586 # oil 0.8.pre4 does not fail with non-existent fd 100.
587 fd=100
588 echo foo >&$fd
589 ## stdout-json: ""
590 ## status: 1
591 ## OK dash status: 2
592
593 #### echo foo >&N where N is first unused fd
594 # 1. prepare default fd for internal uses
595 minfd=10
596 case ${SH##*/} in
597 (mksh) minfd=24 ;;
598 (osh) minfd=100 ;;
599 esac
600
601 # 2. prepare first unused fd
602 fd=$minfd
603 is-fd-open() { : >&$1; }
604 while is-fd-open "$fd"; do
605 : $((fd+=1))
606
607 # prevent infinite loop for broken osh_eval
608 if test $fd -gt 1000; then
609 break
610 fi
611 done
612
613 # 3. test
614 echo foo >&$fd
615 ## stdout-json: ""
616 ## status: 1
617 ## OK dash status: 2
618
619 #### exec {fd}>&- (OSH regression: fails to close fd)
620 # mksh, dash do not implement {fd} redirections.
621 case $SH in (mksh|dash) exit 1 ;; esac
622 # oil 0.8.pre4 fails to close fd by {fd}&-.
623 exec {fd}>file1
624 echo foo >&$fd
625 exec {fd}>&-
626 echo bar >&$fd
627 cat file1
628 ## stdout: foo
629 ## N-I mksh/dash stdout-json: ""
630 ## N-I mksh/dash status: 1