3

I have the following input file:

a,10,12,13
b,20,22
c,30
d,33

and would like to append zeros until each line has three numbers, so this should be the output:

a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0

In sed I could do use these two commands:

sed 's/\([a-z],[0-9]\+$\)/\1,0,0/g'
sed 's/\([a-z],[0-9]\+,[0-9]\+$\)/\1,0/g'

My regex knowledge is limited to sed and I would like to know how to achieve this with awk or perl.

builder-7000
  • 7,131
  • 3
  • 19
  • 43
  • I see that you've added an `awk` tag, but I don't see any awk code in your question. Could you add some? I'd love to help, but I don't think that sed is the best tool for something like this. :-) – ghoti Apr 11 '18 at 03:12
  • I also think `awk` is better than `sed` for this particular job but I'm a beginner with `awk` so I need help. – builder-7000 Apr 11 '18 at 03:15

7 Answers7

5

As a Perl one-liner

perl -pe 's/$/,0/ until tr/,// >= 3' myfile

output

a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0
Borodin
  • 126,100
  • 9
  • 70
  • 144
4

Super simple in awk.

$ awk -F, '{for(;NF<4;$(NF+1)=0);} 1' OFS=, i.csv

This uses a for loop whose condition is your field count goal and whose action adds another field. This works in BSD awk and GNU awk, I haven't tested in mawk or other awks.

In sed, however, the solution looks a little more complicated.

$ sed -e ':start' -e '/,.*,.*,/b end' -e 's/$/,0/' -e 'b start' -e ':end' i.csv

Or more compactly:

$ sed -e ':start
  /,.*,.*,/b end
  s/$/,0/
  b start
  :end' i.csv

The strategy here is to test for four fields by looking for three field separators, then step through a loop that adds a ,0 until we pass that test, at which point we b end (branch to the :end label) to quit. Then print the line, as it's the default action.

This was tested in BSD sed, but should be equally compatible (and perhaps more compactable) in GNU sed.

ghoti
  • 45,319
  • 8
  • 65
  • 104
4
$ awk 'BEGIN{FS=OFS=","} {for (i=2;i<=4;i++) $i+=0} 1' file
a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
4

With perl

$ perl -F, -lane 'print join ",", @F, (0)x(3-$#F)' ip.txt
a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0

$ perl -F, -lane 'print join ",", @F, ("NA")x(3-$#F)' ip.txt
a,10,12,13
b,20,22,NA
c,30,NA,NA
d,33,NA,NA
  • -F, input field delimiter is , and results are accessible from @F array
  • (0)x(3-$#F) add missing zeroes. $#F gives index of last element, for example it is 2 for the second line - so 3-2 zeroes added
    • ("NA")x(3-$#F) to use NA instead of 0 as filler element
  • join "," use , as delimiter to join array elements


Inspired from Borodin's answer

$ perl -pe 's|$|",0" x (3 - tr/,//)|e' ip.txt 
a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0
  • e modifier allows to use Perl code in replacement section
  • tr/,// will give number of , in input line
  • x will repeat the given string by (3 - tr/,//)
Sundeep
  • 23,246
  • 2
  • 28
  • 103
3

Following awk may help here too.

awk -F, 'NF<4{i="";while(i<(4-NF)){val=val?val OFS "0":",0";i++}}{print $0 val;val=""}' OFS=,  Input_file

Adding a non-one liner form of solution too now.

awk -F, '
NF<4{
  i="";
  while(i<(4-NF)){
    val=val?val OFS "0":",0";
    i++}
}
{
  print $0 val;
  val=""
}' OFS=,  Input_file
RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93
2

In awk:

$ awk 'BEGIN{FS=OFS=",";nf=4}{for(i=(NF+1);i<=nf;i++)$i=0}1' file
a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0

Explained:

$ awk '
BEGIN {
    FS=OFS=","               # separators
    nf=4                     # desired field count
}
{
    for(i=(NF+1);i<=nf;i++)  # da loop to create new fields
        $i=0                 # set new fields to 0 
}1' file                     # output
James Brown
  • 36,089
  • 7
  • 43
  • 59
0
awk 'BEGIN{FS=OFS=","}$3==""{$3="0"}$4==""{$4="0"}1' file

a,10,12,13
b,20,22,0
c,30,0,0
d,33,0,0

First it says to keep the field separator in the output as well and then fill empty fields in $3,$4 with zeros.

Claes Wikner
  • 1,457
  • 1
  • 9
  • 8