$ foo="1,2,3,6,7,8,11,13,14,15,16,17"
在shell中,如何将$foo
中的数字分组为1-3,6-8,11,13-17
答案 0 :(得分:2)
给出以下功能:
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[*]}")
}
......如下所示:
# convert your string into an array
IFS=, read -r -a numbers <<<"$foo"
build_range "${numbers[@]}"
......我们得到了输出:
1-3,6-8,11,13-17
答案 1 :(得分:1)
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
答案 2 :(得分:0)
作为替代方案,您可以使用此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命令中,我们保留了2个变量:
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
,如果是这样的话。