In Bash, how to convert number list into ranges of numbers?

对着背影说爱祢 提交于 2019-12-19 02:42:10

问题


Currently I have a sorted output of numbers from a command:

18,19,62,161,162,163,165

I would like to condense these number lists into a list of single numbers or ranges of numbers

18-19,62,161-163,165

I thought about trying to sort through the array in bash and read the next number to see if it is +1... I have a PHP function that does essentially the same thing, but I'm having trouble transposing it to Bash:

foreach ($missing as $key => $tag) {
    $next = $missing[$key+1];
    if (!isset($first)) {
        $first = $tag;
    }
    if($next != $tag + 1) {
        if($first == $tag) {
            echo '<tr><td>'.$tag.'</td></tr>';
        } else {
            echo '<tr><td>'.$first.'-'.$tag.'</td></tr>';
        }
        unset($first);
    }
}

I'm thinking there's probably a one-liner in bash that could do this but my Googling is coming up short....

UPDATE: Thank you @Karoly Horvath for a quick answer which I used to finish my project. I'd sure be interested in any simpler solutions out there.


回答1:


Yes, shell does variable substitution, if prev is not set, that line becomes:

if [ -ne $n+1] 

Here is a working version:

numbers="18,19,62,161,162,163,165"

echo $numbers, | sed "s/,/\n/g" | while read num; do
    if [[ -z $first ]]; then
        first=$num; last=$num; continue;
    fi
    if [[ num -ne $((last + 1)) ]]; then
        if [[ first -eq last ]]; then echo $first; else echo $first-$last; fi
        first=$num; last=$num
    else
        : $((last++))
    fi
done | paste -sd ","

18-19,62,161-163,165



回答2:


With a function:

#!/bin/bash

list2range() {
  set -- ${@//,/ }       # convert string to parameters

  local first a b string IFS
  local -a array
  local endofrange=0

  while [[ $# -ge 1 ]]; do  
    a=$1; shift; b=$1

    if [[ $a+1 -eq $b ]]; then
      if [[ $endofrange -eq 0 ]]; then
        first=$a
        endofrange=1
      fi
    else
      if [[ $endofrange -eq 1 ]]; then
        array+=($first-$a)
      else
        array+=($a)
      fi
      endofrange=0
    fi
  done

  IFS=","; echo "${array[*]}"
}

list2range 18,19,62,161,162,163,165

Output:

18-19,62,161-163,165


来源:https://stackoverflow.com/questions/13708705/in-bash-how-to-convert-number-list-into-ranges-of-numbers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!