1 |
#!/bin/bash |
2 |
|
3 |
### echo dashes |
4 |
echo - |
5 |
echo -- |
6 |
echo --- |
7 |
# stdout-json: "-\n--\n---\n" |
8 |
|
9 |
### exec builtin |
10 |
exec echo hi |
11 |
# stdout: hi |
12 |
|
13 |
### exec builtin with redirects |
14 |
exec 1>&2 |
15 |
echo 'to stderr' |
16 |
# stdout-json: "" |
17 |
# stderr: to stderr |
18 |
|
19 |
### exec builtin with here doc |
20 |
# This has in a separate file because both code and data can be read from |
21 |
# stdin. |
22 |
$SH spec/exec-here-doc.sh |
23 |
# stdout-json: "x=one\ny=two\nDONE\n" |
24 |
|
25 |
### cd and $PWD |
26 |
cd / |
27 |
echo $PWD |
28 |
# stdout: / |
29 |
|
30 |
### $OLDPWD |
31 |
cd / |
32 |
cd $TMP |
33 |
echo "old: $OLDPWD" |
34 |
cd - |
35 |
# stdout-json: "old: /\n/\n" |
36 |
|
37 |
### pushd/popd |
38 |
set -o errexit |
39 |
cd / |
40 |
pushd $TMP |
41 |
popd |
42 |
pwd |
43 |
# status: 0 |
44 |
# N-I dash/mksh status: 127 |
45 |
|
46 |
### Source |
47 |
lib=$TMP/spec-test-lib.sh |
48 |
echo 'LIBVAR=libvar' > $lib |
49 |
. $lib # dash doesn't have source |
50 |
echo $LIBVAR |
51 |
# stdout: libvar |
52 |
|
53 |
### Exit builtin |
54 |
exit 3 |
55 |
# status: 3 |
56 |
|
57 |
### Exit builtin with invalid arg |
58 |
exit invalid |
59 |
# Rationale: runtime errors are 1 |
60 |
# status: 1 |
61 |
# OK dash/bash status: 2 |
62 |
|
63 |
### Exit builtin with too many args |
64 |
exit 7 8 9 |
65 |
echo "no exit: $?" |
66 |
# status: 0 |
67 |
# stdout-json: "no exit: 1\n" |
68 |
# BUG dash status: 7 |
69 |
# BUG dash stdout-json: "" |
70 |
# OK mksh status: 1 |
71 |
# OK mksh stdout-json: "" |
72 |
|
73 |
### Export sets a global variable |
74 |
# Even after you do export -n, it still exists. |
75 |
f() { export GLOBAL=X; } |
76 |
f |
77 |
echo $GLOBAL |
78 |
printenv.py GLOBAL |
79 |
# stdout-json: "X\nX\n" |
80 |
|
81 |
### Export sets a global variable that persists after export -n |
82 |
f() { export GLOBAL=X; } |
83 |
f |
84 |
echo $GLOBAL |
85 |
printenv.py GLOBAL |
86 |
export -n GLOBAL |
87 |
echo $GLOBAL |
88 |
printenv.py GLOBAL |
89 |
# stdout-json: "X\nX\nX\nNone\n" |
90 |
# N-I mksh/dash stdout-json: "X\nX\n" |
91 |
# N-I mksh status: 1 |
92 |
# N-I dash status: 2 |
93 |
|
94 |
### Export a global variable and unset it |
95 |
f() { export GLOBAL=X; } |
96 |
f |
97 |
echo $GLOBAL |
98 |
printenv.py GLOBAL |
99 |
unset GLOBAL |
100 |
echo $GLOBAL |
101 |
printenv.py GLOBAL |
102 |
# stdout-json: "X\nX\n\nNone\n" |
103 |
|
104 |
### Export existing global variables |
105 |
G1=g1 |
106 |
G2=g2 |
107 |
export G1 G2 |
108 |
printenv.py G1 G2 |
109 |
# stdout-json: "g1\ng2\n" |
110 |
|
111 |
### Export existing local variable |
112 |
f() { |
113 |
local L1=local1 |
114 |
export L1 |
115 |
printenv.py L1 |
116 |
} |
117 |
f |
118 |
printenv.py L1 |
119 |
# stdout-json: "local1\nNone\n" |
120 |
|
121 |
### Export a local that shadows a global |
122 |
V=global |
123 |
f() { |
124 |
local V=local1 |
125 |
export V |
126 |
printenv.py V |
127 |
} |
128 |
f |
129 |
printenv.py V # exported local out of scope; global isn't exported yet |
130 |
export V |
131 |
printenv.py V # now it's exported |
132 |
# stdout-json: "local1\nNone\nglobal\n" |
133 |
|
134 |
### Export a variable before defining it |
135 |
export U |
136 |
U=u |
137 |
printenv.py U |
138 |
# stdout: u |
139 |
|
140 |
### Exporting a parent func variable (dynamic scope) |
141 |
# The algorithm is to walk up the stack and export that one. |
142 |
inner() { |
143 |
export outer_var |
144 |
echo "inner: $outer_var" |
145 |
printenv.py outer_var |
146 |
} |
147 |
outer() { |
148 |
local outer_var=X |
149 |
echo "before inner" |
150 |
printenv.py outer_var |
151 |
inner |
152 |
echo "after inner" |
153 |
printenv.py outer_var |
154 |
} |
155 |
outer |
156 |
# stdout-json: "before inner\nNone\ninner: X\nX\nafter inner\nX\n" |
157 |
|
158 |
### time block |
159 |
# bash and mksh work; dash does't. |
160 |
# TODO: osh needs to implement BraceGroup redirect properly. |
161 |
err=_tmp/time-$(basename $SH).txt |
162 |
{ |
163 |
time { |
164 |
sleep 0.01 |
165 |
sleep 0.02 |
166 |
} |
167 |
} 2> $err |
168 |
cat $err | grep --only-matching real |
169 |
# Just check that we found 'real'. |
170 |
# This is fiddly: |
171 |
# | sed -n -E -e 's/.*(0m0\.03).*/\1/' |
172 |
# |
173 |
# status: 0 |
174 |
# stdout: real |
175 |
# BUG dash status: 2 |
176 |
# BUG dash stdout-json: "" |
177 |
|
178 |
### time pipeline |
179 |
time echo hi | wc -c |
180 |
# stdout: 3 |
181 |
# status: 0 |
182 |
|
183 |
### shift |
184 |
set -- 1 2 3 4 |
185 |
shift |
186 |
echo "$@" |
187 |
shift 2 |
188 |
echo "$@" |
189 |
# stdout-json: "2 3 4\n4\n" |
190 |
# status: 0 |
191 |
|
192 |
### Shifting too far |
193 |
set -- 1 |
194 |
shift 2 |
195 |
# status: 1 |
196 |
# OK dash status: 2 |
197 |
|
198 |
### Invalid shift argument |
199 |
shift ZZZ |
200 |
# status: 1 |
201 |
# OK dash status: 2 |
202 |
# BUG mksh status: 0 |
203 |
|
204 |
### Read builtin |
205 |
# NOTE: there are TABS below |
206 |
read x <<EOF |
207 |
A B C D E |
208 |
FG |
209 |
EOF |
210 |
echo "[$x]" |
211 |
# stdout: [A B C D E] |
212 |
# status: 0 |
213 |
|
214 |
### Read builtin with no newline. |
215 |
# This is odd because the variable is populated successfully. OSH/Oil might |
216 |
# need a separate put reading feature that doesn't use IFS. |
217 |
echo -n ZZZ | { read x; echo $?; echo $x; } |
218 |
# stdout-json: "1\nZZZ\n" |
219 |
# status: 0 |
220 |
|
221 |
### Read builtin with multiple variables |
222 |
# NOTE: there are TABS below |
223 |
read x y z <<EOF |
224 |
A B C D E |
225 |
FG |
226 |
EOF |
227 |
echo "$x/$y/$z" |
228 |
# stdout: A/B/C D E |
229 |
# status: 0 |
230 |
|
231 |
### Read builtin with not enough variables |
232 |
set -o errexit |
233 |
set -o nounset # hm this doesn't change it |
234 |
read x y z <<EOF |
235 |
A B |
236 |
EOF |
237 |
echo /$x/$y/$z/ |
238 |
# stdout: /A/B// |
239 |
# status: 0 |