$ foo="1,2,3,6,7,8,11,13,14,15,16,17"
在shell中,如何将$foo
中的数字分组为1-3,6-8,11,13-17
发布于 2017-09-19 17:02:13
作为替代,您可以使用这个awk命令:
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)
}
现在将其运行为:
$> 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
下面是一条用于做同样事情的单行命令:
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命令中,我们保留了两个变量:
p
s
,用于存储需要打印的范围的开始。的工作方式:
NR==1
将s
设置为第一行号时p
小于(current_number -1)或$1-1
,这意味着我们有一个中断的顺序,我们需要打印范围。prnt
进行打印,它只接受一个参数,即结束分隔符。当prnt
从p < $1-1 { ...}
块调用时,我们传递RS
或逗号作为结束分隔符,当从END{...}
块调用它时,则传递ORS
或换行符作为分隔符。p < $1-1 { ...}
内部,我们将s
(开始范围)重置为$1
$1
存储在变量p
中。prnt
使用printf
进行格式化输出。它总是先打印起始号码s
。然后,它检查p > s
和打印连字符,如果是这样的话,然后打印p
。发布于 2017-09-19 15:14:13
鉴于以下职能:
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如下:
# convert your string into an array
IFS=, read -r -a numbers <<<"$foo"
build_range "${numbers[@]}"
...we获得输出:
1-3,6-8,11,13-17
发布于 2017-09-19 18:42:16
扩展示例的awk解决方案:
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"
产出:
1-3,6-8,11,13-17,19-20,33-35
https://stackoverflow.com/questions/46303582
复制相似问题