Text manipulation with sed
You could do it with sed, yes, but other tools are simpler. For example:
$ awk '{
printf "%s ", $2;
for(i=3;i<=NF;i++){
printf "%s:%s:1 ",$1,$(i)
}
print ""
}' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Explanation
awk will split each line of input on whitespace (by default), saving each fields as $1
, $2
, $N
. So:
printf "%s ", $2;
will print the 2nd field and a trailing space.for(i=3;i<=NF;i++){ printf "%s:%s:1 ",$1,$(i) }
: will iterate over fields 3 to the last field (NF
is the number of fields) and for each of them it will print the 1st field, a:
, then the current field and a:1
.print ""
: this just prints a final newline.
Or Perl:
$ perl -ane 'print "$F[1] "; print "$F[0]:$_:1 " for @F[2..$#F]; print "\n"' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
Explanation
The -a
makes perl
behave like awk
and split its input on whitespace. Here, the fields are stored in the array @F
, meaning that the 1st field will be $F[0]
, the 2nd $F[1]
etc. So:
print "$F[1] "
: print the 2nd field.print "$F[0]:$_:1 " for @F[2..$#F];
: iterate over fields 3 to the last field ($#F
is the number of elements in the array@F
, so@F[2..$#F]
takes an array slice starting at the 3rd element until the end of the array) and print the 1st field, a:
, then the current field and a:1
.print "\n"
: this just prints a final newline.
Here is a horrible sed
way!
$ sed -r 's/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/; :a s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /; t a; s/ $//' file
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1
1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1
More readably:
sed -r '
s/^([0-9]+) ([0-9]+) ([0-9]+)/\2 \1:\3:1/
:a
s/([0-9]+)(:[0-9]+:1) ([0-9]+)( |$)/\1\2 \1:\3:1 /
t a
s/ $//'
Notes
-r
use EREs/old/new/
replaceold
withnew
^([0-9]+)
save some numbers at the start of the line\1
backreference to first saved pattern:a
label this section of the scripta
( |$)
either a space or the end of the linet
test whether the last replacement was successful - if it was, then do the next commanda
find the label:a
and do it agains/ $//
remove the trailing space
So after adding the structure to the first part, we repeatedly find the last instance of the structure and apply it to the next number...
But I agree other tools make it easier...
With awk:
awk '{printf "%s ",$2; for (i=3; i<=NF; i++) printf $1":"$i":1 "; printf "\n"}' file
or with bash:
while read -r -a a; do # read line to array a
printf "%s " ${a[1]} # print column #1
for ((i=2;i<${#a[@]};i++)); do # loop from column #2 to number of columns
printf "%s " "${a[0]}:${a[$i]}:1" # print content/values
done
echo # print line break
done < file # read file from stdin
Output:
0 565:10:1 565:12:1 565:23:1 565:18:1 565:17:1 565:25:1 1 564:7:1 564:12:1 564:13:1 564:16:1 564:18:1 564:40:1 564:29:1 564:15:1