Search code examples
shellawksedtext-parsing

Modify numeric values in strings from stdout


I have the input strings that I want to rewrite and modify some of the numeric values:

Input: src/main.tsx(2,31): error TS2304: Cannot find name 'foo'.

Desired output: src/main.tsx:1:30: error: TS2304: Cannot find name 'foo'.

Note that:

  • The numeric values have been reduced by 1.
  • error is dynamic. It can also be warning.
  • The command will have the input piped to it (tsc | MAGIC_HAPPENS_HERE). If there's an error then this command should pass the error along.

So far I have the following: sed -E "s/^([^(]+)\(([0-9]+),([0-9]+)\): ((warning)|(error)) (.*)/\1:\2:\3: \4: \7/"

This works except for the the numeric manipulation. From what I've read I believe sed isn't the right tool for the job. I looked at awk but hit a wall with the regex capture groups.

I'm using MacOS. The command doesn't need to be portable. I'm happy to install additional tools using brew.


Solution

  • Could you please try following.

    awk '
    match($0,/\([^)]*/){
      value=substr($0,RSTART+1,RLENGTH-1)
      num=split(value,array,",")
      for(i=1;i<=num;i++){
        val=(val?val":":"")array[i]-1
      }
      part_2=substr($0,RSTART+RLENGTH+1)
      sub(/error/,"error:",part_2)
      print substr($0,1,RSTART-1) ":" val part_2
      value=part_2=""
    }'  Input_file
    

    Output will be as follows.

    src/main.tsx:1:30: error TS2304: Cannot find name 'foo'.
    

    Explanation: Adding detailed explanation for above code.

    awk '                                          ##Starting awk program here.
    match($0,/\([^)]*/){                           ##Using match function to match regex from ( till ) in line.
      value=substr($0,RSTART+1,RLENGTH-1)          ##Creating variable value which has value of sub-string from RSTART+1 to RLENGTH-1.
      num=split(value,array,",")                   ##Using split, to split the value into an array named array.
      for(i=1;i<=num;i++){                         ##Running for loop from i=1 to till value of num(which is length of array).
        val=(val?val":":"")array[i]-1              ##Creating variable val whose value is subtraction of array[i] value with 1 and keep concatenating to its own value.
      }
      part_2=substr($0,RSTART+RLENGTH+1)           ##Creating variable part_2 whose value is rest of line after matched regex.
      sub(/error/,"error:",part_2)                 ##Substituting string error with error: here in rest of the line.
      print substr($0,1,RSTART-1) ":" val part_2   ##Printing sub-string from 1 to till match found, :, val and part_2 variables here.
      value=part_2=""                              ##Nullify variables value and part_2 here.
    }'  Input_file                                 ##Mentioning Input_file name here.