How to format the output of a program using awk, transforming the first column but keeping the rest as if?

Viewed 136

I need to format the output of go tool cover so I can split the first column by / so I get only the last two values, but keep the rest of the output as the original one.

Here is how the original output looks like:

$ go tool cover -func=coverage.out
github.com/company/utils/common.go:6:              IsFoo                   100.0%
github.com/company/utils/logger.go:118:            maskFoo                 100.0%
github.com/company/utils/logger.go:127:            createFoo               100.0%
github.com/company/utils/logger.go:132:            CreateFakeFoo           100.0%
github.com/company/utils/foo_validaiton.go:43:     IsFoo                   100.0%
github.com/company/utils/foo_validaiton.go:49:     GreaterThanFoo          100.0%
github.com/company/utils/yaml.go:39:               closeFOO                100.0%
total:                                             (statements)            100.0%

I tried using awk to split the first column and get only the utils/file.go part, but the columns are not align.

$ go tool cover -func=coverage.out | awk '{split($1, a, "/"); print a[4]"/"a[5]"\t"$2"\t"$3}'

utils/common.go:6:  IsFoo  100.0%
utils/logger.go:118:  maskFoo  100.0%
utils/logger.go:127:  createFoo  100.0%
utils/logger.go:132:  CreateFakeFoo  100.0%
utils/foo_validaiton.go:43:  IsFoo  100.0%
utils/foo_validaiton.go:49:  GreaterThanFoo  100.0%
utils/yaml.go:39:  closeFOO  100.0%
/  (statements)  100.0%

How can I archive the following output using awk or another cli tool?

utils/common.go:6:              IsFoo                   100.0%
utils/logger.go:118:            maskFoo                 100.0%
utils/logger.go:127:            createFoo               100.0%
utils/logger.go:132:            CreateFakeFoo           100.0%
utils/foo_validaiton.go:43:     IsFoo                   100.0%
utils/foo_validaiton.go:49:     GreaterThanFoo          100.0%
utils/yaml.go:39:               closeFOO                100.0%
total:                          (statements)            100.0%
6 Answers

You can use sed:

go ... | sed 's~\([^/]\{1,\}/\)\{2\}~~'

For nicely aligned output, pipe again to column -t

go ... | sed 's~\([^/]\{1,\}/\)\{2\}~~' | column -t

PS: Of course awk can also being used, but for a pure regex search an replace operation sed is probably good enough:

awk '/\//{sub(/[^\/]+\/{2}/,"",$1)}1' | column -t

If you want to use split:

go ... \
    | awk '{if(split($1, a, "/")>1){print a[3]"/"a[4]"\t"$2"\t"$3}else{print}}' \
    | column -t

If you want to keep the last 2 values separated by / from the first column, you can use a pattern to match 1 or more characters before and after the last occurrence of a foward slash, and set that match to the value of column 1 again.

Loading the example data from an input file:

awk 'match($1, /[^\/]+\/[^\/]+$/) {
  $1 = substr($1, RSTART, RLENGTH)
}1' file | column -t

Output

utils/common.go:6:           IsFoo           100.0%
utils/logger.go:118:         maskFoo         100.0%
utils/logger.go:127:         createFoo       100.0%
utils/logger.go:132:         CreateFakeFoo   100.0%
utils/foo_validaiton.go:43:  IsFoo           100.0%
utils/foo_validaiton.go:49:  GreaterThanFoo  100.0%
utils/yaml.go:39:            closeFOO        100.0%
total:                       (statements)    100.0%

The command would be:

go tool cover -func=coverage.out | awk 'match($1, /[^\/]+\/[^\/]+$/){$1=substr($1,RSTART,RLENGTH)}1' | column -t

If you want to make the pattern more specific, ending on digits between colons like :118: for example:

/[^\/]+\/[^\/]+:[0-9]+:$/

Using awk only, you could do:

awk '$1~/^github\.com/{sub(/^.*utils\//,"utils/"); print; next}1'  file

Example Use/Output

With your program output in the file godata you would have:

$ awk '$1~/^github\.com/{sub(/^.*utils\//,"utils/"); print; next}1' godata
utils/common.go:6:              IsFoo                   100.0%
utils/logger.go:118:            maskFoo                 100.0%
utils/logger.go:127:            createFoo               100.0%
utils/logger.go:132:            CreateFakeFoo           100.0%
utils/foo_validaiton.go:43:     IsFoo                   100.0%
utils/foo_validaiton.go:49:     GreaterThanFoo          100.0%
utils/yaml.go:39:               closeFOO                100.0%
total:                                             (statements)            100.0%

If you must adjust the final total line so it lines up in columns, then you can get the record length for the record above and remove that many spaces beginning with the first space in the record.

That would look like:

awk '
    $1~/^github\.com/{
        sub(/^.*utils\//,"utils/")
        l = length($0)
        print
        next
    }
    {
        diff = length($0)-l
        rem = sprintf("%*s", diff, " ")
        sub(rem,"")
        print
    }
' file

Example Use/Output

$ awk '
>     $1~/^github\.com/{
>         sub(/^.*utils\//,"utils/")
>         l = length($0)
>         print
>         next
>     }
>     {
>         diff = length($0)-l
>         rem = sprintf("%*s", diff, " ")
>         sub(rem,"")
>         print
>     }
> ' godata
utils/common.go:6:              IsFoo                   100.0%
utils/logger.go:118:            maskFoo                 100.0%
utils/logger.go:127:            createFoo               100.0%
utils/logger.go:132:            CreateFakeFoo           100.0%
utils/foo_validaiton.go:43:     IsFoo                   100.0%
utils/foo_validaiton.go:49:     GreaterThanFoo          100.0%
utils/yaml.go:39:               closeFOO                100.0%
total:                          (statements)            100.0%

Choose whichever one you need.

For completeness, a simple awk solution:

go tool cover -func=coverage.out |
    awk '{ sub(/.*utils/,"utils",$1); print }' |
      column -t

utils/common.go:6:           IsFoo           100.0%
utils/logger.go:118:         maskFoo         100.0%
utils/logger.go:127:         createFoo       100.0%
utils/logger.go:132:         CreateFakeFoo   100.0%
utils/foo_validaiton.go:43:  IsFoo           100.0%
utils/foo_validaiton.go:49:  GreaterThanFoo  100.0%
utils/yaml.go:39:            closeFOO        100.0%
total:                       (statements)    100.0%

Also you could pipe this simple awk:

$ go tool cover -func=coverage.out | awk '{sub(/([^\/]+\/){2}/,"")} 1' | column -t

tested using the output data:

awk '{sub(/([^\/]+\/){2}/,"")} 1' file | column -t
utils/common.go:6:           IsFoo           100.0%
utils/logger.go:118:         maskFoo         100.0%
utils/logger.go:127:         createFoo       100.0%
utils/logger.go:132:         CreateFakeFoo   100.0%
utils/foo_validaiton.go:43:  IsFoo           100.0%
utils/foo_validaiton.go:49:  GreaterThanFoo  100.0%
utils/yaml.go:39:            closeFOO        100.0%
total:                       (statements)    100.0%

With your shown samples, please try following awk program. Simple explanation would be, using match function of awk to match regex string utils till next occurrence of space and saving it into 1st field and printing edited/non-edited lines.

awk '
match($1,/utils[^[:space:]]+/){
  $1=substr($1,RSTART,RLENGTH)
}
1
' Input_file| column -t
Related