| 1 | #!/bin/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-json: "/home/foo\n/home/bar\n" |
| 153 | |
| 154 | ### Tilde expansion with brace expansion |
| 155 | # The brace expansion happens FIRST. After that, the second token has tilde |
| 156 | # FIRST, so it gets expanded. The first token has an unexpanded tilde, because |
| 157 | # it's not in the leading position. |
| 158 | # NOTE: mksh gives different behavior! So it probably doesn't matter that |
| 159 | # much... |
| 160 | HOME=/home/bob |
| 161 | echo {foo~,~}/bar |
| 162 | # stdout: foo~/bar /home/bob/bar |
| 163 | # OK mksh stdout: foo~/bar ~/bar |
| 164 | |
| 165 | ### Two kinds of tilde expansion |
| 166 | # ~/foo and ~bar |
| 167 | HOME=/home/bob |
| 168 | echo ~{/src,root} |
| 169 | # stdout: /home/bob/src /root |
| 170 | # OK mksh stdout: ~/src ~root |
| 171 | |
| 172 | ### Tilde expansion come before var expansion |
| 173 | HOME=/home/bob |
| 174 | foo=~ |
| 175 | echo $foo |
| 176 | foo='~' |
| 177 | echo $foo |
| 178 | # In the second instance, we expand into a literal ~, and since var expansion |
| 179 | # comes after tilde expansion, it is NOT tried again. |
| 180 | # stdout-json: "/home/bob\n~\n" |
| 181 | |
| 182 | ### Number range expansion |
| 183 | echo -{1..8..3}- |
| 184 | # stdout: -1- -4- -7- |
| 185 | # N-I mksh stdout: -{1..8..3}- |
| 186 | |
| 187 | ### Ascending number range expansion with negative step |
| 188 | echo -{1..8..-3}- |
| 189 | # stdout: -1- -4- -7- |
| 190 | # OK zsh stdout: -7- -4- -1- |
| 191 | # N-I mksh stdout: -{1..8..-3}- |
| 192 | |
| 193 | ### Descending number range expansion |
| 194 | echo -{8..1..3}- |
| 195 | # stdout: -8- -5- -2- |
| 196 | # N-I mksh stdout: -{8..1..3}- |
| 197 | |
| 198 | ### Descending number range expansion with negative step |
| 199 | echo -{8..1..-3}- |
| 200 | # stdout: -8- -5- -2- |
| 201 | # OK zsh stdout: -2- -5- -8- |
| 202 | # N-I mksh stdout: -{8..1..-3}- |
| 203 | |
| 204 | ### Char range expansion |
| 205 | echo -{a..e}- |
| 206 | # stdout: -a- -b- -c- -d- -e- |
| 207 | # N-I mksh/zsh stdout: -{a..e}- |
| 208 | |
| 209 | ### Char range expansion with step |
| 210 | echo -{a..e..2}- -{a..e..-2}- |
| 211 | # stdout: -a- -c- -e- -a- -c- -e- |
| 212 | # N-I mksh/zsh stdout: -{a..e..2}- -{a..e..-2}- |
| 213 | |
| 214 | ### Descending char range expansion |
| 215 | echo -{e..a..2}- -{e..a..-2}- |
| 216 | # stdout: -e- -c- -a- -e- -c- -a- |
| 217 | # N-I mksh/zsh stdout: -{e..a..2}- -{e..a..-2}- |
| 218 | |
| 219 | ### Fixed width number range expansion |
| 220 | echo -{01..03}- |
| 221 | # stdout: -01- -02- -03- |
| 222 | # N-I mksh stdout: -{01..03}- |
| 223 | |
| 224 | ### Inconsistent fixed width number range expansion |
| 225 | # zsh uses the first one, bash uses the max width? |
| 226 | echo -{01..003}- |
| 227 | # stdout: -001- -002- -003- |
| 228 | # OK zsh stdout: -01- -02- -03- |
| 229 | # N-I mksh stdout: -{01..003}- |
| 230 | |
| 231 | ### Inconsistent fixed width number range expansion |
| 232 | # zsh uses the first width, bash uses the max width? |
| 233 | echo -{01..3}- |
| 234 | # stdout: -01- -02- -03- |
| 235 | # N-I mksh stdout: -{01..3}- |
| 236 | |
| 237 | ### Side effect in expansion |
| 238 | # bash is the only one that does it first. I guess since this is |
| 239 | # non-POSIX anyway, follow bash? |
| 240 | i=0 |
| 241 | echo {a,b,c}-$((i++)) |
| 242 | # stdout: a-0 b-1 c-2 |
| 243 | # OK mksh/zsh stdout: a-0 b-0 c-0 |