首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将shell中的连续数字分组

将shell中的连续数字分组
EN

Stack Overflow用户
提问于 2017-09-19 14:50:42
回答 3查看 345关注 0票数 1
代码语言:javascript
运行
复制
$ foo="1,2,3,6,7,8,11,13,14,15,16,17"

在shell中,如何将$foo中的数字分组为1-3,6-8,11,13-17

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-09-19 17:02:13

作为替代,您可以使用这个awk命令:

代码语言:javascript
运行
复制
cat series.awk
function prnt(delim) {
   printf "%s%s", s, (p > s ? "-" p : "") delim
}
BEGIN {
   RS=","
}
NR==1 {
   s = $1
}
p < $1-1 {
   prnt(RS)
   s = $1
}
{
   p = $1
}
END {
   prnt(ORS)
}

现在将其运行为:

代码语言:javascript
运行
复制
$> foo="1,2,3,6,7,8,11,13,14,15,16,17"
$> awk -f series.awk <<< "$foo"
1-3,6-8,11,13-17

$> foo="1,3,6,7,8,11,13,14,15,16,17"
$> awk -f series.awk <<< "$foo"
1,3,6-8,11,13-17

$> foo="1,3,6,7,8,11,13,14,15,16,17,20"
$> awk -f series.awk <<< "$foo"
1,3,6-8,11,13-17,20

下面是一条用于做同样事情的单行命令:

代码语言:javascript
运行
复制
awk 'function prnt(delim){printf "%s%s", s, (p > s ? "-" p : "") delim}
BEGIN{RS=","} NR==1{s = $1} p < $1-1{prnt(RS); s = $1} {p = $1}END {prnt(ORS)}' <<< "$foo"

在这个awk命令中,我们保留了两个变量:

  1. 用于存储前一行号的p
  2. s,用于存储需要打印的范围的开始。

的工作方式:

  1. NR==1s设置为第一行号时
  2. p小于(current_number -1)或$1-1,这意味着我们有一个中断的顺序,我们需要打印范围。
  3. 我们使用一个函数prnt进行打印,它只接受一个参数,即结束分隔符。当prntp < $1-1 { ...}块调用时,我们传递RS或逗号作为结束分隔符,当从END{...}块调用它时,则传递ORS或换行符作为分隔符。
  4. p < $1-1 { ...}内部,我们将s (开始范围)重置为$1
  5. 处理完每一行后,我们将$1存储在变量p中。
  6. prnt使用printf进行格式化输出。它总是先打印起始号码s。然后,它检查p > s和打印连字符,如果是这样的话,然后打印p
票数 0
EN

Stack Overflow用户

发布于 2017-09-19 15:14:13

鉴于以下职能:

代码语言:javascript
运行
复制
build_range() {
  local range_start= range_end=
  local -a result

  end_range() {
      : range_start="$range_start" range_end="$range_end"
      [[ $range_start ]] || return
      if (( range_end == range_start )); then
        # single number; just add it directly
        result+=( "$range_start" )
      elif (( range_end == (range_start + 1) )); then
        # emit 6,7 instead of 6-7
        result+=( "$range_start" "$range_end" )
      else
        # larger span than 2; emit as start-end
        result+=( "$range_start-$range_end" )
      fi
      range_start= range_end=
  }

  # use the first number to initialize both values
  range_start= range_end=
  result=( )
  for number; do
    : number="$number"
    if ! [[ $range_start ]]; then
      range_start=$number
      range_end=$number
      continue
    elif (( number == (range_end + 1) )); then
      (( range_end += 1 ))
      continue
    else
      end_range
      range_start=$number
      range_end=$number
    fi
  done
  end_range
  (IFS=,; printf '%s\n' "${result[*]}")
}

...called如下:

代码语言:javascript
运行
复制
# convert your string into an array
IFS=, read -r -a numbers <<<"$foo"

build_range "${numbers[@]}"

...we获得输出:

代码语言:javascript
运行
复制
1-3,6-8,11,13-17
票数 2
EN

Stack Overflow用户

发布于 2017-09-19 18:42:16

扩展示例的awk解决方案:

代码语言:javascript
运行
复制
foo="1,2,3,6,7,8,11,13,14,15,16,17,19,20,33,34,35"

awk -F',' '{
                r = nxt = 0; 
                for (i=1; i<=NF; i++) 
                    if ($i+1 == $(i+1)){ if (!r) r = $i"-"; nxt = $(i+1) } 
                    else { printf "%s%s", (r)? r nxt : $i, (i == NF)? ORS : FS; r = 0 }
           }' <<<"$foo"

产出:

代码语言:javascript
运行
复制
1-3,6-8,11,13-17,19-20,33-35
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46303582

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档