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