| 1 | #!/usr/bin/env bash |
| 2 | |
| 3 | # NOTE on bash bug: After setting IFS to array, it never splits anymore? Even |
| 4 | # if you assign IFS again. |
| 5 | |
| 6 | #### IFS is scoped |
| 7 | IFS=b |
| 8 | word=abcd |
| 9 | f() { local IFS=c; argv.py $word; } |
| 10 | f |
| 11 | argv.py $word |
| 12 | ## stdout-json: "['ab', 'd']\n['a', 'cd']\n" |
| 13 | |
| 14 | #### Tilde sub is not split, but var sub is |
| 15 | HOME="foo bar" |
| 16 | argv.py ~ |
| 17 | argv.py $HOME |
| 18 | ## stdout-json: "['foo bar']\n['foo', 'bar']\n" |
| 19 | |
| 20 | #### Word splitting |
| 21 | a="1 2" |
| 22 | b="3 4" |
| 23 | argv.py $a"$b" |
| 24 | ## stdout-json: "['1', '23 4']\n" |
| 25 | |
| 26 | #### Word splitting 2 |
| 27 | a="1 2" |
| 28 | b="3 4" |
| 29 | c="5 6" |
| 30 | d="7 8" |
| 31 | argv.py $a"$b"$c"$d" |
| 32 | ## stdout-json: "['1', '23 45', '67 8']\n" |
| 33 | |
| 34 | # Has tests on differences between $* "$*" $@ "$@" |
| 35 | # http://stackoverflow.com/questions/448407/bash-script-to-receive-and-repass-quoted-parameters |
| 36 | |
| 37 | #### $* |
| 38 | func() { argv.py -$*-; } |
| 39 | func "a 1" "b 2" "c 3" |
| 40 | ## stdout: ['-a', '1', 'b', '2', 'c', '3-'] |
| 41 | |
| 42 | #### "$*" |
| 43 | func() { argv.py "-$*-"; } |
| 44 | func "a 1" "b 2" "c 3" |
| 45 | ## stdout: ['-a 1 b 2 c 3-'] |
| 46 | |
| 47 | #### $@ |
| 48 | # How does this differ from $* ? I don't think it does. |
| 49 | func() { argv.py -$@-; } |
| 50 | func "a 1" "b 2" "c 3" |
| 51 | ## stdout: ['-a', '1', 'b', '2', 'c', '3-'] |
| 52 | |
| 53 | #### "$@" |
| 54 | func() { argv.py "-$@-"; } |
| 55 | func "a 1" "b 2" "c 3" |
| 56 | ## stdout: ['-a 1', 'b 2', 'c 3-'] |
| 57 | |
| 58 | #### empty argv |
| 59 | argv.py 1 "$@" 2 $@ 3 "$*" 4 $* 5 |
| 60 | ## stdout: ['1', '2', '3', '', '4', '5'] |
| 61 | |
| 62 | #### Word elision with space |
| 63 | s1=' ' |
| 64 | argv.py $s1 |
| 65 | ## stdout: [] |
| 66 | |
| 67 | #### Word elision with non-whitespace IFS |
| 68 | # Treated differently than the default IFS. What is the rule here? |
| 69 | IFS='_' |
| 70 | char='_' |
| 71 | space=' ' |
| 72 | empty='' |
| 73 | argv.py $char |
| 74 | argv.py $space |
| 75 | argv.py $empty |
| 76 | ## STDOUT: |
| 77 | [''] |
| 78 | [' '] |
| 79 | [] |
| 80 | ## END |
| 81 | |
| 82 | #### Leading/trailing word elision with non-whitespace IFS |
| 83 | # This behavior is weird. |
| 84 | IFS=_ |
| 85 | s1='_a_b_' |
| 86 | argv.py $s1 |
| 87 | ## stdout: ['', 'a', 'b'] |
| 88 | |
| 89 | #### Leading ' ' vs leading ' _ ' |
| 90 | # This behavior is weird, but all shells agree. |
| 91 | IFS='_ ' |
| 92 | s1='_ a b _ ' |
| 93 | s2=' a b _ ' |
| 94 | argv.py $s1 |
| 95 | argv.py $s2 |
| 96 | ## STDOUT: |
| 97 | ['', 'a', 'b'] |
| 98 | ['a', 'b'] |
| 99 | ## END |
| 100 | |
| 101 | #### Multiple non-whitespace IFS chars. |
| 102 | IFS=_- |
| 103 | s1='a__b---c_d' |
| 104 | argv.py $s1 |
| 105 | ## stdout: ['a', '', 'b', '', '', 'c', 'd'] |
| 106 | |
| 107 | #### IFS with whitespace and non-whitepace. |
| 108 | # NOTE: Three delimiters means two empty words in the middle. No elision. |
| 109 | IFS='_ ' |
| 110 | s1='a_b _ _ _ c _d e' |
| 111 | argv.py $s1 |
| 112 | ## stdout: ['a', 'b', '', '', 'c', 'd', 'e'] |
| 113 | |
| 114 | #### empty $@ and $* is elided |
| 115 | func() { argv.py 1 $@ $* 2; } |
| 116 | func |
| 117 | ## stdout: ['1', '2'] |
| 118 | |
| 119 | #### unquoted empty arg is elided |
| 120 | empty="" |
| 121 | argv.py 1 $empty 2 |
| 122 | ## stdout: ['1', '2'] |
| 123 | |
| 124 | #### unquoted whitespace arg is elided |
| 125 | space=" " |
| 126 | argv.py 1 $space 2 |
| 127 | ## stdout: ['1', '2'] |
| 128 | |
| 129 | #### empty literals are not elided |
| 130 | space=" " |
| 131 | argv.py 1 $space"" 2 |
| 132 | ## stdout: ['1', '', '2'] |
| 133 | |
| 134 | #### no splitting when IFS is empty |
| 135 | IFS="" |
| 136 | foo="a b" |
| 137 | argv.py $foo |
| 138 | ## stdout: ['a b'] |
| 139 | |
| 140 | #### default value can yield multiple words |
| 141 | argv.py 1 ${undefined:-"2 3" "4 5"} 6 |
| 142 | ## stdout: ['1', '2 3', '4 5', '6'] |
| 143 | |
| 144 | #### default value can yield multiple words with part joining |
| 145 | argv.py 1${undefined:-"2 3" "4 5"}6 |
| 146 | ## stdout: ['12 3', '4 56'] |
| 147 | |
| 148 | #### default value with unquoted IFS char |
| 149 | IFS=_ |
| 150 | argv.py 1${undefined:-"2_3"x_x"4_5"}6 |
| 151 | ## stdout: ['12_3x', 'x4_56'] |
| 152 | |
| 153 | #### IFS empty doesn't do splitting |
| 154 | IFS='' |
| 155 | x=$(echo -e ' a b\tc\n') |
| 156 | argv.py $x |
| 157 | ## STDOUT: |
| 158 | [' a b\tc'] |
| 159 | ## END |
| 160 | ## N-I dash STDOUT: |
| 161 | ['-e a b\tc'] |
| 162 | ## END |
| 163 | |
| 164 | |
| 165 | #### IFS unset behaves like $' \t\n' |
| 166 | unset IFS |
| 167 | x=$(echo -e ' a b\tc\n') |
| 168 | argv.py $x |
| 169 | ## STDOUT: |
| 170 | ['a', 'b', 'c'] |
| 171 | ## END |
| 172 | ## N-I dash STDOUT: |
| 173 | ['-e', 'a', 'b', 'c'] |
| 174 | ## END |
| 175 | |
| 176 | #### IFS='\' |
| 177 | # NOTE: OSH fails this because of double backslash escaping issue! |
| 178 | IFS='\' |
| 179 | s='a\b' |
| 180 | argv.py $s |
| 181 | ## STDOUT: |
| 182 | ['a', 'b'] |
| 183 | ## END |
| 184 | |
| 185 | #### IFS='\ ' |
| 186 | # NOTE: OSH fails this because of double backslash escaping issue! |
| 187 | # When IFS is \, then you're no longer using backslash escaping. |
| 188 | IFS='\ ' |
| 189 | s='a\b \\ c d\' |
| 190 | argv.py $s |
| 191 | ## STDOUT: |
| 192 | ['a', 'b', '', 'c', 'd'] |
| 193 | ## END |
| 194 | |
| 195 | #### IFS characters are glob metacharacters |
| 196 | IFS='* ' |
| 197 | s='a*b c' |
| 198 | argv.py $s |
| 199 | |
| 200 | IFS='?' |
| 201 | s='?x?y?z?' |
| 202 | argv.py $s |
| 203 | |
| 204 | IFS='[' |
| 205 | s='[x[y[z[' |
| 206 | argv.py $s |
| 207 | ## STDOUT: |
| 208 | ['a', 'b', 'c'] |
| 209 | ['', 'x', 'y', 'z'] |
| 210 | ['', 'x', 'y', 'z'] |
| 211 | ## END |
| 212 | |
| 213 | #### Trailing space |
| 214 | argv.py 'Xec ho ' |
| 215 | argv.py X'ec ho ' |
| 216 | argv.py X"ec ho " |
| 217 | ## STDOUT: |
| 218 | ['Xec ho '] |
| 219 | ['Xec ho '] |
| 220 | ['Xec ho '] |
| 221 | ## END |
| 222 | |
| 223 | |
| 224 | # TODO: |
| 225 | # - unquoted args of whitespace are not elided (when IFS = null) |
| 226 | # - empty quoted args are kept |
| 227 | # - Test ${@:1} and so forth? |
| 228 | # |
| 229 | # - $* $@ with empty IFS |
| 230 | # - $* $@ with custom IFS |
| 231 | # |
| 232 | # - no splitting when IFS is empty |
| 233 | # - word splitting removes leading and trailing whitespace |
| 234 | |
| 235 | # TODO: test framework needs common setup |
| 236 | |
| 237 | # Test IFS and $@ $* on all these |
| 238 | #### TODO |
| 239 | empty="" |
| 240 | space=" " |
| 241 | AB="A B" |
| 242 | X="X" |
| 243 | Yspaces=" Y " |
| 244 | |
| 245 |