Match a pattern within a block of text bounded by two different patterns - bash

Is there a way to match a pattern within a block of text whose boundaries are two unique patterns on different lines? By that I mean:
/*some comment*/
void something_patternWord1()
{
some_code;
some_code;
some_code_patternWord3;
some_code;
}
/*some comment patternWord2
Desired Output:
/*some comment*/
void something_patternWord1()
{
some_code;
some_code;
new_line_of_some_code; //after performing sed command
some_code_patternWord3;
some_code;
}
/*some comment patternWord2
What I've tried:
sed '/patternWord1/,/patternWord2/{/patternWord3/i new_line_of_some_code;}' inputFile > outputFile
The above command doesn't seem to be working.
Ultimately, what I'm trying to achieve is using bash script, read in some stuff from .csv file, and if the word read in is someWord then search for a block of text bounded by patternWord1 and patternWord2, match patternWord3 within that block of text and insert some code above it.
I have many .cpp files with many different functions and therefore many combinations of different patternWord1 and patternWord2.
Is what I'm trying to do even possible with sed, and is sed the best tool for something like this?
Perhaps another approach: would it be possible for me to use if-then statements in bash while having the script read in a .cpp file, and then use the sed command to match for patternWord3 and insert a new line of code?
If you could help me out with sed that would be great, but also I'm open to other suggestions as well if it make more sense/it is more efficient. Thank you in advance.

The point here is to tell the insert command where to stop.
You may do it several ways, either placing the inserted string on a new line, or by closing the sed command with the ' where the inserted line should end.
You may use
sed -e '/patternWord1/,/patternWord2/{/patternWord3/i\
new_line_of_some_code;
}'
Or,
sed -e '/patternWord1/,/patternWord2/{/patternWord3/i \ \ \ new_line_of_some_code;' -e '}' inputFile > outputFile
See the online demo:
s='/*some comment*/
void something_patternWord1()
{
some_code;
some_code;
some_code_patternWord3;
some_code;
}
/*some comment patternWord2'
sed -e '/patternWord1/,/patternWord2/{/patternWord3/i \ \ \ new_line_of_some_code;' -e '}' <<< "$s"
Output:
/*some comment*/
void something_patternWord1()
{
some_code;
some_code;
new_line_of_some_code;
some_code_patternWord3;
some_code;
}
/*some comment patternWord2

Related

How could I add new string while keeping the tab line?

I tried to add new string using shell script when the code line have "MSG_LOG".
I did add new string at the new line using this shell script,
#!/bin/bash
############Set File Name
fileName=$1
echo ">>>>Set_FileName is done"
echo ">>>>fileName is ${fileName}"
############Get Elements
lineNumArr=(`grep -n "MSG_LOG" $fileName | awk -F ':' '{print $1}'`)
echo ">>>>Getting LineNumArr is done"
for lineNum in "${lineNumArr[#]}"
do
############Replace logger
sed -i "${lineNum} a printf('MSG_Log is show');" $fileName
echo ">>>>Replace_logger is Done"
done
echo ">>>>for loop is closed"
exit
But it doesn't keep tabs on existing lines of code.
For Example, I want to make my .cpp file like this,
#include<iostream>
int main(void){
bool value=true;
....
if(value){
MSG_LOG("VALUE IS TRUE");
printf('MSG_LOG is show');
}
....
But when I actuate this shell script, the result is...
#include<iostream>
int main(void){
bool value=true;
....
if(value){
MSG_LOG("VALUE IS TRUE");
printf('MSG_LOG is show');
}
....
How can I add new code while preserving the tablines?
You should add the same amount of withespace as the previous line when adding a new line.
You want something like this
sed -E 's/^(.*)(MSG_LOG\(.*\);)(.*)$/\1\2\3\n\1printf("MSG_LOG is show");/g'
If I understood correctly.
You don't have to invoke sed once per line as it loops over the entire file.
Also, you need double quotes in printf.
Using sed, you can duplicate the original line containing MSG_LOG and then replace the text, this will keep the tab spacing in place.
$ sed "/MSG_LOG/{p;s/[[:alpha:]].*/printf('MSG_LOG is show');/}" "$fileName"

