1 #!/usr/bin/env bash
2
3 ### >&
4 echo hi 1>&2
5 # stderr: hi
6
7 ### <&
8 # Is there a simpler test case for this?
9 echo foo > $TMP/lessamp.txt
10 exec 5< $TMP/lessamp.txt
11 read line <&5
12 echo "[$line]"
13 # stdout: [foo]
14
15 ### Leading redirect
16 echo hello >$TMP/hello.txt # temporary fix
17 <$TMP/hello.txt cat
18 # stdout: hello
19
20 ### Nonexistent file
21 cat <$TMP/nonexistent.txt
22 echo status=$?
23 # stdout: status=1
24 # OK dash stdout: status=2
25
26 ### Redirect in command sub
27 FOO=$(echo foo 1>&2)
28 echo $FOO
29 # stdout:
30 # stderr: foo
31
32 ### Redirect in assignment is invalid
33 # Hm this is valid in bash and dash. It's parsed as an assigment with a
34 # redirect, which doesn't make sense. But it's a mistake, and should be a W2
35 # warning for us.
36 FOO=bar 2>/dev/null
37 # status: 2
38 # OK bash/dash/mksh status: 0
39
40 ### Redirect in assignment
41 # dash captures stderr to a file here, which seems correct. Bash doesn't and
42 # just lets it go to actual stderr.
43 # For now we agree with dash/mksh, since it involves fewer special cases in the
44 # code.
45 FOO=$(echo foo 1>&2) 2>$TMP/no-command.txt
46 echo FILE=
47 cat $TMP/no-command.txt
48 echo "FOO=$FOO"
49 # status: 2
50 # OK dash/mksh stdout-json: "FILE=\nfoo\nFOO=\n"
51 # OK dash/mksh status: 0
52 # BUG bash stdout-json: "FILE=\nFOO=\n"
53 # OK bash status: 0
54
55 ### Redirect in function body.
56 func() { echo hi; } 1>&2
57 func
58 # stdout-json: ""
59 # stderr-json: "hi\n"
60
61 ### Redirect in function body is evaluated multiple times
62 i=0
63 func() { echo "file $i"; } 1> "$TMP/file$((i++))"
64 func
65 func
66 echo i=$i
67 echo __
68 cat $TMP/file0
69 echo __
70 cat $TMP/file1
71 # stdout-json: "i=2\n__\nfile 1\n__\nfile 2\n"
72 # N-I dash stdout-json: ""
73 # N-I dash status: 2
74
75 ### Redirect in function body AND function call
76 func() { echo hi; } 1>&2
77 func 2>&1
78 # stdout-json: "hi\n"
79 # stderr-json: ""
80
81 ### Descriptor redirect with spaces
82 # Hm this seems like a failure of lookahead! The second thing should look to a
83 # file-like thing.
84 # I think this is a posix issue.
85 # tag: posix-issue
86 echo one 1>&2
87 echo two 1 >&2
88 echo three 1>& 2
89 # stderr-json: "one\ntwo 1\nthree\n"
90
91 ### Filename redirect with spaces
92 # This time 1 *is* a descriptor, not a word. If you add a space between 1 and
93 # >, it doesn't work.
94 echo two 1> $TMP/file-redir1.txt
95 cat $TMP/file-redir1.txt
96 # stdout: two
97
98 ### Quoted filename redirect with spaces
99 # POSIX makes node of this
100 echo two \1 > $TMP/file-redir2.txt
101 cat $TMP/file-redir2.txt
102 # stdout: two 1
103
104 ### Descriptor redirect with filename
105 # bash/mksh treat this like a filename, not a descriptor.
106 # dash aborts.
107 echo one 1>&$TMP/nonexistent-filename__
108 echo "status=$?"
109 # stdout: status=1
110 # BUG bash stdout: status=0
111 # OK dash stdout-json: ""
112 # OK dash status: 2
113
114 ### redirect for loop
115 for i in $(seq 3)
116 do
117 echo $i
118 done > $TMP/redirect-for-loop.txt
119 cat $TMP/redirect-for-loop.txt
120 # stdout-json: "1\n2\n3\n"
121
122 ### redirect subshell
123 ( echo foo ) 1>&2
124 # stderr: foo
125 # stdout-json: ""
126
127 ### Prefix redirect for loop -- not allowed
128 >$TMP/redirect2.txt for i in $(seq 3)
129 do
130 echo $i
131 done
132 cat $TMP/redirect2.txt
133 # status: 2
134 # OK mksh status: 1
135
136 ### Brace group redirect
137 # Suffix works, but prefix does NOT work.
138 # That comes from '| compound_command redirect_list' in the grammar!
139 { echo block-redirect; } > $TMP/br.txt
140 cat $TMP/br.txt | wc -c
141 # stdout: 15
142
143 ### Redirect echo to stderr, and then redirect all of stdout somewhere.
144 { echo foo 1>&2; echo 012345789; } > $TMP/block-stdout.txt
145 cat $TMP/block-stdout.txt | wc -c
146 # stderr: foo
147 # stdout: 10
148
149 ### Redirect in the middle of two assignments
150 FOO=foo >$TMP/out.txt BAR=bar printenv.py FOO BAR
151 tac $TMP/out.txt
152 # stdout-json: "bar\nfoo\n"
153
154 ### Redirect in the middle of a command
155 f=$TMP/out
156 echo -n 1 2 '3 ' > $f
157 echo -n 4 5 >> $f '6 '
158 echo -n 7 >> $f 8 '9 '
159 echo -n >> $f 1 2 '3 '
160 echo >> $f -n 4 5 '6 '
161 cat $f
162 # stdout-json: "1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 "
163
164 ### Named file descriptor
165 exec {myfd}> $TMP/named-fd.txt
166 echo named-fd-contents >& $myfd
167 cat $TMP/named-fd.txt
168 # stdout: named-fd-contents
169 # status: 0
170 # N-I dash/mksh stdout-json: ""
171 # N-I dash/mksh status: 127
172
173 ### Redirect function stdout
174 f() { echo one; echo two; }
175 f > $TMP/redirect-func.txt
176 cat $TMP/redirect-func.txt
177 # stdout-json: "one\ntwo\n"
178
179 ### Nested function stdout redirect
180 # Shows that a stack is necessary.
181 inner() {
182 echo i1
183 echo i2
184 }
185 outer() {
186 echo o1
187 inner > $TMP/inner.txt
188 echo o2
189 }
190 outer > $TMP/outer.txt
191 cat $TMP/inner.txt
192 echo --
193 cat $TMP/outer.txt
194 # stdout-json: "i1\ni2\n--\no1\no2\n"
195
196 ### Redirect to empty string
197 f=''
198 echo s > "$f"
199 echo "result=$?"
200 set -o errexit
201 echo s > "$f"
202 echo DONE
203 # stdout: result=1
204 # status: 1
205 # OK dash stdout: result=2
206 # OK dash status: 2
207
208 ### Redirect to file descriptor that's not open
209 # BUGS:
210 # - dash doesn't allow file descriptors greater than 9. (This is a good thing,
211 # because the bash chapter in AOSA book mentions that juggling user vs. system
212 # file descriptors is a huge pain.)
213 # - But somehow running in parallel under spec-runner.sh changes whether descriptor
214 # 3 is open. e.g. 'echo hi 1>&3'. Possibly because of /usr/bin/time. The
215 # _tmp/spec/*.task.txt file gets corrupted!
216 # - Oh this is because I use time --output-file. That opens descriptor 3. And
217 # then time forks the shell script. The file descriptor table is inherited.
218 # - You actually have to set the file descriptor to something. What do
219 # configure and debootstrap too?
220 echo hi 1>&9
221 # status: 1
222 # OK dash status: 2
223
224 ### Open descriptor with exec
225 # What is the point of this? ./configure scripts and debootstrap use it.
226 exec 3>&1
227 echo hi 1>&3
228 # stdout: hi
229 # status: 0
230
231 ### Open multiple descriptors with exec
232 # What is the point of this? ./configure scripts and debootstrap use it.
233 exec 3>&1
234 exec 4>&1
235 echo three 1>&3
236 echo four 1>&4
237 # stdout-json: "three\nfour\n"
238 # status: 0
239
240 ### >| to clobber
241 echo XX >| $TMP/c.txt
242 set -o noclobber
243 echo YY > $TMP/c.txt # not globber
244 echo status=$?
245 cat $TMP/c.txt
246 echo ZZ >| $TMP/c.txt
247 cat $TMP/c.txt
248 # stdout-json: "status=1\nXX\nZZ\n"
249 # OK dash stdout-json: "status=2\nXX\nZZ\n"
250
251 ### &> redirects stdout and stderr
252 stdout_stderr.py &> $TMP/f.txt
253 # order is indeterminate
254 grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
255 grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
256 # stdout-json: "ok\nok\n"
257 # N-I dash stdout: STDOUT
258 # N-I dash stderr: STDERR
259 # N-I dash status: 1
260
261 ### 1>&2- to close file descriptor
262 # NOTE: "hi\n" goes to stderr, but it's hard to test this because other shells
263 # put errors on stderr.
264 echo hi 1>&2-
265 # stdout-json: ""
266 # N-I dash status: 2
267 # N-I dash stdout-json: ""
268 # N-I mksh status: 1
269 # N-I mksh stdout-json: ""
270
271 ### <> for read/write
272 echo first >$TMP/rw.txt
273 exec 8<>$TMP/rw.txt
274 read line <&8
275 echo line=$line
276 echo second 1>&8
277 echo CONTENTS
278 cat $TMP/rw.txt
279 # stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n"