Adil Saju
Adil Saju

Reputation: 1189

Edit/Match a string n lines below some specific match in Perl?

Hi recently I've posted a problem. edit nth line below a line match in bash?
(Problem: I have to edit the PROTOCOL line 3 lines below [MARATHON])

This is the file:

# /etc/ndxconfig.ini will override this file
# if APP_ID is added in service propery, service discovery will be using marathon;
# HOST/PORT specified will override values retrieved from marathon

[MARATHON]
HOSTS = {{ ','.join(groups['marathon'])}}
PORT = 8080
PROTOCOL = http
SECRET = SGpQIcjK2P7RYnrdimhhhGg7i8MdmUqwvA2JlzbyujFS4mR8M88svI7RfNWt5rnKy4WHnAihEZmmIUb940bnlYmnu47HdUHE


[MYSQL]
; APP_ID = /neon/infra/mysql
HOST = {{keepalived_mysql_virtual_ip}}
PORT = 3306
SECRET = tIUFN1rjjDBdEXUsOJjPEtdieg8KhwTzierD48JsgDeYc84DD6Uy5a6kzHfKolq1MNS1DKwlSqxENk33UulJd9DPHPzYCxFm

The problem is if I rerun the script in some case, the
sed '/\[MARATHON\]/{N;N;N;s/http/https/;}' <file> will append more and more s to http (httpsssss).
So I would add a positive lookahead in regex for s. I can do it with perl -pe but do not know how I do it on nth line below a match. Please Help. How can I do it?

Upvotes: 4

Views: 155

Answers (2)

Timur Shtatland
Timur Shtatland

Reputation: 12347

Try this one-liner, which does exactly what you asked for: edit a string n lines below some specific match in Perl. But also consider NOTES below.

cat file | perl -lpe '
$seen = 1 if m{\[MARATHON\]}xms;
$num_lines++ if $seen;
s{ \A ( PROTOCOL \s+ = \s+ ) http }{${1}https}xms if $num_lines == 4;
'

NOTES:

The code above answers the question, but it is poor code. Why? It is generally a bad idea to parse a known, widely used format relying only on the expected patterns and the expected number of lines. Here, the format seems to be .ini, so consider searching for modules/libraries used for parsing/reading/writing config ini files. For example:

If you do not want to use the modules such as the ones above, at least consider the possible future changes to the input and using fewer assumptions in your code. In this case, the assumption of the nth line from the section header seems the least robust to change. The ini parsers in the upstream code that write this file may not care about the order of the lines. The section headers are more robust. Perhaps read the entire file (slurp) and change to https using a non-greedy multi-line regex from [MARATHON] to PROTOCOL = http. Do not specify the exact number of lines, just make sure the line you change is in the same section. But this is still reinventing the wheel - avoid doing it if you can.

SEE ALSO:

Upvotes: 2

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626758

If you are using a GNU sed, the solution can be using word boundaries:

sed '/\[MARATHON\]/{N;N;N;s/\<http\>/https/;}' file 
#                          ^^     ^^

You may also leverage context details. As the http is after PROTOCOL you may simply use this to reset the value:

sed '/\[MARATHON\]/{N;N;N;s/\(.*= *\).*/\1https/;}' file 

The \(.*= *\).* will match and capture the key with = and optional spaces and its value is restored with \1 while https will always re-write the value.

Upvotes: 2

Related Questions