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/exec-here-doc.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 ### Exit builtin
99 exit 3
100 # status: 3
101
102 ### Exit builtin with invalid arg
103 exit invalid
104 # Rationale: runtime errors are 1
105 # status: 1
106 # OK dash/bash status: 2
107
108 ### Exit builtin with too many args
109 exit 7 8 9
110 echo "no exit: $?"
111 # status: 0
112 # stdout-json: "no exit: 1\n"
113 # BUG dash status: 7
114 # BUG dash stdout-json: ""
115 # OK mksh status: 1
116 # OK mksh stdout-json: ""
117
118 ### time block
119 # bash and mksh work; dash does't.
120 # TODO: osh needs to implement BraceGroup redirect properly.
121 err=_tmp/time-$(basename $SH).txt
122 {
123 time {
124 sleep 0.01
125 sleep 0.02
126 }
127 } 2> $err
128 cat $err | grep --only-matching real
129 # Just check that we found 'real'.
130 # This is fiddly:
131 # | sed -n -E -e 's/.*(0m0\.03).*/\1/'
132 #
133 # status: 0
134 # stdout: real
135 # BUG dash status: 2
136 # BUG dash stdout-json: ""
137
138 ### time pipeline
139 time echo hi | wc -c
140 # stdout: 3
141 # status: 0
142
143 ### shift
144 set -- 1 2 3 4
145 shift
146 echo "$@"
147 shift 2
148 echo "$@"
149 # stdout-json: "2 3 4\n4\n"
150 # status: 0
151
152 ### Shifting too far
153 set -- 1
154 shift 2
155 # status: 1
156 # OK dash status: 2
157
158 ### Invalid shift argument
159 shift ZZZ
160 # status: 1
161 # OK dash status: 2
162 # BUG mksh status: 0
163
164 ### Read builtin
165 # NOTE: there are TABS below
166 read x <<EOF
167 A B C D E
168 FG
169 EOF
170 echo "[$x]"
171 # stdout: [A B C D E]
172 # status: 0
173
174 ### Read builtin with no newline.
175 # This is odd because the variable is populated successfully. OSH/Oil might
176 # need a separate put reading feature that doesn't use IFS.
177 echo -n ZZZ | { read x; echo $?; echo $x; }
178 # stdout-json: "1\nZZZ\n"
179 # status: 0
180
181 ### Read builtin with multiple variables
182 # NOTE: there are TABS below
183 read x y z <<EOF
184 A B C D E
185 FG
186 EOF
187 echo "$x/$y/$z"
188 # stdout: A/B/C D E
189 # status: 0
190
191 ### Read builtin with not enough variables
192 set -o errexit
193 set -o nounset # hm this doesn't change it
194 read x y z <<EOF
195 A B
196 EOF
197 echo /$x/$y/$z/
198 # stdout: /A/B//
199 # status: 0
200
201 ### get umask
202 umask | grep '[0-9]\+' # check for digits
203 # status: 0
204
205 ### Read -n (with $REPLY)
206 echo 12345 > $TMP/readn.txt
207 read -n 4 x < $TMP/readn.txt
208 read -n 2 < $TMP/readn.txt # Do it again with no variable
209 argv.py $x $REPLY
210 # stdout: ['1234', '12']
211 # N-I dash stdout: []
212
213 ### read -r ignores backslashes
214 echo 'one\ two' > $TMP/readr.txt
215 read escaped < $TMP/readr.txt
216 read -r raw < $TMP/readr.txt
217 argv "$escaped" "$raw"
218 # stdout: ['one two', 'one\\ two']
219
220 ### get umask
221 umask | grep '[0-9]\+' # check for digits
222 # status: 0
223
224 ### set umask in octal
225 rm $TMP/umask-one $TMP/umask-two
226 umask 0002
227 echo one > $TMP/umask-one
228 umask 0022
229 echo two > $TMP/umask-two
230 stat -c '%a' $TMP/umask-one $TMP/umask-two
231 # status: 0
232 # stdout-json: "664\n644\n"
233 # stderr-json: ""
234
235 ### set umask symbolically
236 rm $TMP/umask-one $TMP/umask-two
237 echo one > $TMP/umask-one
238 umask g-w,o-w
239 echo two > $TMP/umask-two
240 stat -c '%a' $TMP/umask-one $TMP/umask-two
241 # status: 0
242 # stdout-json: "664\n644\n"
243 # stderr-json: ""
244