Replace specific field on specific line sed or awk

I'm trying to update a text file via sed/awk, after a lot of searching I still can't find a code snippet that I can get to work.

Brief overview:
I have user input a line to a variable, I then find a specific value in this line 10th field in this case. After asking for new input and doing some arithmetic I'd like to replace the 10th field on the 4th line(assuming user chose 4) in the input file.

Any ideas? Thanks in advance

Sample input and expected output will help us to help you...

--ahamed

Update the mileage on a bike
assume line 4 is:
2005:Suzuki:GS500f:Blue:6500:6000:6500:6500:6500:7000

I've extracted the 10th field of the 4th line i.e. 7000.
User enters 12000 (new odometer reading)
If the difference is greater than 4000 (scheduled maintenance), I need to "service" the bike and update the mileage.

Replace 4th line, 10th field with new odometer reading
2005:Suzuki:GS500f:Blue:6500:6000:6500:6500:6500:12000

Try this...

awk -F: '{if($0~bike&&new_mileage-$10>=4000){$10=new_mileage}}1' bike=Suzuki new_mileage=12000 OFS=":" input_file

or

#!/bin/bash

read -p "Which bike ? : " bike
read -p "Enter new mileage : " new_mileage
old_mileage=$( sed "/$bike/{s/.*:\([0-9]*\)$/\1/g}" input_file )
((diff=new_mileage-old_mileage))
if [ $diff -ge 4000 ]
then
        sed "/$bike/{s/$old_mileage$/$new_mileage/g}" input_file
fi

Use -i option with sed to make the changes inline i.e. directly

--ahamed

I have a large number of these not specific to values, so I was hoping to replace $# rather than specific values.

Also wouldn't the sed replace all the values? Thanks for the -i that might be what I was missing

    #prob pipe was not needed but I ccouldn't get the syntax, revisiting later
    old_mileage=$(awk -F':' '{print $10}' motorcycles.txt | awk -v line=$1 'FNR==line')
    routine_maint=$(awk -F':' '{print $6}' motorcycles.txt | awk -v line=$1 'FNR==line')
    (( difference=current_mileage-old_mileage ))
    
        if [ "$difference" -ge "$routine_maint" ]; then
            echo "Overdue"
            #do stuff here ie maintenance
        else
            echo "else"
        fi

But I have 5 different function i.e. front tire, rear tire, chain, etc and I'm hoping to just change the $# to update for the specific function

You can replace with sed also but it might get difficult to form the regex. AWK will be pretty easy!

--ahamed

---------- Post updated at 02:16 PM ---------- Previous update was at 02:05 PM ----------

old_mileage=$( awk -F':' 'FNR==line{print $10}' line=$1 motorcycles.txt )
routine_maint=$( awk -F':' 'FNR==line{print $6}' line=$1 motorcycles.txt )

You don't need 2 awk in your code.
--ahamed

awk -F: '{if($0~bike&&new_mileage-$10>=4000){$10=new_mileage}}1' bike=Suzuki new_mileage=12000 OFS=":" input_file

I could change this to awk -F: 'if{(FNR==linenum){$10=new_mileage}}1' linenum=$linenum new_mileage=$new_mileage OFS":" input_file, correct?

Quick question, the 1 following the $10=new_mileage, what's its purpose?

awk -F: 'FNR==linenum{if(new_mileage-$10>=4000)$10=new_mileage}1' linenum=$linenum new_mileage=$new_mileage OFS":" input_file

1 => is for printing the output.

--ahamed

I tried using the exact code above and got an error could not find file OFS etc before attempting to modify it to work for my particular case.
awk: cmd. line:1: fatal cannot open file `OFS`: for reading (No such file or directory)

I already have the action in a conditional statement, so I just need to modify the data on the variable line number in the 10th field and have this written to the file, not just displayed on screen.

if [ "$rout_difference" -ge "$routine_maint" ]; then
            echo "Routine maintenance is required on this bike"
            echo "Would you like to perform this maintenance and update your records?"
            echo "Please enter Yes to confirm, [RETURN] to skip"
            read confirm_routine
                if [ "$confirm_routine" == "Yes" -o "yes" -o "YES" ]; then
                    echo "Updating routine maintenance record"
                    awk -F: 'FNR==linenum{$10=new_mileage}1' linenum=$1 new_mileage=$current_new_mileage OFS":" motorcycles.txt
                fi

A bug... try this...

awk -F: 'FNR==linenum{if(new_mileage-$10>=4000)$10=new_mileage}1' linenum=$linenum new_mileage=$new_mileage OFS=":" input_file

--ahamed

Still no luck.

ex record in file:
2005:Suzuki:GS500f:Blue:6500:4000:6500:6500:6500:6500

commands runs but does not update

root@bt:/tmp# cat input_file
2005:Suzuki:GS500f:Blue:6500:4000:6500:6500:6500:6500

root@bt:/tmp# awk -F: 'FNR==linenum{if(new_mileage-$10>=4000)$10=new_mileage}1' linenum=1 new_mileage=12000 OFS=":" input_file
2005:Suzuki:GS500f:Blue:6500:4000:6500:6500:6500:12000

--ahamed

It displays correctly but does not update the file

It will not... You have to redirect the output to a temp file and then rename it...

awk '{...}' file > temp_file
mv -f temp_file file

--ahamed

And that's the piece I've been missing, sorry for being such a pain :smiley: