This reorder function is meant to swap values of a two-element array if
unordered. Bash and ksh produce reversed results. mksh and zsh do as expected.
#!/usr/bin/env bash
[[ -n ${ZSH_VERSION+_} ]] && emulate ksh
function reorder {
(( x[1] < x && (x=x[1], x[1]=$x) ))
echo "${x[@]}"
}
x=(123 456)
reorder
x=(456 123)
reorder
eval echo '${'{BA,K,Z}SH_VERSION\}
# vim: ft=sh :
$ bash ./reorder
456 123
123 456
4.2.37(1)-release
$ ksh ./reorder
123 456
456 123
Version AJM 93u+ 2012-06-28
$ mksh ./reorder
123 456
123 456
@(#)MIRBSD KSH R40 2012/09/01
$ zsh ./reorder
123 456
123 456
5.0.0
The Ksh issue seems to be that an explicit x[0] is needed (it's a slightly
outdated dev build), but I can't figure out why Bash is doing this. No
parameter expansion in the arithmetic does the same:
function reorder2 {
_=$x let '(x[1] < x) && (x=x[1], x[1]=_)'
echo "${x[@]}"
}
Some variations crash:
$ bash -c 'function reorder { (( x[1] < x[0] && (x=x[1], x[1]=$x) )); echo
"${x[@]}"; }; x=(123 456); reorder; x=(456 123); reorder'
Segmentation fault
$ bash -c 'function reorder { (( (x > x[1]) && (x=${x[1]}, x[1]=$x) ));
echo "${x[@]}"; }; x=(123 456); reorder; x=(456 123); reorder'
123 456
Segmentation fault
The second issue is that Bash tries to resolve arithmetic variables when
evaluation should never reach them. Some methods of short-circuiting do work,
e.g. to populate an array:
$ n=0 a=a[n]=n=(n+1)%10,a; ((a)); echo "${a[@]}" # Bash
0 1 2 3 4 5 6 7 8 9
However, In this case, `a' should be protected by `&&'. The same occurs with
`||' and `x?y:z'.
$ zsh -c 'emulate ksh; typeset -a a; n=0 a="(a[n]=n++)<7&&a"; ((a)); echo
"${a[@]:1}"' # max depth == 256
0 1 2 3 4 5 6 7
$ ksh -c 'n=0 a="(a[n]=++n)<7&&a[0]"; ((a[0])); echo "${a[@]:1}"'
# max depth == 8
1 2 3 4 5 6 7
$ bash -c 'n=0 a="(a[n]=++n)<7&&a[0]"; ((a[0])); echo "${a[@]:1}"'
# max depth == 1024
bash: n: expression recursion level exceeded (error token is "n")
$ bash -c 'n=0 a="(a[n]=n++)<7&&a"; ((a)); echo "${a[@]:1}"'
bash: (a[n]=n++)<7&&a: expression recursion level exceeded (error token is
"n++)<7&&a")
0 1 2 3 4 5 6 7
The last one both gets the right answer and throws an error... weird.
It appears the mksh method is to keep track of which variables have been
visited and error if any are referenced twice, rather than counting the
arithmetic evaluator stack depth, so this isn't possible in that shell.
--
Dan Douglas
signature.asc
Description: This is a digitally signed message part.