sed or awk add content to a body of a c function running in git bash v2.21.0

To turn this function in a c file (test.c)
void Fuction(uint8, var)
{
dosomething();
}
// void Fuction(uint8, var)
// should not be injected below a comment with same pattern content
into:
void Fuction(uint8, var)
{
injected1();
injected2();
injected3();
dosomething();
}
// void Fuction(uint8, var)
// should not be injected below a comment with same pattern content
By injecting this one (inject.c)
injected1();
injected2();
injected3();
I tried several approaches with sed and awk but actually i was not able to inject the code below the open curly braces the code was injected before the curly braces.
On a regex website I was able to select the pattern including the curly braces, but in my script it did not work. May be awk is more compatible, but I have no deeper experiance with awk may some one coeld help here?
With awk i had a additional problem to pass the pattern variable with an ^ancor
call in git bash should be like this:
./inject.sh "void Fuction(uint8, var)" test.c inject.c
(my actual inject.sh bash script)
PATTERN=$1
FILE=$2
INJECTFILE=$3
sed -i "/^$PATTERN/r $INJECTFILE" $FILE
#sed -i "/^$PATTERN\r\{/r $INJECTFILE" $FILE
I actually have no idear to catch also the \n and the { in the next line
My result is:
void Fuction(uint8, var)
injected1();
injected2();
injected3();
{
dosomething();
}
// void Fuction(uint8, var)
// should not be injected below a comment with same pattern content
Expanding on OP's sed code:
sed "/^${PATTERN}/,/{/ {
/{/ r ${INJECTFILE}
}" $FILE
# or as a one-liner
sed -e "/^${PATTERN}/,/{/ {" -e "/{/ r ${INJECTFILE}" -e "}" $FILE
Where:
/^${PATTERN}/,/{/ finds range of rows starting with ^${PATTERN} and ending with a line that contains a {
{ ... } within that range ...
/{/ r ${INJECTFILE} - find the line containing a { and append the contents of ${INJECTFILE}
Results:
$ ./inject.sh "void Fuction(uint8, var)" test.c inject.c
void Fuction(uint8, var)
{
injected1();
injected2();
injected3();
dosomething();
}
// void Fuction(uint8, var)
// should not be injected below a comment with same pattern content
Once OP verifies the output the -i flag can be added to force sed to overwrite the file.
NOTE: OP's expected output shows the injected lines with some additional leading white space; if the intention is to auto-indent the injected lines to match with the current lines ... I'd probably want to look at something like awk in order to provide the additional formatting.

How to match and delete some if statements from a file based on pattern matching

I have following code
if (temp==1) {
some text
}
some more text
abcdef
if (temp==1) {
some text
}
if (temp2==1) {
some text
}
I need to use any script/command to delete all the if statements.
Required output:
some more text
abcdef
if (temp2==1) {
some text
}
What i can already achieve is the following
grep -zPo "if\ \(temp==1\) (\{([^{}]++)*\})" filename
and i get the following output
if (temp==1) {
some text
}
if (temp==1) {
some text
}
Same result from perl command too
perl -l -0777 -ne
"print $& while /if \(temp==1\) (\{([^{}]++|(?1))*\})/g" filename
Now i need to delete the matched lines from the file.
So all if(temp2==1) must be retained and if(temp==1) must be deleted.
How can i do this?
What you're asking to do is impossible in general without a parser for whatever language that code is written in but you can produce the output you want from that specific input using any awk in any OS on any UNIX box with:
awk '/if \(temp==1/{f=1} !f; /}/{f=0}' file
if that's all you want.
You probably can use sed to do this:
$ sed '/temp==1/,/}/d' inputfile
some more text
abcdef
if (temp2==1) {
some text
}
Above deletes (with d) all lines between and including the patterns, /temp==1 and }.
Note: It will not work with nested patterns as OP is suggesting in his comment. As per OP's comment, one could do the following:
$ sed '/temp==1/,/}/d;/}/,/}/d' 1.txt
This removes additional texts and patterns that are between two }s.

Remove the range of lines from file using shell script

My test file test.txt is given below:
destination mailerr { file("/var/log/mail.err" fsync(yes)); };
log { source(src); filter(f_mailerr); destination(mailerr); };
#
# and also all in one file:
#
destination mail { file("/var/log/mail"); };
log { source(src); filter(f_mail); destination(mail); };
destination mailwarn { file("/var/log/mail.warn"); };
log
{
#source(src);
filter(f_mailwarn); destination(mailwarn); };
I want to remove below lines using shell script
log
{
#source(src);
filter(f_mailwarn); destination(mailwarn); };
This lines might be comes different structure like
log{ source(src); filter(f_mailwarn); destination(mailwarn); };
(or)
log
{
source(src);
filter(f_mailwarn);
destination(mailwarn);
};
(or)
log
{ source(src); filter(f_mailwarn); destination(mailwarn); };
(or)
# log { source(src); filter(f_mailwarn); destination(mailwarn); };
};
These are the possibilities. I am using sed command:
sed '/log/{:a;/destination(mailwarn);.*}/d;N;ba}' test.txt
But it'll remove all line because first line it self "log" comes. So many of "log" comes in this file so how to remove the particular lines using shell script.
You can try this sed:
sed '/^[# ]*log/{:loop; /destination(.*}/d; /}/{n}; N; b loop;}' file
Explanation:
/^[# ]*log/ - Starts to process the block {} when regex matches.
/destination(.*}/d - Deletes the pattern space when regex matches. Starts a new cycle.
/}/{n} - When it finds }, then prints pattern space and reads next line of input. (for printing out an non log...destination() line.
N - This appends next line input to pattern space.
b loop - This transfers flow control to loop.
If the file is not too big, use the -z option to process entire file in one shot (#SLePort notes that this is a GNU specific option)
sed -z 's/log[ \t\n]*{[^}]*destination(mailwarn);[ \t\n]*};//g' test.txt
log start of match
[ \t\n]*{ zero or more non space/tab/newline characters followed by {
to avoid false matching log/mail.warn"); }; if [^{]*{ was used instead
[^}]* zero or more non } characters
destination(mailwarn); string to match
[ \t\n]*}; zero or more non space/tab/newline characters followed by };
the matched pattern gets deleted as replacement string is empty
Similar with perl
perl -0777 -pe 's/log\s*\{[^}]*destination\(mailwarn\);\s*};//g' test.txt
Here is my somewhat janky way of getting the job done. It seems to work in all the cases you posted, but is in no way a universal solution to this problem.
tr '\n' '\0' < test.txt | sed 's/log[^{}]*{[^}]*destination(mailwarn);[^}]*};//g' | tr '\0' '\n'
If test.txt contains null characters, it may not work properly. This command essentially looks for log followed by a { at some point, then until a } is reached destination(mailwarn);, eventually followed by };, and deletes this match if found.
I hope someone else posts a more robust solution to this problem, but this is the only quick solution I was able to come up with.
With sed:
sed '/^#*[[:blank:]]*log/{:a;/};/!{N;ba;s/\n//;};/destination(mailwarn)/d;}' file
Adds lines from log up to }; to the pattern space, search in it for destination(mailwarn) and if found, deletes lines.

sed with replacement part given as input from command line

I am trying to pass command line argument as the replacement part to sed command. But $1 doesn't work for me.
For example I unsuccesfully tried
function changeversion() {
sed -i 's/[0-9]\+/$1/g' file.xml;
}
which only replaced all numbers in file with '$1' text.
How can I fix it?
I think you need to put the expression in double qoutes to get variable substitution
function changeversion() {
sed -i "s/[0-9]\+/$1/g" file.xml;
}

Resources