1 #!/usr/bin/env bash
2
3 ### echo dashes
4 echo -
5 echo --
6 echo ---
7 # stdout-json: "-\n--\n---\n"
8
9 ### echo -en
10 echo -en 'abc\ndef\n'
11 # stdout-json: "abc\ndef\n"
12 # N-I dash stdout-json: "-en abc\ndef\n\n"
13
14 ### echo -ez (invalid flag)
15 # bash differs from the other two shells, but its behavior is possibly more
16 # sensible, if you're going to ignore the error. It doesn't make sense for the
17 # 'e' to mean 2 different things simultaneously: flag and literal to be
18 # printed.
19 echo -ez 'abc\n'
20 # stdout-json: "-ez abc\\n\n"
21 # BUG dash/mksh stdout-json: "-ez abc\n\n"
22
23 ### exec builtin
24 exec echo hi
25 # stdout: hi
26
27 ### exec builtin with redirects
28 exec 1>&2
29 echo 'to stderr'
30 # stdout-json: ""
31 # stderr: to stderr
32
33 ### exec builtin with here doc
34 # This has in a separate file because both code and data can be read from
35 # stdin.
36 $SH spec/builtins-exec-here-doc-helper.sh
37 # stdout-json: "x=one\ny=two\nDONE\n"
38
39 ### cd and $PWD
40 cd /
41 echo $PWD
42 # stdout: /
43
44 ### $OLDPWD
45 cd /
46 cd $TMP
47 echo "old: $OLDPWD"
48 cd -
49 # stdout-json: "old: /\n/\n"
50
51 ### cd with no arguments
52 HOME=$TMP/home
53 mkdir -p $HOME
54 cd
55 test $(pwd) = "$HOME" && echo OK
56 # stdout: OK
57
58 ### cd to nonexistent dir
59 cd /nonexistent/dir
60 echo status=$?
61 # stdout: status=1
62 # OK dash/mksh stdout: status=2
63
64 ### pushd/popd
65 set -o errexit
66 cd /
67 pushd $TMP
68 popd
69 pwd
70 # status: 0
71 # N-I dash/mksh status: 127
72
73 ### Eval
74 eval "a=3"
75 echo $a
76 # stdout: 3
77
78 ### Source
79 lib=$TMP/spec-test-lib.sh
80 echo 'LIBVAR=libvar' > $lib
81 . $lib # dash doesn't have source
82 echo $LIBVAR
83 # stdout: libvar
84
85 ### Source nonexistent
86 source /nonexistent/path
87 echo status=$?
88 # stdout: status=1
89 # OK dash stdout: status=127
90
91 ### Source with no arguments
92 source
93 echo status=$?
94 # stdout: status=1
95 # OK bash stdout: status=2
96 # OK dash stdout: status=127
97
98 ### Source script that returns
99 echo one
100 . spec/return-helper.sh
101 echo $?
102 echo two
103 # stdout-json: "one\nreturn-helper.sh\n42\ntwo\n"
104
105 ### Exit builtin
106 exit 3
107 # status: 3
108
109 ### Exit builtin with invalid arg
110 exit invalid
111 # Rationale: runtime errors are 1
112 # status: 1
113 # OK dash/bash status: 2
114
115 ### Exit builtin with too many args
116 exit 7 8 9
117 echo "no exit: $?"
118 # status: 0
119 # stdout-json: "no exit: 1\n"
120 # BUG dash status: 7
121 # BUG dash stdout-json: ""
122 # OK mksh status: 1
123 # OK mksh stdout-json: ""
124
125 ### time block
126 # bash and mksh work; dash does't.
127 # TODO: osh needs to implement BraceGroup redirect properly.
128 err=_tmp/time-$(basename $SH).txt
129 {
130 time {
131 sleep 0.01
132 sleep 0.02
133 }
134 } 2> $err
135 cat $err | grep --only-matching real
136 # Just check that we found 'real'.
137 # This is fiddly:
138 # | sed -n -E -e 's/.*(0m0\.03).*/\1/'
139 #
140 # status: 0
141 # stdout: real
142 # BUG dash status: 2
143 # BUG dash stdout-json: ""
144
145 ### time pipeline
146 time echo hi | wc -c
147 # stdout: 3
148 # status: 0
149
150 ### shift
151 set -- 1 2 3 4
152 shift
153 echo "$@"
154 shift 2
155 echo "$@"
156 # stdout-json: "2 3 4\n4\n"
157 # status: 0
158
159 ### Shifting too far
160 set -- 1
161 shift 2
162 # status: 1
163 # OK dash status: 2
164
165 ### Invalid shift argument
166 shift ZZZ
167 # status: 1
168 # OK dash status: 2
169 # BUG mksh status: 0
170
171 ### Read builtin
172 # NOTE: there are TABS below
173 read x <<EOF
174 A B C D E
175 FG
176 EOF
177 echo "[$x]"
178 # stdout: [A B C D E]
179 # status: 0
180
181 ### Read builtin with no newline.
182 # This is odd because the variable is populated successfully. OSH/Oil might
183 # need a separate put reading feature that doesn't use IFS.
184 echo -n ZZZ | { read x; echo $?; echo $x; }
185 # stdout-json: "1\nZZZ\n"
186 # status: 0
187
188 ### Read builtin with multiple variables
189 # NOTE: there are TABS below
190 read x y z <<EOF
191 A B C D E
192 FG
193 EOF
194 echo "$x/$y/$z"
195 # stdout: A/B/C D E
196 # status: 0
197
198 ### Read builtin with not enough variables
199 set -o errexit
200 set -o nounset # hm this doesn't change it
201 read x y z <<EOF
202 A B
203 EOF
204 echo /$x/$y/$z/
205 # stdout: /A/B//
206 # status: 0
207
208 ### get umask
209 umask | grep '[0-9]\+' # check for digits
210 # status: 0
211
212 ### Read -n (with $REPLY)
213 echo 12345 > $TMP/readn.txt
214 read -n 4 x < $TMP/readn.txt
215 read -n 2 < $TMP/readn.txt # Do it again with no variable
216 argv.py $x $REPLY
217 # stdout: ['1234', '12']
218 # N-I dash stdout: []
219
220 ### read -r ignores backslashes
221 echo 'one\ two' > $TMP/readr.txt
222 read escaped < $TMP/readr.txt
223 read -r raw < $TMP/readr.txt
224 argv "$escaped" "$raw"
225 # stdout: ['one two', 'one\\ two']
226
227 ### get umask
228 umask | grep '[0-9]\+' # check for digits
229 # status: 0
230
231 ### set umask in octal
232 rm $TMP/umask-one $TMP/umask-two
233 umask 0002
234 echo one > $TMP/umask-one
235 umask 0022
236 echo two > $TMP/umask-two
237 stat -c '%a' $TMP/umask-one $TMP/umask-two
238 # status: 0
239 # stdout-json: "664\n644\n"
240 # stderr-json: ""
241
242 ### set umask symbolically
243 rm $TMP/umask-one $TMP/umask-two
244 echo one > $TMP/umask-one
245 umask g-w,o-w
246 echo two > $TMP/umask-two
247 stat -c '%a' $TMP/umask-one $TMP/umask-two
248 # status: 0
249 # stdout-json: "664\n644\n"
250 # stderr-json: ""
251