$ foo="1,2,3,6,7,8,11,13,14,15,16,17"
In shell, how to group the numbers in $foo as 1-3,6-8,11,13-17
Advertisement
Answer
As an alternative, you can use this awk command:
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)
}
Now run it as:
$> 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
Here is an one-liner for doing the same:
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"
In this awk command we keep 2 variables:
pfor storing previous line’s numbersfor storing start of the range that need to be printed
How it works:
- When
NR==1we setsto first line’s number - When
pis less than (current_number -1) or$1-1that indicates we have a break in sequence and we need to print the range. - We use a function
prntfor doing the printing that accepts only one argument that is end delimiter. Whenprntis called fromp < $1-1 { ...}block then we passRSor comma as end delimiter and when it gets called fromEND{...}block then we passORSor newline as delimiter. - Inside
p < $1-1 { ...}we resets(start range) to$1 - After processing each line we store
$1in variablep. prntusesprintffor formatted output. It always prints starting numbersfirst. Then it checks ifp > sand prints hyphen followed bypif that is the case.