Home > Enterprise >  Using awk to make changes to nth character in nth line in a file
Using awk to make changes to nth character in nth line in a file

Time:11-12

I have written an awk command

awk 'NR==5 {sub(substr($1,14,1),(substr($1,14,1) 1)); print "test.py"}' > test.py

This is trying to change the 14th character on the 5th line of a python file. For some reason this doesn't stop executing and I have to break it. It also deletes the contents of the file.

Sample input:

import tools

tools.setup(
    name='test',
    tagvisc='0.0.8',
    packages=tools.ges(),
    line xyz
)

`

Output:

import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',
    packages=tools.ges(),
    line xyz
)

CodePudding user response:

If I understand the nuances of what you need to do now, you will need to split the first field of the 5th record into an array using "." as the fieldsep and then remove the "\"," from the end of the 3rd element of the array (optional) before incrementing the number and putting the field back together. You can do so with:

awk '{split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"\","}1'

(NR==5 omitted for example)

Example Use/Output

$ echo 'tagvisc="3.4.30"', | 
  awk '{split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"\","}1'
tagvisc="3.4.31",

I'll leave redirecting to a temp file and then back to the original to you. Let me know if this isn't what you need.

Adding NR == 5 you would have

awk 'NR==5 {split($1,a,"."); sub(/["],/,"",a[3]); $1=a[1]"."a[2]"."(a[3] 1)"\","}1' test.py > tmp; mv -f tmp test.py

CodePudding user response:

Using GNU awk for the 3rd arg to match() and "inplace" editing:

$ awk -i inplace '
    match($0,/^([[:space:]]*tagvisc=\047)([^\047] )(.*)/,a) {
        split(a[2],ver,".")
        $0 = a[1] ver[1] "." ver[2] "." ver[3] 1 a[3]
    }
    { print }
' test.py

$ cat test.py
import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',
    packages=tools.ges(),
    line xyz
)

CodePudding user response:

Using awk to make changes to nth character in [mth] line in a file:

$ awk 'BEGIN{FS=OFS=""}NR==5{$18=9}1' file # > tmp && mv tmp file

Outputs:

import tools

tools.setup(
    name='test',
    tagvisc='0.0.9',   <----- this is not output but points to what changed
    packages=tools.ges(),
    line xyz
)

Explained:

$ awk '
BEGIN {
    FS=OFS=""    # set the field separators to empty and you can reference
}                # each char in record by a number
NR==5 {          # 5th record
    $18=9        # and 18th char is replaced with a 9
}1' file         # > tmp && mv tmp file # output to a tmp file and replace

Notice: Some awks (probably all but GNU awk) will fail if you try to replace a multibyte char by a single byte one (for example utf8 ä (0xc3 0xa4) with an a (0x61) will result in 0x61 0xa4). Naturally an ä before the position you'd like to replace will set your calculations off by 1.

Oh yeah, you can replace one char with multiple chars but not vice versa.

CodePudding user response:

Get away from the fixed line number (NR==5) and fixed character position (14) and instead look at dynamically finding what you want to change/increment, eg:

$ cat test.py
import tools

tools.setup(
    name='test',
    tagvisc='0.0.10',
    packages=tools.ges(),
    line xyz
)

One awk idea to increment the 10 (3rd line, 3rd numeric string in line):

awk '
/tagvisc=/ { split($0,arr,".")                             # split line on periods
             sub("." arr[3] 0 "\047","." arr[3] 1 "\047")  # replace .<oldvalue>\047 with .<newvalue>\047; \047 == single quote
           }
1
' test.py

NOTES:

  • arr[3] = 10',; with arr[3] 0 awk will take the leftmost all-numeric content, strip off everything else, then add 0, leaving us with arr[3] = 10; same logic applies for arr[3] 1 (arr[3] 1 = 11); basically a trick for discarding any suffix that is not numeric
  • if there are multiple lines in the file with the string tagvisc='x.y.z' then this will change z in all of the lines; we can get around this by adding some more logic to only change the first occurrence, but I'll leave that out for now assuming it's not an issue

This generates:

import tools

tools.setup(
    name='test',
    tagvisc='0.0.11',
    packages=tools.ges(),
    line xyz
)

If the objective is to overwrite the original file with the new values you have a couple options:

# use temporary file:

awk '...' test.py > tmp ; mv tmp test.py

# if using GNU awk, and once accuracy of script has been verified:

awk -i inplace '...' test.py

CodePudding user response:

With GNU awk. Use . and " as field separator.

awk -v FS='\\.|"' -v q='"' 'NR==5{$0=$1 q $2 "." $3 "." $4 1 q}1' file

CodePudding user response:

something like this...

$ awk 'function join(a,k,s,sep) {for(k in a) {s=s sep a[k]; sep="."}  return s}
       BEGIN {FS=OFS="\""}
       /^tagvisc=/{v[split($2,v,".")]  ; $2=join(v)}1' file > newfile
  • Related