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 |
#### : 3>&3 (OSH regression) |
198 |
: 3>&3 |
199 |
echo hello |
200 |
## stdout: hello |
201 |
## BUG mksh stdout-json: "" |
202 |
## BUG mksh status: 1 |
203 |
|
204 |
#### : 3>&3- |
205 |
: 3>&3- |
206 |
echo hello |
207 |
## stdout: hello |
208 |
## N-I dash/mksh stdout-json: "" |
209 |
## N-I mksh status: 1 |
210 |
## N-I dash status: 2 |
211 |
|
212 |
#### 3>&- << EOF (OSH regression: fail to restore fds) |
213 |
exec 3> "$TMP/fd.txt" |
214 |
echo hello 3>&- << EOF |
215 |
EOF |
216 |
echo world >&3 |
217 |
exec 3>&- # close |
218 |
cat "$TMP/fd.txt" |
219 |
## STDOUT: |
220 |
hello |
221 |
world |
222 |
## END |
223 |
|
224 |
#### Open file on descriptor 3 and write to it many times |
225 |
|
226 |
# different than case below because 3 is the likely first FD of open() |
227 |
|
228 |
exec 3> "$TMP/fd3.txt" |
229 |
echo hello >&3 |
230 |
echo world >&3 |
231 |
exec 3>&- # close |
232 |
cat "$TMP/fd3.txt" |
233 |
## STDOUT: |
234 |
hello |
235 |
world |
236 |
## END |
237 |
|
238 |
#### Open file on descriptor 4 and write to it many times |
239 |
|
240 |
# different than the case above because because 4 isn't the likely first FD |
241 |
|
242 |
exec 4> "$TMP/fd4.txt" |
243 |
echo hello >&4 |
244 |
echo world >&4 |
245 |
exec 4>&- # close |
246 |
cat "$TMP/fd4.txt" |
247 |
## STDOUT: |
248 |
hello |
249 |
world |
250 |
## END |
251 |
|
252 |
#### Redirect function stdout |
253 |
f() { echo one; echo two; } |
254 |
f > $TMP/redirect-func.txt |
255 |
cat $TMP/redirect-func.txt |
256 |
## stdout-json: "one\ntwo\n" |
257 |
|
258 |
#### Nested function stdout redirect |
259 |
# Shows that a stack is necessary. |
260 |
inner() { |
261 |
echo i1 |
262 |
echo i2 |
263 |
} |
264 |
outer() { |
265 |
echo o1 |
266 |
inner > $TMP/inner.txt |
267 |
echo o2 |
268 |
} |
269 |
outer > $TMP/outer.txt |
270 |
cat $TMP/inner.txt |
271 |
echo -- |
272 |
cat $TMP/outer.txt |
273 |
## stdout-json: "i1\ni2\n--\no1\no2\n" |
274 |
|
275 |
#### Redirect to empty string |
276 |
f='' |
277 |
echo s > "$f" |
278 |
echo "result=$?" |
279 |
set -o errexit |
280 |
echo s > "$f" |
281 |
echo DONE |
282 |
## stdout: result=1 |
283 |
## status: 1 |
284 |
## OK dash stdout: result=2 |
285 |
## OK dash status: 2 |
286 |
|
287 |
#### Redirect to file descriptor that's not open |
288 |
# Notes: |
289 |
# - dash doesn't allow file descriptors greater than 9. (This is a good |
290 |
# thing, because the bash chapter in AOSA book mentions that juggling user |
291 |
# vs. system file descriptors is a huge pain.) |
292 |
# - But somehow running in parallel under spec-runner.sh changes whether |
293 |
# descriptor 3 is open. e.g. 'echo hi 1>&3'. Possibly because of |
294 |
# /usr/bin/time. The _tmp/spec/*.task.txt file gets corrupted! |
295 |
# - Oh this is because I use time --output-file. That opens descriptor 3. And |
296 |
# then time forks the shell script. The file descriptor table is inherited. |
297 |
# - You actually have to set the file descriptor to something. What do |
298 |
# configure and debootstrap too? |
299 |
|
300 |
# 3/2020 note: file descriptor 9 failed on Travis, so I changed it to 8. The |
301 |
# process state isn't necessarly clean. TODO: Close the descriptor when OSH |
302 |
# supports it? |
303 |
|
304 |
echo hi 1>&8 |
305 |
## status: 1 |
306 |
## OK dash status: 2 |
307 |
|
308 |
#### Open descriptor with exec |
309 |
# What is the point of this? ./configure scripts and debootstrap use it. |
310 |
exec 3>&1 |
311 |
echo hi 1>&3 |
312 |
## stdout: hi |
313 |
## status: 0 |
314 |
|
315 |
#### Open multiple descriptors with exec |
316 |
# What is the point of this? ./configure scripts and debootstrap use it. |
317 |
exec 3>&1 |
318 |
exec 4>&1 |
319 |
echo three 1>&3 |
320 |
echo four 1>&4 |
321 |
## stdout-json: "three\nfour\n" |
322 |
## status: 0 |
323 |
|
324 |
#### >| to clobber |
325 |
echo XX >| $TMP/c.txt |
326 |
|
327 |
set -o noclobber |
328 |
|
329 |
echo YY > $TMP/c.txt # not globber |
330 |
echo status=$? |
331 |
|
332 |
cat $TMP/c.txt |
333 |
echo ZZ >| $TMP/c.txt |
334 |
|
335 |
cat $TMP/c.txt |
336 |
## STDOUT: |
337 |
status=1 |
338 |
XX |
339 |
ZZ |
340 |
## END |
341 |
## OK dash STDOUT: |
342 |
status=2 |
343 |
XX |
344 |
ZZ |
345 |
## END |
346 |
|
347 |
#### &> redirects stdout and stderr |
348 |
stdout_stderr.py &> $TMP/f.txt |
349 |
# order is indeterminate |
350 |
grep STDOUT $TMP/f.txt >/dev/null && echo 'ok' |
351 |
grep STDERR $TMP/f.txt >/dev/null && echo 'ok' |
352 |
## STDOUT: |
353 |
ok |
354 |
ok |
355 |
## END |
356 |
## N-I dash stdout: STDOUT |
357 |
## N-I dash stderr: STDERR |
358 |
## N-I dash status: 1 |
359 |
|
360 |
#### 1>&- to close file descriptor |
361 |
exec 5> "$TMP/f.txt" |
362 |
echo hello >&5 |
363 |
exec 5>&- |
364 |
echo world >&5 |
365 |
cat "$TMP/f.txt" |
366 |
## stdout-json: "hello\n" |
367 |
|
368 |
#### 1>&2- to move file descriptor |
369 |
exec 5> "$TMP/f.txt" |
370 |
echo hello5 >&5 |
371 |
exec 6>&5- |
372 |
echo world5 >&5 |
373 |
echo world6 >&6 |
374 |
exec 6>&- |
375 |
cat "$TMP/f.txt" |
376 |
## stdout-json: "hello5\nworld6\n" |
377 |
## N-I dash status: 2 |
378 |
## N-I dash stdout-json: "" |
379 |
## N-I mksh status: 1 |
380 |
## N-I mksh stdout-json: "" |
381 |
|
382 |
#### 1>&2- (Bash bug: fail to restore closed fd) |
383 |
exec 7> "$TMP/f.txt" |
384 |
: 8>&7 7>&- |
385 |
echo hello >&7 |
386 |
: 8>&7- |
387 |
echo world >&7 |
388 |
exec 7>&- |
389 |
cat "$TMP/f.txt" |
390 |
## status: 2 |
391 |
## stdout-json: "" |
392 |
## OK mksh status: 1 |
393 |
## BUG bash status: 0 |
394 |
## BUG bash stdout: hello |
395 |
|
396 |
#### <> for read/write |
397 |
echo first >$TMP/rw.txt |
398 |
exec 8<>$TMP/rw.txt |
399 |
read line <&8 |
400 |
echo line=$line |
401 |
echo second 1>&8 |
402 |
echo CONTENTS |
403 |
cat $TMP/rw.txt |
404 |
## stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n" |
405 |
|
406 |
#### <> for read/write named pipes |
407 |
rm -f "$TMP/f.pipe" |
408 |
mkfifo "$TMP/f.pipe" |
409 |
exec 8<> "$TMP/f.pipe" |
410 |
echo first >&8 |
411 |
echo second >&8 |
412 |
read line1 <&8 |
413 |
read line2 <&8 |
414 |
exec 8<&- |
415 |
echo line1=$line1 line2=$line2 |
416 |
## stdout: line1=first line2=second |
417 |
|
418 |
#### &>> appends stdout and stderr |
419 |
|
420 |
# Fix for flaky tests: dash behaves non-deterministically under load! It |
421 |
# doesn't implement the behavior anyway so I don't care why. |
422 |
case $SH in |
423 |
*dash) |
424 |
exit 1 |
425 |
;; |
426 |
esac |
427 |
|
428 |
echo "ok" > $TMP/f.txt |
429 |
stdout_stderr.py &>> $TMP/f.txt |
430 |
grep ok $TMP/f.txt >/dev/null && echo 'ok' |
431 |
grep STDOUT $TMP/f.txt >/dev/null && echo 'ok' |
432 |
grep STDERR $TMP/f.txt >/dev/null && echo 'ok' |
433 |
## STDOUT: |
434 |
ok |
435 |
ok |
436 |
ok |
437 |
## END |
438 |
## N-I dash stdout-json: "" |
439 |
## N-I dash status: 1 |
440 |
|
441 |
#### exec redirect then various builtins |
442 |
exec 5>$TMP/log.txt |
443 |
echo hi >&5 |
444 |
set -o >&5 |
445 |
echo done |
446 |
## STDOUT: |
447 |
done |
448 |
## END |
449 |
|
450 |
#### >$file touches a file |
451 |
rm -f myfile |
452 |
test -f myfile |
453 |
echo status=$? |
454 |
>myfile |
455 |
test -f myfile |
456 |
echo status=$? |
457 |
## STDOUT: |
458 |
status=1 |
459 |
status=0 |
460 |
## END |
461 |
# regression for OSH |
462 |
## stderr-json: "" |
463 |
|
464 |
#### $(< $file) yields the contents of the file |
465 |
|
466 |
echo FOO > myfile |
467 |
foo=$(< myfile) |
468 |
echo $foo |
469 |
## STDOUT: |
470 |
FOO |
471 |
## END |
472 |
## N-I dash/ash/yash stdout-json: "\n" |
473 |
|
474 |
#### $(< file) with more statements |
475 |
|
476 |
# note that it doesn't do this without a command sub! |
477 |
# It's apparently a special case in bash, mksh, and zsh? |
478 |
foo=$(echo begin; < myfile) |
479 |
echo $foo |
480 |
echo --- |
481 |
|
482 |
foo=$(< myfile; echo end) |
483 |
echo $foo |
484 |
echo --- |
485 |
|
486 |
foo=$(< myfile; <myfile) |
487 |
echo $foo |
488 |
echo --- |
489 |
|
490 |
## STDOUT: |
491 |
begin |
492 |
--- |
493 |
end |
494 |
--- |
495 |
|
496 |
--- |
497 |
## END |
498 |
# weird, zsh behaves differently |
499 |
## OK zsh STDOUT: |
500 |
begin |
501 |
FOO |
502 |
--- |
503 |
FOO |
504 |
end |
505 |
--- |
506 |
FOO |
507 |
FOO |
508 |
--- |
509 |
## END |
510 |
|
511 |
|
512 |
#### < file in pipeline and subshell doesn't work |
513 |
echo FOO > file2 |
514 |
|
515 |
# This only happens in command subs, which is weird |
516 |
< file2 | tr A-Z a-z |
517 |
( < file2 ) |
518 |
echo end |
519 |
## STDOUT: |
520 |
end |
521 |
## END |
522 |
|
523 |
#### 2>&1 with no command |
524 |
( exit 42 ) # status is reset after this |
525 |
echo status=$? |
526 |
2>&1 |
527 |
echo status=$? |
528 |
## STDOUT: |
529 |
status=42 |
530 |
status=0 |
531 |
## END |
532 |
## stderr-json: "" |
533 |
|
534 |
#### 2&>1 (is it a redirect or is it like a&>1) |
535 |
2&>1 |
536 |
echo status=$? |
537 |
## STDOUT: |
538 |
status=127 |
539 |
## END |
540 |
## OK mksh/dash STDOUT: |
541 |
status=0 |
542 |
## END |
543 |
|
544 |
#### can't mention big file descriptor |
545 |
echo hi 9>&1 |
546 |
# 23 is the max descriptor fo rmksh |
547 |
#echo hi 24>&1 |
548 |
echo hi 99>&1 |
549 |
echo hi 100>&1 |
550 |
## OK osh STDOUT: |
551 |
hi |
552 |
hi |
553 |
hi 100 |
554 |
## END |
555 |
## STDOUT: |
556 |
hi |
557 |
hi 99 |
558 |
hi 100 |
559 |
## END |
560 |
## BUG bash STDOUT: |
561 |
hi |
562 |
hi |
563 |
hi |
564 |
## END |
565 |
|
566 |
#### : >/dev/null 2> / (OSH regression: fail to pop fd frame) |
567 |
# oil 0.8.pre4 fails to restore fds after redirection failure. In the |
568 |
# following case, the fd frame remains after the redirection failure |
569 |
# "2> /" so that the effect of redirection ">/dev/null" remains after |
570 |
# the completion of the command. |
571 |
: >/dev/null 2> / |
572 |
echo hello |
573 |
## stdout: hello |
574 |
## OK dash stdout-json: "" |
575 |
## OK dash status: 2 |
576 |
## OK mksh stdout-json: "" |
577 |
## OK mksh status: 1 |
578 |
# dash/mksh terminates the execution of script on the redirection. |
579 |
|
580 |
#### echo foo >&100 (OSH regression: does not fail with invalid fd 100) |
581 |
# oil 0.8.pre4 does not fail with non-existent fd 100. |
582 |
fd=100 |
583 |
echo foo >&$fd |
584 |
## stdout-json: "" |
585 |
## status: 1 |
586 |
## OK dash status: 2 |
587 |
|
588 |
#### echo foo >&N where N is first unused fd |
589 |
# 1. prepare default fd for internal uses |
590 |
minfd=10 |
591 |
case ${SH##*/} in |
592 |
(mksh) minfd=24 ;; |
593 |
(osh) minfd=100 ;; |
594 |
esac |
595 |
|
596 |
# 2. prepare first unused fd |
597 |
fd=$minfd |
598 |
is-fd-open() { : >&$1; } |
599 |
while is-fd-open "$fd"; do |
600 |
: $((fd+=1)) |
601 |
|
602 |
# prevent infinite loop for broken osh_eval |
603 |
if test $fd -gt 1000; then |
604 |
break |
605 |
fi |
606 |
done |
607 |
|
608 |
# 3. test |
609 |
echo foo >&$fd |
610 |
## stdout-json: "" |
611 |
## status: 1 |
612 |
## OK dash status: 2 |
613 |
|
614 |
#### exec {fd}>&- (OSH regression: fails to close fd) |
615 |
# mksh, dash do not implement {fd} redirections. |
616 |
case $SH in (mksh|dash) exit 1 ;; esac |
617 |
# oil 0.8.pre4 fails to close fd by {fd}&-. |
618 |
exec {fd}>file1 |
619 |
echo foo >&$fd |
620 |
exec {fd}>&- |
621 |
echo bar >&$fd |
622 |
cat file1 |
623 |
## stdout: foo |
624 |
## N-I mksh/dash stdout-json: "" |
625 |
## N-I mksh/dash status: 1 |