With zsh instead, you could define a function like:
reduce() { local i=1 argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups while ((i < $#)) { if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) { argv[i]=${argv[i]%-*}-${argv[i+1]#*-} argv[i+1]=() } else { ((i++)) } } print ${(j:,:)@} } Which would also accept ranges on input:
$ reduce 1,2,3,5,6,7,8,9,12,14 1-3,5-9,12,14 $ reduce 1,2,3,5-7,8,9-11,12,13-20 1-3,5-20 $ reduce 5,2,4,5,6 2,4-6 Note that it won't work properly if the input has overlapping ranges:
$ reduce 1-3,2 1-3,2 $ reduce 1-3,2-4 1-3,2-4 From bash, you'd define the function as:
reduce() { zsh -c ' i=1 argv=(${(nus:,:)1}) # split $1 on ",", numerically sort and remove dups while ((i < $#)) { if ((${argv[i]#*-} + 1 == ${argv[i+1]%-*})) { argv[i]=${argv[i]%-*}-${argv[i+1]#*-} argv[i+1]=() } else { ((i++)) } } print ${(j:,:)@}' zsh "$@" }