| 1 | 
 | 
| 2 | #### recursive arith: one level
 | 
| 3 | a='b=123'
 | 
| 4 | echo $((a))
 | 
| 5 | ## stdout: 123
 | 
| 6 | ## N-I dash status: 2
 | 
| 7 | ## N-I dash stdout-json: ""
 | 
| 8 | ## N-I yash stdout: b=123
 | 
| 9 | 
 | 
| 10 | #### recursive arith: two levels
 | 
| 11 | a='b=c' c='d=123'
 | 
| 12 | echo $((a))
 | 
| 13 | ## stdout: 123
 | 
| 14 | ## N-I dash status: 2
 | 
| 15 | ## N-I dash stdout-json: ""
 | 
| 16 | ## N-I yash stdout: b=c
 | 
| 17 | 
 | 
| 18 | #### recursive arith: short circuit &&, ||
 | 
| 19 | # Note: mksh R52 has a bug. Even though it supports a short circuit like
 | 
| 20 | #   "echo $((cond&&(a=1)))", it doesn't work with "x=a=1; echo
 | 
| 21 | #   $((cond&&x))". It is fixed at least in mksh R57.
 | 
| 22 | # Note: "busybox sh" doesn't support short circuit.
 | 
| 23 | a=b=123
 | 
| 24 | echo $((1||a)):$((b))
 | 
| 25 | echo $((0||a)):$((b))
 | 
| 26 | c=d=321
 | 
| 27 | echo $((0&&c)):$((d))
 | 
| 28 | echo $((1&&c)):$((d))
 | 
| 29 | ## STDOUT:
 | 
| 30 | 1:0
 | 
| 31 | 1:123
 | 
| 32 | 0:0
 | 
| 33 | 1:321
 | 
| 34 | ## END
 | 
| 35 | 
 | 
| 36 | ## BUG mksh/ash STDOUT:
 | 
| 37 | 1:123
 | 
| 38 | 1:123
 | 
| 39 | 0:321
 | 
| 40 | 1:321
 | 
| 41 | ## END
 | 
| 42 | 
 | 
| 43 | ## N-I dash/yash status: 2
 | 
| 44 | ## N-I dash/yash STDOUT:
 | 
| 45 | 1:0
 | 
| 46 | ## END
 | 
| 47 | 
 | 
| 48 | #### recursive arith: short circuit ?:
 | 
| 49 | # Note: "busybox sh" behaves strangely.
 | 
| 50 | y=a=123 n=a=321
 | 
| 51 | echo $((1?(y):(n))):$((a))
 | 
| 52 | echo $((0?(y):(n))):$((a))
 | 
| 53 | ## STDOUT:
 | 
| 54 | 123:123
 | 
| 55 | 321:321
 | 
| 56 | ## END
 | 
| 57 | ## BUG ash STDOUT:
 | 
| 58 | 123:321
 | 
| 59 | 321:321
 | 
| 60 | ## END
 | 
| 61 | ## N-I dash status: 2
 | 
| 62 | ## N-I dash stdout-json: ""
 | 
| 63 | ## N-I yash STDOUT: 
 | 
| 64 | a=123:0
 | 
| 65 | a=321:0
 | 
| 66 | ## END
 | 
| 67 | 
 | 
| 68 | #### recursive arith: side effects
 | 
| 69 | # In Zsh and Busybox sh, the side effect of inner arithmetic
 | 
| 70 | # evaluations seems to take effect only after the whole evaluation.
 | 
| 71 | a='b=c' c='d=123'
 | 
| 72 | echo $((a,d)):$((d))
 | 
| 73 | ## stdout: 123:123
 | 
| 74 | ## BUG zsh/ash stdout: 0:123
 | 
| 75 | ## N-I dash/yash status: 2
 | 
| 76 | ## N-I dash/yash stdout-json: ""
 | 
| 77 | 
 | 
| 78 | #### recursive arith: recursion
 | 
| 79 | loop='i<=100&&(s+=i,i++,loop)' s=0 i=0
 | 
| 80 | echo $((a=loop,s))
 | 
| 81 | ## stdout: 5050
 | 
| 82 | ## N-I mksh status: 1
 | 
| 83 | ## N-I mksh stdout-json: ""
 | 
| 84 | ## N-I ash/dash/yash status: 2
 | 
