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