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