| 85 | ## N-I ash/dash/yash stdout-json: ""
 | 
| 86 | 
 | 
| 87 | #### recursive arith: array elements
 | 
| 88 | text[1]='d=123'
 | 
| 89 | text[2]='text[1]'
 | 
| 90 | text[3]='text[2]'
 | 
| 91 | echo $((a=text[3]))
 | 
| 92 | ## stdout: 123
 | 
| 93 | ## N-I ash/dash/yash status: 2
 | 
| 94 | ## N-I ash/dash/yash stdout-json: ""
 | 
| 95 | 
 | 
| 96 | #### dynamic arith varname: assign
 | 
| 97 | vec2_set () {
 | 
| 98 |   local this=$1 x=$2 y=$3
 | 
| 99 |   : $(( ${this}_x = $2 ))
 | 
| 100 |   : $(( ${this}_y = y ))
 | 
| 101 | }
 | 
| 102 | vec2_set a 3 4
 | 
| 103 | vec2_set b 5 12
 | 
| 104 | echo a_x=$a_x a_y=$a_y
 | 
| 105 | echo b_x=$b_x b_y=$b_y
 | 
| 106 | ## STDOUT:
 | 
| 107 | a_x=3 a_y=4
 | 
| 108 | b_x=5 b_y=12
 | 
| 109 | ## END
 | 
| 110 | 
 | 
| 111 | #### dynamic arith varname: read
 | 
| 112 | 
 | 
| 113 | vec2_load() {
 | 
| 114 |   local this=$1
 | 
| 115 |   x=$(( ${this}_x ))
 | 
| 116 |   : $(( y = ${this}_y ))
 | 
| 117 | }
 | 
| 118 | a_x=12 a_y=34
 | 
| 119 | vec2_load a
 | 
| 120 | echo x=$x y=$y
 | 
| 121 | ## STDOUT:
 | 
| 122 | x=12 y=34
 | 
| 123 | ## END
 | 
| 124 | 
 | 
| 125 | #### dynamic arith varname: copy/add
 | 
| 126 | shopt -s eval_unsafe_arith  # for RHS
 | 
| 127 | 
 | 
| 128 | vec2_copy () {
 | 
| 129 |   local this=$1 rhs=$2
 | 
| 130 |   : $(( ${this}_x = $(( ${rhs}_x )) ))
 | 
| 131 |   : $(( ${this}_y = ${rhs}_y ))
 | 
| 132 | }
 | 
| 133 | vec2_add () {
 | 
| 134 |   local this=$1 rhs=$2
 | 
| 135 |   : $(( ${this}_x += $(( ${rhs}_x )) ))
 | 
| 136 |   : $(( ${this}_y += ${rhs}_y ))
 | 
| 137 | }
 | 
| 138 | a_x=3 a_y=4
 | 
| 139 | b_x=4 b_y=20
 | 
| 140 | vec2_copy c a
 | 
| 141 | echo c_x=$c_x c_y=$c_y
 | 
| 142 | vec2_add c b
 | 
| 143 | echo c_x=$c_x c_y=$c_y
 | 
| 144 | ## STDOUT:
 | 
| 145 | c_x=3 c_y=4
 | 
| 146 | c_x=7 c_y=24
 | 
| 147 | ## END
 | 
| 148 | 
 | 
| 149 | #### is-array with ${var@a}
 | 
| 150 | case $SH in (mksh|ash|dash|yash) exit 1 ;; esac
 | 
| 151 | 
 | 
| 152 | function ble/is-array { [[ ${!1@a} == *a* ]]; }
 | 
| 153 | 
 | 
| 154 | ble/is-array undef
 | 
| 155 | echo undef $?
 | 
| 156 | 
 | 
| 157 | string=''
 | 
| 158 | ble/is-array string
 | 
| 159 | echo string $?
 | 
| 160 | 
 | 
| 161 | array=(one two three)
 | 
| 162 | ble/is-array array
 | 
| 163 | echo array $?
 | 
| 164 | ## STDOUT:
 | 
| 165 | undef 1
 | 
| 166 | string 1
 | 
| 167 | array 0
 | 
| 168 | ## END
 | 
| 169 | ## N-I zsh/mksh/ash/dash/yash status: 1
 | 
| 170 | ## N-I zsh/mksh/ash/dash/yash stdout-json: ""
 | 
