There are several questions about matching between only the first occurrence of two patterns (e.g., this) — but they all seem to rely on exit
, which is no good, as I want to keep processing the file.
What I want to do is modify the first occurrence of a range between patterns (but not other occurrences) and do other awk foo. Specifically, I'm trying to comment in/out some sections of a dovecot conf file. In a truly ideal world, I'd want a single line* that makes sure the userdb { driver = prefetch }
block is un-commented, and all other userdb { }
blocks are commented. But since the file is at least a little predictable, I was aiming for uncommenting the first commented block, and then commenting all others.
I have
... some stuff that should be echoed as is ...
#userdb {
# driver = prefetch
#}
... more stuff
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
... still more stuff
#userdb {
#driver = static
#args = uid=vmail gid=vmail home=/var/vmail/%u
#}
And I want
... original stuff ...
userdb {
driver = prefetch
}
... more stuff
#userdb {
# driver = sql
# args = /etc/dovecot/dovecot-sql.conf.ext
#}
... still more stuff
#userdb {
#driver = static
#args = uid=vmail gid=vmail home=/var/vmail/%u
#}
I thought I was going to get somewhere with setting a flag in awk, thusly:
awk '/^#userdb/,/^#}/ && !ididit {
print substr($0,2);
next;
ididit=1;
}
/^userdb/,/}/ {
print "#", $0;
next
}
{print}' auth-sql.conf.ext
Which was oh-so-close, but setting my ididit
flag (I added a bunch of prints, just to make sure it was actually getting set) doesn't cut it:
... original stuff ...
userdb {
driver = prefetch
}
... more stuff
# userdb {
# driver = sql
# args = /etc/dovecot/dovecot-sql.conf.ext
# }
... more stuff ...
userdb {
#driver = static
#args = uid=vmail gid=vmail home=/var/vmail/%u
}
Note the last userdb {}
block managed to get itself uncommented. This was quite vexing, until I found deep in the bowels of GNU awk docs this little gem:
echo Yes | awk '/1/,/2/ || /Yes/'
The author of this program intended it to mean
(/1/,/2/) || /Yes/
. However, awk interprets this as/1/, (/2/ || /Yes/)
. This cannot be changed or worked around; range patterns do not combine with other patterns.
So, my little && !ididit
is a non-starter.
Any suggestions on how I can make this (or better, my "In a truly ideal world", from above) happen on a single line*? perl
is not an option, but sed
or even bash
could be.
* "a single line" can be a fairly complex line, though I'd still like it to be understandable, and throw a reasonable exit code — I'm trying to put this into an AWS CloudFormation template.
FWIW
$awk -V
GNU Awk 4.0.2
Thanks!
awk
command into shorter lines, so it doesn’t require horizontal scrolling? – G-Man Says 'Reinstate Monica' Apr 16 '19 at 15:16