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:
257 ok
258 ok
259 ## END
260 ## N-I dash stdout: STDOUT
261 ## N-I dash stderr: STDERR
262 ## N-I dash status: 1
263
264 #### 1>&2- to close file descriptor
265 # NOTE: "hi\n" goes to stderr, but it's hard to test this because other shells
266 # put errors on stderr.
267 echo hi 1>&2-
268 ## stdout-json: ""
269 ## N-I dash status: 2
270 ## N-I dash stdout-json: ""
271 ## N-I mksh status: 1
272 ## N-I mksh stdout-json: ""
273
274 #### <> for read/write
275 echo first >$TMP/rw.txt
276 exec 8<>$TMP/rw.txt
277 read line <&8
278 echo line=$line
279 echo second 1>&8
280 echo CONTENTS
281 cat $TMP/rw.txt
282 ## stdout-json: "line=first\nCONTENTS\nfirst\nsecond\n"
283
284 #### &>> appends stdout and stderr
285 echo "ok" > $TMP/f.txt
286 stdout_stderr.py &>> $TMP/f.txt
287 grep ok $TMP/f.txt >/dev/null && echo 'ok'
288 grep STDOUT $TMP/f.txt >/dev/null && echo 'ok'
289 grep STDERR $TMP/f.txt >/dev/null && echo 'ok'
290 ## STDOUT:
291 ok
292 ok
293 ok
294 ## END
295 ## N-I dash STDOUT:
296 ok
297 STDOUT
298
299 ## END
300 ## N-I dash status: 1