Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Regex to replace or append line in file | Linux Shell script

I am trying to find the proper command to execute on a file that looks something like this: (ie .cshrc files)

setenv BLAH foo

I need the command to replace the line where it detects the string BLAH and replace the entire line like such:

setenv BLAH newfoo 

If BLAH doesn't exist in the file, then append it to the file.

I've played around with sed like such, but this does not achieve my goal.

sed 's/^.*?BLAH.*/setenv BLAH newfoo/g' text.txt > text.tmp && mv text.tmp text.txt

I've also played with awk and also cant seem to get the command working exactly how i want it.

awk -v s="setenv BLAH newfoo" '/^BLAH/{f=1;$0=s}7;END{if(!f)print s}' text.txt > text.tmp && mv text.tmp text.txt

Any ideas on how to achieve this would be awesome.

UPDATE

I am trying to make this script work as expected.

script

ARG1="$1"
sed -i "/BLAH/s/^.*\$/setenv ${ENV_KEY} ${ENV_VAL}/g" file.txt
# If the BLAH keyword isnt there, append the file then.
grep -v -q "BLAH" file.txt && echo "setenv BLAH ${ARG1}" >> file.txt

file.txt (default)

setenv BLAH randomstring

usage of script

script.sh newvalue

file.txt (result)

setenv BLAH newvalue

Currently the script seems to be appending the file everytime. I'd also tried a few of the other recommendations but i cannot get them to accept the incoming arg1 value in the sed string.

like image 287
Jaison Brooks Avatar asked Jan 08 '23 05:01

Jaison Brooks


2 Answers

Intro

This could by done by this sed script:

sed  '/BLAH/{s/ \w+$/ newfoo/;h};${x;/BLAH/ba;x;s/$/\nsetenv BLAH newfoo/;x;:a;x}'

Explanation

sed -e '
    /^\(\|.*\W\)BLAH\(\W.*\|\)$/{  # lines matching word BLAH
      s/ \w\+$/ newfoo/; # replace last word by "newfoo"
      h;         # Store for not adding them at end of file 
    };
    ${           # On last line...
      x;         # Swap stored line with current line
      /BLAH/ba;  # if match, branch to label a:
      x;         # Swap back
      s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
      x;         # Swap again
    :a;          # Label a:
      x          # Swap back
}'

You could use with in place edition switch:

sed -e'/BLAH/{h};${x;/BLAH/ba;x;s/$/\nsetenv BLAH newfoo/;x;:a;x}' -i text.txt

To be more accurate, search for delimited word BLAH:

sed -e '
    /^\(.*\W\|\)BLAH\(\W.*\|\)$/{h}; # Store lines matching word BLAH
    ${           # On last line...
      x;         # Swap stored line with current line
      /./ba;  # if match, branch to label a:
      x;         # Swap back
      s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
      x;         # Swap again
    :a;          # Label a:
      x          # Swap back
    }'  <(echo BLAH=foo;seq 1 4)
BLAH=foo
1
2
3
4

while

sed -e '
    /^\(.*\W\|\)BLAH\(\W.*\|\)$/{h}; # Store lines matching word BLAH
    ${           # On last line...
      x;         # Swap stored line with current line
      /./ba;  # if match, branch to label a:
      x;         # Swap back
      s/$/\nsetenv BLAH newfoo/; # Replace end of line with newline...
      x;         # Swap again
    :a;          # Label a:
      x          # Swap back
    }'  <(echo BLAHBLAH=foo;seq 1 4)
BLAHBLAH=foo
1
2
3
4
setenv BLAH newfoo

You may see that second BLAH was replaced by .. This could be done because if 1st was not found, swap space is empty. So if there is at least one char, this mean that 1st did occure.

Clean and script

There is a way to make this more scriptable:

sedcmd='/^\(.*\W\|\)%s\(\W.*\|\)$/{s/ \w\+$/ %s/;h};'
sedcmd+='${x;/./ba;x;s/$/\\nsetenv %s %s/;x;:a;x}'
varnam=BLAH
varcnt=foo
filnam=/tmp/file.txt
printf -v sedcmd "$sedcmd" ${varnam} ${varcnt} ${varnam} ${varcnt}
sed -e "$sedcmd" -i "$filnam"

You could remove -i switch on last line...

You could try this by running:

sed -e "$sedcmd" <(echo setenv BLAH oldfoo;seq 1 4) 
setenv BLAH foo
1
2
3
4

sed -e "$sedcmd" <(echo setenv BLAHBLAH oldfoo;seq 1 4) 
setenv BLAHBLAH oldfoo
1
2
3
4
setenv BLAH foo
like image 200
F. Hauri Avatar answered Jan 16 '23 21:01

F. Hauri


This awk command should work for both cases:

awk -v kw='BLAH' '$2 == kw{$3="newfoo"; seen=1} 1;
      END{if (!seen) print "setenv " kw " newfoo"}' file

You can pass ny other keyword to search using -v kw=... command line option.

END block will execute only when given keyword is not found in the file.

like image 22
anubhava Avatar answered Jan 16 '23 21:01

anubhava