| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | #### no expansion |
| 4 | echo {foo} |
| 5 | ## stdout: {foo} |
| 6 | |
| 7 | #### incomplete trailing expansion |
| 8 | echo {a,b}_{ |
| 9 | ## stdout: a_{ b_{ |
| 10 | ## OK osh stdout: {a,b}_{ |
| 11 | |
| 12 | #### partial leading expansion |
| 13 | echo }_{a,b} |
| 14 | ## stdout: }_a }_b |
| 15 | ## OK osh stdout: }_{a,b} |
| 16 | |
| 17 | #### partial leading expansion 2 |
| 18 | echo {x}_{a,b} |
| 19 | ## stdout: {x}_a {x}_b |
| 20 | ## OK osh stdout: {x}_{a,b} |
| 21 | |
| 22 | #### } in expansion |
| 23 | # hm they treat this the SAME. Leftmost { is matched by first }, and then |
| 24 | # there is another } as the postfix. |
| 25 | echo {a,b}} |
| 26 | ## stdout: a} b} |
| 27 | ## status: 0 |
| 28 | ## OK osh stdout: {a,b}} |
| 29 | ## OK zsh stdout-json: "" |
| 30 | ## OK zsh status: 1 |
| 31 | |
| 32 | #### single expansion |
| 33 | echo {foo,bar} |
| 34 | ## stdout: foo bar |
| 35 | |
| 36 | #### double expansion |
| 37 | echo {a,b}_{c,d} |
| 38 | ## stdout: a_c a_d b_c b_d |
| 39 | |
| 40 | #### triple expansion |
| 41 | echo {0,1}{0,1}{0,1} |
| 42 | ## stdout: 000 001 010 011 100 101 110 111 |
| 43 | |
| 44 | #### double expansion with single and double quotes |
| 45 | echo {'a',b}_{c,"d"} |
| 46 | ## stdout: a_c a_d b_c b_d |
| 47 | |
| 48 | #### expansion with mixed quotes |
| 49 | echo -{\X"b",'cd'}- |
| 50 | ## stdout: -Xb- -cd- |
| 51 | |
| 52 | #### expansion with simple var |
| 53 | a=A |
| 54 | echo -{$a,b}- |
| 55 | ## stdout: -A- -b- |
| 56 | |
| 57 | #### double expansion with simple var -- bash bug |
| 58 | # bash is inconsistent with the above |
| 59 | a=A |
| 60 | echo {$a,b}_{c,d} |
| 61 | ## stdout: A_c A_d b_c b_d |
| 62 | ## BUG bash stdout: b_c b_d |
| 63 | |
| 64 | #### double expansion with braced variable |
| 65 | # This fixes it |
| 66 | a=A |
| 67 | echo {${a},b}_{c,d} |
| 68 | ## stdout: A_c A_d b_c b_d |
| 69 | |
| 70 | #### double expansion with literal and simple var |
| 71 | a=A |
| 72 | echo {_$a,b}_{c,d} |
| 73 | ## stdout: _A_c _A_d b_c b_d |
| 74 | ## BUG bash stdout: _ _ b_c b_d |
| 75 | |
| 76 | #### expansion with command sub |
| 77 | a=A |
| 78 | echo -{$(echo a),b}- |
| 79 | ## stdout: -a- -b- |
| 80 | |
| 81 | #### expansion with arith sub |
| 82 | a=A |
| 83 | echo -{$((1 + 2)),b}- |
| 84 | ## stdout: -3- -b- |
| 85 | |
| 86 | #### double expansion with escaped literals |
| 87 | a=A |
| 88 | echo -{\$,\[,\]}- |
| 89 | ## stdout: -$- -[- -]- |
| 90 | |
| 91 | #### { in expansion |
| 92 | # bash and mksh treat this differently. bash treats the |
| 93 | # first { is a prefix. I think it's harder to read, and \{{a,b} should be |
| 94 | # required. |
| 95 | echo {{a,b} |
| 96 | ## stdout: {{a,b} |
| 97 | ## BUG bash/zsh stdout: {a {b |
| 98 | |
| 99 | #### quoted { in expansion |
| 100 | echo \{{a,b} |
| 101 | ## stdout: {a {b |
| 102 | |
| 103 | #### Empty expansion |
| 104 | echo a{X,,Y}b |
| 105 | ## stdout: aXb ab aYb |
| 106 | |
| 107 | #### Empty alternative |
| 108 | # zsh and mksh don't do word elision, probably because they do brace expansion |
| 109 | # AFTER variable substitution. |
| 110 | argv.py {X,,Y,} |
| 111 | ## stdout: ['X', 'Y'] |
| 112 | ## OK mksh/zsh stdout: ['X', '', 'Y', ''] |
| 113 | ## status: 0 |
| 114 | |
| 115 | #### Empty alternative with empty string suffix |
| 116 | # zsh and mksh don't do word elision, probably because they do brace expansion |
| 117 | # AFTER variable substitution. |
| 118 | argv.py {X,,Y,}'' |
| 119 | ## stdout: ['X', '', 'Y', ''] |
| 120 | ## status: 0 |
| 121 | |
| 122 | #### nested brace expansion |
| 123 | echo -{A,={a,b}=,B}- |
| 124 | ## stdout: -A- -=a=- -=b=- -B- |
| 125 | |
| 126 | #### triple nested brace expansion |
| 127 | echo -{A,={a,.{x,y}.,b}=,B}- |
| 128 | ## stdout: -A- -=a=- -=.x.=- -=.y.=- -=b=- -B- |
| 129 | |
| 130 | #### nested and double brace expansion |
| 131 | echo -{A,={a,b}{c,d}=,B}- |
| 132 | ## stdout: -A- -=ac=- -=ad=- -=bc=- -=bd=- -B- |
| 133 | |
| 134 | #### expansion on RHS of assignment |
| 135 | # I think bash's behavior is more consistent. No splitting either. |
| 136 | v={X,Y} |
| 137 | echo $v |
| 138 | ## stdout: {X,Y} |
| 139 | ## BUG mksh stdout: X Y |
| 140 | |
| 141 | #### no expansion with RHS assignment |
| 142 | {v,x}=X |
| 143 | ## status: 127 |
| 144 | ## stdout-json: "" |
| 145 | ## OK zsh status: 1 |
| 146 | |
| 147 | #### Tilde expansion |
| 148 | HOME=/home/foo |
| 149 | echo ~ |
| 150 | HOME=/home/bar |
| 151 | echo ~ |
| 152 | ## STDOUT: |
| 153 | /home/foo |
| 154 | /home/bar |
| 155 | ## END |
| 156 | |
| 157 | #### Tilde expansion with brace expansion |
| 158 | # NOTE: osh matches mksh. Is that OK? |
| 159 | # The brace expansion happens FIRST. After that, the second token has tilde |
| 160 | # FIRST, so it gets expanded. The first token has an unexpanded tilde, because |
| 161 | # it's not in the leading position. |
| 162 | # NOTE: mksh gives different behavior! So it probably doesn't matter that |
| 163 | # much |
| 164 | HOME=/home/bob |
| 165 | echo {foo~,~}/bar |
| 166 | ## stdout: foo~/bar /home/bob/bar |
| 167 | ## OK osh/mksh stdout: foo~/bar ~/bar |
| 168 | |
| 169 | #### Two kinds of tilde expansion |
| 170 | # NOTE: osh matches mksh. Is that OK? |
| 171 | # ~/foo and ~bar |
| 172 | HOME=/home/bob |
| 173 | echo ~{/src,root} |
| 174 | ## stdout: /home/bob/src /root |
| 175 | ## OK osh/mksh stdout: ~/src ~root |
| 176 | |
| 177 | #### Tilde expansion come before var expansion |
| 178 | HOME=/home/bob |
| 179 | foo=~ |
| 180 | echo $foo |
| 181 | foo='~' |
| 182 | echo $foo |
| 183 | # In the second instance, we expand into a literal ~, and since var expansion |
| 184 | # comes after tilde expansion, it is NOT tried again. |
| 185 | ## STDOUT: |
| 186 | /home/bob |
| 187 | ~ |
| 188 | ## END |
| 189 | |
| 190 | #### Number range expansion |
| 191 | echo -{1..8..3}- |
| 192 | echo -{1..10..3}- |
| 193 | ## STDOUT: |
| 194 | -1- -4- -7- |
| 195 | -1- -4- -7- -10- |
| 196 | ## N-I mksh STDOUT: |
| 197 | -{1..8..3}- |
| 198 | -{1..10..3}- |
| 199 | ## END |
| 200 | |
| 201 | #### Ascending number range expansion with negative step is invalid |
| 202 | echo -{1..8..-3}- |
| 203 | ## stdout-json: "" |
| 204 | ## status: 2 |
| 205 | ## BUG bash stdout: -1- -4- -7- |
| 206 | ## BUG zsh stdout: -7- -4- -1- |
| 207 | ## BUG bash/zsh status: 0 |
| 208 | ## N-I mksh stdout: -{1..8..-3}- |
| 209 | ## N-I mksh status: 0 |
| 210 | |
| 211 | #### Descending number range expansion with positive step is invalid |
| 212 | echo -{8..1..3}- |
| 213 | ## stdout-json: "" |
| 214 | ## status: 2 |
| 215 | ## BUG bash/zsh stdout: -8- -5- -2- |
| 216 | ## BUG bash/zsh status: 0 |
| 217 | ## N-I mksh stdout: -{8..1..3}- |
| 218 | ## N-I mksh status: 0 |
| 219 | |
| 220 | #### Descending number range expansion with negative step |
| 221 | echo -{8..1..-3}- |
| 222 | ## stdout: -8- -5- -2- |
| 223 | # zsh behavior seems clearly wrong! |
| 224 | ## BUG zsh stdout: -2- -5- -8- |
| 225 | ## N-I mksh stdout: -{8..1..-3}- |
| 226 | |
| 227 | #### Singleton ranges |
| 228 | echo {1..1}- |
| 229 | echo {-9..-9}- |
| 230 | echo {-9..-9..3}- |
| 231 | echo {-9..-9..-3}- |
| 232 | echo {a..a}- |
| 233 | ## STDOUT: |
| 234 | 1- |
| 235 | -9- |
| 236 | -9- |
| 237 | -9- |
| 238 | a- |
| 239 | ## END |
| 240 | ## N-I mksh STDOUT: |
| 241 | {1..1}- |
| 242 | {-9..-9}- |
| 243 | {-9..-9..3}- |
| 244 | {-9..-9..-3}- |
| 245 | {a..a}- |
| 246 | ## END |
| 247 | |
| 248 | #### Singleton char ranges with steps |
| 249 | echo {a..a..2}- |
| 250 | echo {a..a..-2}- |
| 251 | ## STDOUT: |
| 252 | a- |
| 253 | a- |
| 254 | ## END |
| 255 | # zsh is considered buggy because it implements {a..a} but not {a..a..1} ! |
| 256 | ## BUG zsh STDOUT: |
| 257 | {a..a..2}- |
| 258 | {a..a..-2}- |
| 259 | ## END |
| 260 | ## N-I mksh STDOUT: |
| 261 | {a..a..2}- |
| 262 | {a..a..-2}- |
| 263 | ## END |
| 264 | |
| 265 | #### Char range expansion |
| 266 | echo -{a..e}- |
| 267 | ## stdout: -a- -b- -c- -d- -e- |
| 268 | ## N-I mksh stdout: -{a..e}- |
| 269 | |
| 270 | #### Char range expansion with step |
| 271 | echo -{a..e..2}- |
| 272 | ## stdout: -a- -c- -e- |
| 273 | ## N-I mksh/zsh stdout: -{a..e..2}- |
| 274 | |
| 275 | #### Char ranges with steps of the wrong sign |
| 276 | echo -{a..e..-2}- |
| 277 | echo -{e..a..2}- |
| 278 | ## stdout-json: "" |
| 279 | ## status: 2 |
| 280 | ## BUG bash STDOUT: |
| 281 | -a- -c- -e- |
| 282 | -e- -c- -a- |
| 283 | ## END |
| 284 | ## BUG bash status: 0 |
| 285 | ## N-I mksh/zsh STDOUT: |
| 286 | -{a..e..-2}- |
| 287 | -{e..a..2}- |
| 288 | ## END |
| 289 | ## BUG mksh/zsh status: 0 |
| 290 | |
| 291 | #### Mixed case char expansion is invalid |
| 292 | case $SH in *zsh) echo BUG; exit ;; esac |
| 293 | echo -{z..A}- |
| 294 | echo -{z..A..2}- |
| 295 | ## stdout-json: "" |
| 296 | ## status: 2 |
| 297 | ## OK mksh STDOUT: |
| 298 | -{z..A}- |
| 299 | -{z..A..2}- |
| 300 | ## END |
| 301 | ## OK mksh status: 0 |
| 302 | ## BUG zsh stdout: BUG |
| 303 | ## BUG zsh status: 0 |
| 304 | # This is exposed a weird bash bug!!! |
| 305 | ## BUG bash stdout-json: "" |
| 306 | ## BUG bash status: 1 |
| 307 | |
| 308 | #### Descending char range expansion |
| 309 | echo -{e..a..-2}- |
| 310 | ## stdout: -e- -c- -a- |
| 311 | ## N-I mksh/zsh stdout: -{e..a..-2}- |
| 312 | |
| 313 | #### Fixed width number range expansion |
| 314 | echo -{01..03}- |
| 315 | echo -{09..12}- # doesn't become -012-, fixed width |
| 316 | echo -{12..07}- |
| 317 | ## STDOUT: |
| 318 | -01- -02- -03- |
| 319 | -09- -10- -11- -12- |
| 320 | -12- -11- -10- -09- -08- -07- |
| 321 | ## END |
| 322 | ## N-I mksh STDOUT: |
| 323 | -{01..03}- |
| 324 | -{09..12}- |
| 325 | -{12..07}- |
| 326 | ## END |
| 327 | |
| 328 | #### Inconsistent fixed width number range expansion |
| 329 | # zsh uses the first one, bash uses the max width? |
| 330 | echo -{01..003}- |
| 331 | ## stdout: -001- -002- -003- |
| 332 | ## OK zsh stdout: -01- -02- -03- |
| 333 | ## N-I mksh stdout: -{01..003}- |
| 334 | |
| 335 | #### Inconsistent fixed width number range expansion |
| 336 | # zsh uses the first width, bash uses the max width? |
| 337 | echo -{01..3}- |
| 338 | ## stdout: -01- -02- -03- |
| 339 | ## N-I mksh stdout: -{01..3}- |
| 340 | |
| 341 | #### Adjacent comma and range works |
| 342 | echo -{a,b}{1..3}- |
| 343 | ## STDOUT: |
| 344 | -a1- -a2- -a3- -b1- -b2- -b3- |
| 345 | ## END |
| 346 | ## N-I mksh STDOUT: |
| 347 | -a{1..3}- -b{1..3}- |
| 348 | ## END |
| 349 | |
| 350 | #### Range inside comma works |
| 351 | echo -{a,_{1..3}_,b}- |
| 352 | ## STDOUT: |
| 353 | -a- -_1_- -_2_- -_3_- -b- |
| 354 | ## END |
| 355 | ## N-I mksh STDOUT: |
| 356 | -a- -_{1..3}_- -b- |
| 357 | ## END |
| 358 | |
| 359 | #### Mixed comma and range doesn't work |
| 360 | echo -{a,b,1..3}- |
| 361 | ## STDOUT: |
| 362 | -a- -b- -1..3- |
| 363 | ## END |
| 364 | |
| 365 | #### comma and invalid range (adjacent and nested) |
| 366 | echo -{a,b}{1...3}- |
| 367 | echo -{a,{1...3}}- |
| 368 | echo {a,b}{} |
| 369 | ## STDOUT: |
| 370 | -a{1...3}- -b{1...3}- |
| 371 | -a- -{1...3}- |
| 372 | a{} b{} |
| 373 | ## END |
| 374 | # osh doesn't expand ANYTHING on invalid syntax. That's OK because of the test |
| 375 | # case below. |
| 376 | ## OK osh STDOUT: |
| 377 | -{a,b}{1...3}- |
| 378 | -{a,{1...3}}- |
| 379 | {a,b}{} |
| 380 | ## END |
| 381 | |
| 382 | #### OSH provides an alternative to invalid syntax |
| 383 | echo -{a,b}\{1...3\}- |
| 384 | echo -{a,\{1...3\}}- |
| 385 | echo {a,b}\{\} |
| 386 | ## STDOUT: |
| 387 | -a{1...3}- -b{1...3}- |
| 388 | -a- -{1...3}- |
| 389 | a{} b{} |
| 390 | ## END |
| 391 | |
| 392 | #### Side effect in expansion |
| 393 | # bash is the only one that does it first. I guess since this is |
| 394 | # non-POSIX anyway, follow bash? |
| 395 | i=0 |
| 396 | echo {a,b,c}-$((i++)) |
| 397 | ## stdout: a-0 b-1 c-2 |
| 398 | ## OK mksh/zsh stdout: a-0 b-0 c-0 |
| 399 | |
| 400 | #### Invalid brace expansions don't expand |
| 401 | echo {1.3} |
| 402 | echo {1...3} |
| 403 | echo {1__3} |
| 404 | ## STDOUT: |
| 405 | {1.3} |
| 406 | {1...3} |
| 407 | {1__3} |
| 408 | ## END |
| 409 | |
| 410 | #### Invalid brace expansions mixing characters and numbers |
| 411 | # zsh does something crazy like : ; < = > that I'm not writing |
| 412 | case $SH in */zsh) echo BUG; exit ;; esac |
| 413 | echo {1..a} |
| 414 | echo {z..3} |
| 415 | ## STDOUT: |
| 416 | {1..a} |
| 417 | {z..3} |
| 418 | ## END |
| 419 | ## BUG zsh STDOUT: |
| 420 | BUG |
| 421 | ## END |