Replace the second and third with . if it is 0

Viewed 119

I have data with three fields. Fields 2 and 3 can be zero and I want to change to "." if it is zero.

Example data:

1 0 0
2 0 0
3 0 0
4 0 1
5 1 0

Here is what I tried:

awk '{
     if($2=="0" && $3=="0"){
     `$3=".";`
     `$2=".";`
       }
       else if($3=="0"){
        $3="."
        } 
      else if($2=="0"){
       $2="." }
       else {
        $2=$2;
        $3=$3;
        }

     print $0
}' file.txt>changedfile.txt

It changed only the second field but not the third field. Thanks a lot, I want to do it with Awk control structure

6 Answers

With more Generic approach here, please try following solution. You need to simply pass field numbers where you want to perform check and replace it will dot in case its zero pass its field number to function named fillValues right now I have passed fillValues(2,3) since you want to check 2nd and 3rd fields only, this can handle n number of fields.

awk '
function fillValues(fields){
  num=split(fields,arr,",")
  for(i=1;i<=num;i++){
    $arr[i]=$arr[i]==0?".":$arr[i]
  }
}
{
  fillValues("2,3")
}
1
'  Input_file

Explanation: Adding detailed explanation for above.

awk '                                ##Starting awk program from here.
function fillValues(fields){         ##Starting function named fillValues with passing fields variable in it.
  num=split(fields,arr,",")          ##Splitting fields values into arr with delimiter of , here.
  for(i=1;i<=num;i++){               ##Starting a for loop till value of num here.
    $arr[i]=$arr[i]==0?".":$arr[i]   ##Checking if current field is 0 then assign dot or keep it as it is.
  }
}
{
  fillValues("2,3")                  ##Calling function with field number of 2 and 3 so that function could be performed on those fields.
}
1                                    ##Printing current line here.
' Input_file                         ##Mentioning Input_file name here.
$ cat input
1 0 0
2 0 0
3 0 0
4 0 1
5 1 0
$ awk '$2 == 0 { $2 = "." } $3 == 0 { $3 = "." }1' input
1 . .
2 . .
3 . .
4 . 1
5 1 .

Using gnu-awk:

awk '{print gensub(/(\s)0(\s)/, "\\1.\\2", "g", gensub(/(\s)0$/, "\\1.", "1"))}' file

1 . .
2 . .
3 . .
4 . 1
5 1 .

If there are always 3 fields, you might also loop the last 2 fields and set the value to a dot it any of the values are zero.

awk '{                          # Start the program
  for (i=2; i<=NF; ++i) {       # Loop only the last 2 fields
    if ($i == 0) $i="."         # If the value of the field is 0, set it to a dot
  }
} 1' file                       # print the whole line

Output

1 . .
2 . .
3 . .
4 . 1
5 1 .

You might use ternary operator (?:) for this task following way, let file.txt content be

1 0 0
2 0 0
3 0 0
4 0 1
5 1 0

then

awk '{$2=$2=="0"?".":$2;$3=$3=="0"?".":$3;print}' file.txt

output

1 . .
2 . .
3 . .
4 . 1
5 1 .

Explanation: ternary operator is more compact alternative to using if-else in this case. It consist of check?valueiftrue:valueifelse

(tested in gawk 4.2.1)

I guess this will be the shortest awk

$ awk '!$2{$2="."} !$3{$3="."}1' file

1 . .
2 . .
3 . .
4 . 1
5 1 .

still shorter with sed

$ sed 's/ 0\b/ ./g' file
Related