【问题标题】:In Bash, how to convert number list into ranges of numbers?在 Bash 中,如何将数字列表转换为数字范围?
【发布时间】:2012-12-04 17:38:17
【问题描述】:

目前我有一个命令的数字排序输出:

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

我想将这些数字列表压缩成单个数字或数字范围的列表

18-19,62,161-163,165

我曾想过尝试在 bash 中对数组进行排序并读取下一个数字以查看它是否为 +1...重击:

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);
    }
}

我在想 bash 中可能有一个单行代码可以做到这一点,但我的谷歌搜索功能不足......

更新: 感谢@Karoly Horvath 的快速回答,我曾经用它来完成我的项目。我肯定会对任何更简单的解决方案感兴趣。

【问题讨论】:

  • “但我在将其转换为 Bash 时遇到了麻烦” - 您对哪些部分有问题?
  • 你想为这个启动一个代码高尔夫吗? :-)
  • @KarolyHorvath 我遇到的问题是将列表放入数组中,遍历数组并输出,只是不太了解语法。
  • 那么不要使用数组。您可以 1) 一个一个地读取它们 2) 不要尝试读取下一个值,而要记住旧的值。 sed "s/,/\n/g"
  • in bash [ 是一个命令,所以后面需要一个空格。此外,算术需要特殊语法:if [ $prev -ne $((n+1)) ]if (( prev != n+1 ))

标签: arrays bash numbers range


【解决方案1】:

是的,shell 会进行变量替换,如果未设置prev,则该行变为:

if [ -ne $n+1] 

这是一个工作版本:

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

【讨论】:

  • 我可以推荐tr 而不是sed 进行单字符翻译吗? tr , '\n'(就此而言,您可以使用参数扩展并跳过子shell。)
  • @kojiro:是的,那会奏效。 (老实说,如果我编写 shell 脚本,我从不担心性能。如果它很重要,我会选择另一种语言。顺便说一句,我永远不会在 shell 脚本中实现它,但是嘿,这就是 OP 要求的:)
  • IFS=,代替sed怎么样?
  • @xiaoyi 是的,或者只是将数字放在一个数组中以开头并使用for
  • 好剧本!对于以行分隔而不是逗号分隔的数字列表(通常在处理表格时),在列表文件的底部保留一个额外的换行符,并将前两个命令替换为:cat numbers.txt | while ....
【解决方案2】:

仅在 bash 中使用函数:

#!/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

输出:

18-19,62,161-163,165

【讨论】:

    猜你喜欢
    • 2017-10-02
    • 2011-09-18
    • 1970-01-01
    • 2012-04-08
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多