LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Newbie (https://www.linuxquestions.org/questions/linux-newbie-8/)
-   -   SED replace line after match (https://www.linuxquestions.org/questions/linux-newbie-8/sed-replace-line-after-match-947576/)

r00t 05-30-2012 08:31 AM

SED replace line after match
 
I have the following scenario:

Code:

$ cat file
# option1
setting=blah

# option2
#setting=blah

# option3
#setting=blah

Now I need to implement a function to a script to switch between the different options. Ie. comment out the next line after the pattern "option1" and remove the comment from the next line after "option2" to activate it. Ideally I'd need a command that would first make sure that each next line after "option[1-3]" is commented out and then remove the comment from the line after "option2".
I'm not that bad at bash, but sed and awk are giving me some headache at the moment, I hope someone can help me with this.

pan64 05-30-2012 08:34 AM

for me it looks much easier to recreate this file every time you run that function

r00t 05-30-2012 08:38 AM

What exactly do you mean? Of course this is just an example file, the ones I want to use this for have a _lot_ more and various content and there are plenty of them (vHosts).

pan64 05-30-2012 08:46 AM

I mean you have a template file with the full content and some keywords like THIS_IS_THE_PLACE_FOR_OPTION_1 ....
and that can be easily replaced in a single sed: s/OPT_KEYWORD_1/opt_value_1/ ...
So you do not need to take care of the generated files at all.

r00t 05-30-2012 08:49 AM

Yeah well this would be possible if the content of the file would stay the same. But imagine 50+ vHost configs with different IPs and domain names, it would be a mess in this case. Also the option strings are very long and not just a few words like in my example case, so I can't just replace the whole string.

grail 05-30-2012 09:01 AM

Would it not be simpler to maybe populate an array with the data and then just call the number as required, ie. index 0 is option 1 and so on?

r00t 05-30-2012 09:09 AM

I think using arrays would still be messy, because the option strings are very long. Example:

Code:

# option1
setting='<html><body><script type="text/javascript" src="/enc.js"></script><script>function toNumbers(d){var e=[];d.replace(/(..)/g,function(d){e.push(parseInt(d,16))});return e}function toHex(){for(var d=[],d=1==arguments.length&&arguments[0].constructor==Array?arguments[0]:arguments,e="",f=0;f<d.length;f++)e+=(16>d[f]?"0":"")+d[f].toString(16);return e.toLowerCase()}var a=toNumbers("$key"),b=toNumbers("$iv"),c=toNumbers("$set");document.cookie="Cookie="+toHex(slowAES.decrypt(c,2,a,b))+"; path=/";location.href="$url";</script></body></html>';


schneidz 05-30-2012 09:48 AM

[quick-and-dirty]
Code:

read option
grep $option file
grep $option -A 1 file | tail -n 1 | cut -b 2- # assuming each line in file begins wit '#'

this one is better:
Code:

awk 'BEGIN { RS = "" } /option2/ {print "\#" $2 "\n"  substr($3,2)}' r00t.lst

r00t 05-30-2012 10:28 AM

Thank you schneidz! The awk one looks good, I will test it later.

David the H. 05-31-2012 01:13 AM

Here's a sed solution:

Code:


sed -r -e '/^# option2/ { n ; s/^#// }' -e '/^# option/ { n ; s/^#*(.)/#\1/ }' file

The first -e expression says that, if the current line matches option2, then move on to the next line and substitute away any #'s at the start of it. The second -e is then applied and works the same way, only this time ensuring that there is a # at the front of every line.

Notice that the order of the two expressions is important in this case. The second expression ignores the "option2" line only due to the n option in the first expression. it means that that line is passed up and gone before the second expression ever has a chance to see it.

r00t 05-31-2012 06:36 AM

Many thanks David, it seems to be exactly what I was looking for (sed with n) and your explaination is very nice.

PS: Would you mind explaining the regex of the second part "s/^#*(.)/#\1/" a little more, so I completely understand it, or do you know any good paper on sed's regex? For example I don't understand this regex "*(.)" and this one "\1". Thank you in advance!

pan64 05-31-2012 07:02 AM

^ means beginning of line
#* means any number of # (including 0 or more)
(.) dot means any character, (.) means it is grouped
/ is a delimiter, between the first two there is a search expression, after the second one there is a replace expression:
# means # itself
\1 means the first group found in the search expression - which was (.)



man sed is a good place to start with (see links also at the end)



_____________________________________
If someone helps you, or you approve of what's posted, click the "Add to Reputation" button, on the left of the post.
Happy with solution ... mark as SOLVED
(located in the "thread tools")

David the H. 05-31-2012 07:17 AM

Sure.

The left-hand side:
Code:

^#*(.)
^ == The start of the line
#* == A hashmark, found zero or more "*" times
(.) == Any single character ".", captured in parentheses for use in the replacement

In essence, it matches all lines with and without #'s in front of them. The important thing is to capture the first non-# character.

You could replace the "*" with a question mark"?", for zero or one times, assuming that there's always only a single #.


The right-hand side:
Code:

#\1
# == A single hashmark
\1 == The contents of the first set of parentheses in the LHS match

So it takes whatever character is captured above and adds a hashmark to it, and this replaces the entire match. If the line had a hash previously, it ends up unchanged.

Notice the use of the -r option too. backreferencing, as this is generally called, is an extended regex feature in sed.

Here are a few regular expressions tutorials:
http://mywiki.wooledge.org/RegularExpression
http://www.grymoire.com/Unix/Regular.html
http://www.regular-expressions.info/

Regular expressions come in several flavors, as supported by different tools, but it's generally in the more advanced features where they differ. The big thing to worry about in grep and sed is the difference between basic and extended regex. The grep man page has a good explanation of them.


All times are GMT -5. The time now is 11:21 PM.