OILS / spec / redirect.test.sh View on Github | oilshell.org

485 lines, 212 significant
1## oils_failures_allowed: 2
2## compare_shells: bash dash mksh
3
4#### >& and <& are the same
5
6echo one 1>&2
7
8echo two 1<&2
9
10## STDERR:
11one
12two
13## END
14
15
16#### <&
17# Is there a simpler test case for this?
18echo foo > $TMP/lessamp.txt
19
20exec 6< $TMP/lessamp.txt
21read line <&6
22
23echo "[$line]"
24## stdout: [foo]
25
26#### 2>&1 with no command
27( exit 42 ) # status is reset after this
28echo status=$?
292>&1
30echo status=$?
31## STDOUT:
32status=42
33status=0
34## END
35## stderr-json: ""
36
37
38#### 2&>1 (is it a redirect or is it like a&>1)
392&>1
40echo status=$?
41## STDOUT:
42status=127
43## END
44## OK mksh/dash STDOUT:
45status=0
46## END
47
48
49#### Nonexistent file
50cat <$TMP/nonexistent.txt
51echo 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
60echo one 1>&2
61echo two 1 >&2
62echo 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.
68echo two 1> $TMP/file-redir1.txt
69cat $TMP/file-redir1.txt
70## stdout: two
71
72#### Quoted filename redirect with spaces
73# POSIX makes node of this
74echo two \1 > $TMP/file-redir2.txt
75cat $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.
81echo one 1>&$TMP/nonexistent-filename__
82echo "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
90cat $TMP/block-stdout.txt | wc -c
91## stderr: foo
92## stdout: 10
93
94#### Named file descriptor
95exec {myfd}> $TMP/named-fd.txt
96echo named-fd-contents >& $myfd
97cat $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)
104exec 20> "$TMP/double-digit-fd.txt"
105echo hello20 >&20
106cat "$TMP/double-digit-fd.txt"
107## stdout: hello20
108## BUG dash stdout-json: ""
109## BUG dash status: 127
110
111#### : 9> fdleak (OSH regression)
112true 9> "$TMP/fd.txt"
113( echo world >&9 )
114cat "$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.
121case $SH in (mksh) exit ;; esac
122
123: 3>&3
124echo hello
125## stdout: hello
126## BUG mksh stdout-json: ""
127## BUG mksh status: 0
128
129#### : 3>&3-
130: 3>&3-
131echo 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)
138exec 3> "$TMP/fd.txt"
139echo hello 3>&- << EOF
140EOF
141echo world >&3
142exec 3>&- # close
143cat "$TMP/fd.txt"
144## STDOUT:
145hello
146world
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
153exec 3> "$TMP/fd3.txt"
154echo hello >&3
155echo world >&3
156exec 3>&- # close
157cat "$TMP/fd3.txt"
158## STDOUT:
159hello
160world
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
167exec 4> "$TMP/fd4.txt"
168echo hello >&4
169echo world >&4
170exec 4>&- # close
171cat "$TMP/fd4.txt"
172## STDOUT:
173hello
174world
175## END
176
177#### Redirect to empty string
178f=''
179echo s > "$f"
180echo "result=$?"
181set -o errexit
182echo s > "$f"
183echo 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
204opened=$(ls /proc/$$/fd)
205if echo "$opened" | egrep '^7$'; then
206 echo "FD 7 shouldn't be open"
207 echo "OPENED:"
208 echo "$opened"
209fi
210
211echo 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.
218exec 3>&1
219echo 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.
225exec 3>&1
226exec 4>&1
227echo three 1>&3
228echo four 1>&4
229## stdout-json: "three\nfour\n"
230## status: 0
231
232#### >| to clobber
233echo XX >| $TMP/c.txt
234
235set -o noclobber
236
237echo YY > $TMP/c.txt # not clobber
238echo status=$?
239
240cat $TMP/c.txt
241echo ZZ >| $TMP/c.txt
242
243cat $TMP/c.txt
244## STDOUT:
245status=1
246XX
247ZZ
248## END
249## OK dash STDOUT:
250status=2
251XX
252ZZ
253## END
254
255#### &> redirects stdout and stderr
256tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
257#echo $tmp
258
259stdout_stderr.py &> $tmp
260
261# order is indeterminate
262grep STDOUT $tmp
263grep STDERR $tmp
264
265## STDOUT:
266STDOUT
267STDERR
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.
276case $SH in (dash|mksh) exit 1 ;; esac
277
278tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
279
280stdout_stderr.py >&$tmp
281
282# order is indeterminate
283grep STDOUT $tmp
284grep STDERR $tmp
285
286## STDOUT:
287STDOUT
288STDERR
289## END
290## N-I dash/mksh status: 1
291## N-I dash/mksh stdout-json: ""
292
293#### 1>&- to close file descriptor
294exec 5> "$TMP/f.txt"
295echo hello >&5
296exec 5>&-
297echo world >&5
298cat "$TMP/f.txt"
299## stdout-json: "hello\n"
300
301#### 1>&2- to move file descriptor
302exec 5> "$TMP/f.txt"
303echo hello5 >&5
304exec 6>&5-
305echo world5 >&5
306echo world6 >&6
307exec 6>&-
308cat "$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.
320exec 6>&- 7>&-
321
322opened=$(ls /proc/$$/fd)
323if echo "$opened" | egrep '^7$'; then
324 echo "FD 7 shouldn't be open"
325 echo "OPENED:"
326 echo "$opened"
327fi
328if echo "$opened" | egrep '^6$'; then
329 echo "FD 6 shouldn't be open"
330 echo "OPENED:"
331 echo "$opened"
332fi
333
334exec 7> "$TMP/f.txt"
335: 6>&7 7>&-
336echo hello >&7
337: 6>&7-
338echo world >&7
339exec 7>&-
340cat "$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
351echo first >$TMP/rw.txt
352exec 8<>$TMP/rw.txt
353read line <&8
354echo line=$line
355echo second 1>&8
356echo CONTENTS
357cat $TMP/rw.txt
358## stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n"
359
360#### <> for read/write named pipes
361rm -f "$TMP/f.pipe"
362mkfifo "$TMP/f.pipe"
363exec 8<> "$TMP/f.pipe"
364echo first >&8
365echo second >&8
366read line1 <&8
367read line2 <&8
368exec 8<&-
369echo 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.
376case $SH in
377 *dash)
378 exit 1
379 ;;
380esac
381
382echo "ok" > $TMP/f.txt
383stdout_stderr.py &>> $TMP/f.txt
384grep ok $TMP/f.txt >/dev/null && echo 'ok'
385grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
386grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
387## STDOUT:
388ok
389ok
390ok
391## END
392## N-I dash stdout-json: ""
393## N-I dash status: 1
394
395#### exec redirect then various builtins
396exec 5>$TMP/log.txt
397echo hi >&5
398set -o >&5
399echo done
400## STDOUT:
401done
402## END
403
404#### can't mention big file descriptor
405echo hi 9>&1
406# trivia: 23 is the max descriptor for mksh
407#echo hi 24>&1
408echo hi 99>&1
409echo hi 100>&1
410## OK osh STDOUT:
411hi
412hi
413hi 100
414## END
415## STDOUT:
416hi
417hi 99
418hi 100
419## END
420## BUG bash STDOUT:
421hi
422hi
423hi
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> /
432echo 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.
442fd=100
443echo 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
450minfd=10
451case ${SH##*/} in
452(mksh) minfd=24 ;;
453(osh) minfd=100 ;;
454esac
455
456# 2. prepare first unused fd
457fd=$minfd
458is-fd-open() { : >&$1; }
459while 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
466done
467
468# 3. test
469echo 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.
476case $SH in (mksh|dash) exit 1 ;; esac
477# oil 0.8.pre4 fails to close fd by {fd}&-.
478exec {fd}>file1
479echo foo >&$fd
480exec {fd}>&-
481echo bar >&$fd
482cat file1
483## stdout: foo
484## N-I mksh/dash stdout-json: ""
485## N-I mksh/dash status: 1