Reputation: 5826
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.
I am trying to make this script work as expected.
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
setenv BLAH randomstring
script.sh newvalue
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.
Upvotes: 4
Views: 2881
Reputation: 70822
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}'
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.
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
Upvotes: 6
Reputation:
I think you can make the sed
a bit easier if you pass over it with a quick grep
afterward and append the text if its still not found.
E.g.
ENV_KEY=BLAH
ENV_VAL=foo
sed -i "/BLAH/s/^.*\$/setenv ${ENV_KEY} ${ENV_VAL}/g" text.tmp
! grep -q "${ENV_KEY}" text.tmp && echo "setenv ${ENV_KEY} ${ENV_VAL}" >> text.tmp
This tells sed
to search for the string BLAH, if it finds it then replace the entire line ^.*$
. The -i
option tells sed
to update the file in place.
Upvotes: 1
Reputation: 58430
This might work for you (GNU sed):
sed '/blah/h;//s/foo/newfoo/;$!b;G;/blah/!s/.$/&setenv blah newfoo/;t;P;d' file
If blah
found store that line in the hold space (HS) and substitute newfoo
for foo
. Print each line if not end-of-file. At end-of-file append the HS to the pattern space and check for blah
. If not found append setenv blah newfoo
and print otherwise print the last line and delete the remainder.
Upvotes: 1
Reputation: 785246
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.
Upvotes: 1