| 171 | 
 | 
| 172 | 
 | 
| 173 | #### Sparse array with big index
 | 
| 174 | 
 | 
| 175 | # TODO: more BashArray idioms / stress tests ?
 | 
| 176 | 
 | 
| 177 | a=()
 | 
| 178 | 
 | 
| 179 | if false; then
 | 
| 180 |   # This takes too long!  # From Zulip
 | 
| 181 |   i=$(( 0x0100000000000000 ))
 | 
| 182 | else
 | 
| 183 |   # smaller number that's OK
 | 
| 184 |   i=$(( 0x0100000 ))
 | 
| 185 | fi
 | 
| 186 | 
 | 
| 187 | a[i]=1
 | 
| 188 | 
 | 
| 189 | echo len=${#a[@]}
 | 
| 190 | 
 | 
| 191 | ## STDOUT:
 | 
| 192 | len=1
 | 
| 193 | ## END
 | 
| 194 | 
 | 
| 195 | ## N-I ash status: 2
 | 
| 196 | ## N-I ash STDOUT:
 | 
| 197 | ## END
 | 
| 198 | 
 | 
| 199 | ## BUG zsh STDOUT:
 | 
| 200 | len=1048576
 | 
| 201 | ## END
 | 
| 202 | 
 | 
| 203 | 
 | 
| 204 | #### shift unshift reverse
 | 
| 205 | 
 | 
| 206 | case $SH in mksh|ash) exit ;; esac
 | 
| 207 | 
 | 
| 208 | # https://github.com/akinomyoga/ble.sh/blob/79beebd928cf9f6506a687d395fd450d027dc4cd/src/util.sh#L578-L582
 | 
| 209 | 
 | 
| 210 | # @fn ble/array#unshift arr value...
 | 
| 211 | function ble/array#unshift {
 | 
| 212 |   builtin eval -- "$1=(\"\${@:2}\" \"\${$1[@]}\")"
 | 
| 213 | }
 | 
| 214 | # @fn ble/array#shift arr count
 | 
| 215 | function ble/array#shift {
 | 
| 216 |   # Note: Bash 4.3 以下では ${arr[@]:${2:-1}} が offset='${2'
 | 
| 217 |   # length='-1' に解釈されるので、先に算術式展開させる。
 | 
| 218 |   builtin eval -- "$1=(\"\${$1[@]:$((${2:-1}))}\")"
 | 
| 219 | }
 | 
| 220 | # @fn ble/array#reverse arr
 | 
| 221 | function ble/array#reverse {
 | 
| 222 |   builtin eval "
 | 
| 223 |   set -- \"\${$1[@]}\"; $1=()
 | 
| 224 |   local e$1 i$1=\$#
 | 
| 225 |   for e$1; do $1[--i$1]=\"\$e$1\"; done"
 | 
| 226 | }
 | 
| 227 | 
 | 
| 228 | a=( {1..6} )
 | 
| 229 | echo "${a[@]}"
 | 
| 230 | 
 | 
| 231 | ble/array#shift a 1
 | 
| 232 | echo "${a[@]}"
 | 
| 233 | 
 | 
| 234 | ble/array#shift a 2
 | 
| 235 | echo "${a[@]}"
 | 
| 236 | 
 | 
| 237 | echo ---
 | 
| 238 | 
 | 
| 239 | ble/array#unshift a 99
 | 
| 240 | echo "${a[@]}"
 | 
| 241 | 
 | 
| 242 | echo ---
 | 
| 243 | 
 | 
| 244 | # doesn't work in zsh!
 | 
| 245 | ble/array#reverse a
 | 
| 246 | echo "${a[@]}"
 | 
| 247 | 
 | 
| 248 | 
 | 
| 249 | ## STDOUT:
 | 
| 250 | 1 2 3 4 5 6
 | 
| 251 | 2 3 4 5 6
 | 
| 252 | 4 5 6
 | 
| 253 | ---
 | 
| 254 | 99 4 5 6
 | 
| 255 | ---
 | 
| 256 | 6 5 4 99
 | 
| 257 | ## END
 | 
| 258 | 
 | 
| 259 | ## BUG zsh STDOUT:
 | 
| 260 | 1 2 3 4 5 6
 | 
| 261 | 2 3 4 5 6
 | 
| 262 | 4 5 6
 | 
