OILS / spec / redirect.test.sh View on Github | oils.pub

502 lines, 221 significant
1## oils_failures_allowed: 1
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 foo51 > $TMP/lessamp.txt
19
20exec 6< $TMP/lessamp.txt
21read line <&6
22
23echo "[$line]"
24## stdout: [foo51]
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:
64one
65two 1
66three
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.
72echo two 1> $TMP/file-redir1.txt
73cat $TMP/file-redir1.txt
74## stdout: two
75
76#### Quoted filename redirect with spaces
77# POSIX makes node of this
78echo two \1 > $TMP/file-redir2.txt
79cat $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.
85echo one 1>&$TMP/nonexistent-filename__
86echo "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 foo52 1>&2; echo 012345789; } > $TMP/block-stdout.txt
94cat $TMP/block-stdout.txt | wc -c
95## stderr: foo52
96## stdout: 10
97
98#### Named file descriptor
99exec {myfd}> $TMP/named-fd.txt
100echo named-fd-contents >& $myfd
101cat $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)
108exec 20> "$TMP/double-digit-fd.txt"
109echo hello20 >&20
110cat "$TMP/double-digit-fd.txt"
111## stdout: hello20
112## BUG dash stdout-json: ""
113## BUG dash status: 127
114
115#### : 9> fdleak (OSH regression)
116true 9> "$TMP/fd.txt"
117( echo world >&9 )
118cat "$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.
125case $SH in (mksh) exit ;; esac
126
127: 3>&3
128echo hello
129## stdout: hello
130## BUG mksh stdout-json: ""
131## BUG mksh status: 0
132
133#### : 3>&3-
134: 3>&3-
135echo 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)
142exec 3> "$TMP/fd.txt"
143echo hello 3>&- << EOF
144EOF
145echo world >&3
146exec 3>&- # close
147cat "$TMP/fd.txt"
148## STDOUT:
149hello
150world
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
157exec 3> "$TMP/fd3.txt"
158echo hello >&3
159echo world >&3
160exec 3>&- # close
161cat "$TMP/fd3.txt"
162## STDOUT:
163hello
164world
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
171exec 4> "$TMP/fd4.txt"
172echo hello >&4
173echo world >&4
174exec 4>&- # close
175cat "$TMP/fd4.txt"
176## STDOUT:
177hello
178world
179## END
180
181#### Redirect to empty string
182f=''
183echo s > "$f"
184echo "result=$?"
185set -o errexit
186echo s > "$f"
187echo 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
208opened=$(ls /proc/$$/fd)
209if echo "$opened" | egrep '^7$'; then
210 echo "FD 7 shouldn't be open"
211 echo "OPENED:"
212 echo "$opened"
213fi
214
215echo 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.
222exec 3>&1
223echo 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.
229exec 3>&1
230exec 4>&1
231echo three 1>&3
232echo four 1>&4
233## STDOUT:
234three
235four
236## END
237## status: 0
238
239#### >| to clobber
240echo XX >| $TMP/c.txt
241
242set -o noclobber
243
244echo YY > $TMP/c.txt # not clobber
245echo status=$?
246
247cat $TMP/c.txt
248echo ZZ >| $TMP/c.txt
249
250cat $TMP/c.txt
251## STDOUT:
252status=1
253XX
254ZZ
255## END
256## OK dash STDOUT:
257status=2
258XX
259ZZ
260## END
261
262#### &> redirects stdout and stderr
263tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
264#echo $tmp
265
266stdout_stderr.py &> $tmp
267
268# order is indeterminate
269grep STDOUT $tmp
270grep STDERR $tmp
271
272## STDOUT:
273STDOUT
274STDERR
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.
283case $SH in (dash|mksh) exit 1 ;; esac
284
285tmp="$(basename $SH)-$$.txt" # unique name for shell and test case
286
287stdout_stderr.py >&$tmp
288
289# order is indeterminate
290grep STDOUT $tmp
291grep STDERR $tmp
292
293## STDOUT:
294STDOUT
295STDERR
296## END
297## N-I dash/mksh status: 1
298## N-I dash/mksh stdout-json: ""
299
300#### 1>&- to close file descriptor
301exec 5> "$TMP/f.txt"
302echo hello >&5
303exec 5>&-
304echo world >&5
305cat "$TMP/f.txt"
306## STDOUT:
307hello
308## END
309
310#### 1>&2- to move file descriptor
311exec 5> "$TMP/f.txt"
312echo hello5 >&5
313exec 6>&5-
314echo world5 >&5
315echo world6 >&6
316exec 6>&-
317cat "$TMP/f.txt"
318## STDOUT:
319hello5
320world6
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.
332exec 6>&- 7>&-
333
334opened=$(ls /proc/$$/fd)
335if echo "$opened" | egrep '^7$'; then
336 echo "FD 7 shouldn't be open"
337 echo "OPENED:"
338 echo "$opened"
339fi
340if echo "$opened" | egrep '^6$'; then
341 echo "FD 6 shouldn't be open"
342 echo "OPENED:"
343 echo "$opened"
344fi
345
346exec 7> "$TMP/f.txt"
347: 6>&7 7>&-
348echo hello >&7
349: 6>&7-
350echo world >&7
351exec 7>&-
352cat "$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
363echo first >$TMP/rw.txt
364exec 8<>$TMP/rw.txt
365read line <&8
366echo line=$line
367echo second 1>&8
368echo CONTENTS
369cat $TMP/rw.txt
370## STDOUT:
371line=first
372CONTENTS
373first
374second
375## END
376
377#### <> for read/write named pipes
378rm -f "$TMP/f.pipe"
379mkfifo "$TMP/f.pipe"
380exec 8<> "$TMP/f.pipe"
381echo first >&8
382echo second >&8
383read line1 <&8
384read line2 <&8
385exec 8<&-
386echo 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.
393case $SH in
394 *dash)
395 exit 1
396 ;;
397esac
398
399echo "ok" > $TMP/f.txt
400stdout_stderr.py &>> $TMP/f.txt
401grep ok $TMP/f.txt >/dev/null && echo 'ok'
402grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
403grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
404## STDOUT:
405ok
406ok
407ok
408## END
409## N-I dash stdout-json: ""
410## N-I dash status: 1
411
412#### exec redirect then various builtins
413exec 5>$TMP/log.txt
414echo hi >&5
415set -o >&5
416echo done
417## STDOUT:
418done
419## END
420
421#### can't mention big file descriptor
422echo hi 9>&1
423# trivia: 23 is the max descriptor for mksh
424#echo hi 24>&1
425echo hi 99>&1
426echo hi 100>&1
427## OK osh STDOUT:
428hi
429hi
430hi 100
431## END
432## STDOUT:
433hi
434hi 99
435hi 100
436## END
437## BUG bash STDOUT:
438hi
439hi
440hi
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> /
449echo 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.
459fd=100
460echo foo53 >&$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
467minfd=10
468case ${SH##*/} in
469(mksh) minfd=24 ;;
470(osh) minfd=100 ;;
471esac
472
473# 2. prepare first unused fd
474fd=$minfd
475is_fd_open() { : >&$1; }
476while is_fd_open "$fd"; do
477 : $((fd+=1))
478
479 # OLD: prevent infinite loop for broken oils-for-unix
480 #if test $fd -gt 1000; then
481 # break
482 #fi
483done
484
485# 3. test
486echo foo54 >&$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.
493case $SH in (mksh|dash) exit 1 ;; esac
494# oil 0.8.pre4 fails to close fd by {fd}&-.
495exec {fd}>file1
496echo foo55 >&$fd
497exec {fd}>&-
498echo bar >&$fd
499cat file1
500## stdout: foo55
501## N-I mksh/dash stdout-json: ""
502## N-I mksh/dash status: 1