| 263 | ---
 | 
| 264 | 99 4 5 6
 | 
| 265 | ---
 | 
| 266 | 5 4 99
 | 
| 267 | ## END
 | 
| 268 | 
 | 
| 269 | ## N-I mksh/ash STDOUT:
 | 
| 270 | ## END
 | 
| 271 | 
 | 
| 272 | 
 | 
| 273 | #### Performance demo
 | 
| 274 | 
 | 
| 275 | case $SH in bash|zsh|mksh|ash) exit ;; esac
 | 
| 276 | 
 | 
| 277 | #pp line (a)
 | 
| 278 | 
 | 
| 279 | a=( foo {25..27} bar )
 | 
| 280 | 
 | 
| 281 | a[10]='sparse'
 | 
| 282 | 
 | 
| 283 | var sp = _a2sp(a)
 | 
| 284 | echo $[type(sp)]
 | 
| 285 | 
 | 
| 286 | echo len: $[_opsp(sp, 'len')]
 | 
| 287 | 
 | 
| 288 | #echo $[len(sp)]
 | 
| 289 | 
 | 
| 290 | shopt -s ysh:upgrade
 | 
| 291 | 
 | 
| 292 | echo subst: @[_opsp(sp, 'subst')]
 | 
| 293 | echo keys: @[_opsp(sp, 'keys')]
 | 
| 294 | 
 | 
| 295 | echo slice: @[_opsp(sp, 'slice', 2, 5)]
 | 
| 296 | 
 | 
| 297 | call _opsp(sp, 'set', 0, 'set0')
 | 
| 298 | 
 | 
| 299 | echo get0: $[_opsp(sp, 'get', 0)]
 | 
| 300 | echo get1: $[_opsp(sp, 'get', 1)]
 | 
| 301 | echo ---
 | 
| 302 | 
 | 
| 303 | to_append=(x y)
 | 
| 304 | echo append
 | 
| 305 | call _opsp(sp, 'append', to_append)
 | 
| 306 | echo subst: @[_opsp(sp, 'subst')]
 | 
| 307 | echo keys: @[_opsp(sp, 'keys')]
 | 
| 308 | echo ---
 | 
| 309 | 
 | 
| 310 | echo unset
 | 
| 311 | call _opsp(sp, 'unset', 11)
 | 
| 312 | echo subst: @[_opsp(sp, 'subst')]
 | 
| 313 | echo keys: @[_opsp(sp, 'keys')]
 | 
| 314 | 
 | 
| 315 | echo ---
 | 
| 316 | 
 | 
| 317 | # Sparse
 | 
| 318 | var d = {
 | 
| 319 |   '1': 'a',
 | 
| 320 |   '10': 'b',
 | 
| 321 |   '100': 'c',
 | 
| 322 |   '1000': 'd',
 | 
| 323 |   '10000': 'e',
 | 
| 324 |   '100000': 'f',
 | 
| 325 | }
 | 
| 326 | 
 | 
| 327 | var sp2 = _d2sp(d)
 | 
| 328 | 
 | 
| 329 | echo len: $[_opsp(sp2, 'len')]
 | 
| 330 | echo subst: @[_opsp(sp2, 'subst')]
 | 
| 331 | 
 | 
| 332 | 
 | 
| 333 | ## STDOUT:
 | 
| 334 | SparseArray
 | 
| 335 | len: 6
 | 
| 336 | subst: foo 25 26 27 bar sparse
 | 
| 337 | keys: 0 1 2 3 4 10
 | 
| 338 | slice: 26 27 bar
 | 
| 339 | get0: set0
 | 
| 340 | get1: 25
 | 
| 341 | ---
 | 
| 342 | append
 | 
| 343 | subst: set0 25 26 27 bar sparse x y
 | 
| 344 | keys: 0 1 2 3 4 10 11 12
 | 
| 345 | ---
 | 
| 346 | unset
 | 
| 347 | subst: set0 25 26 27 bar sparse y
 | 
| 348 | keys: 0 1 2 3 4 10 12
 | 
| 349 | ---
 | 
| 350 | len: 6
 | 
| 351 | subst: a b c d e f
 | 
| 352 | ## END
 | 
| 353 | 
 | 
| 354 | ## N-I bash/zsh/mksh/ash STDOUT:
 | 
| 355 | ## END
 |