In the general case, the answer to any question regarding sed is "use something else."
Not that sed is not useful! It definitely is! It's just that it's best for simple tasks that have compact, obvious solutions.
In particular, Perl is good at replacing sed scripts, thanks to its more expressive regular expressions and uncomplicated access to conventional programming constructs. It even comes with an automated tool to do the translation from sed to Perl, called s2p
.
perl -pli.bak -e'
next unless s/^(\s*)(#\s*)?(?=server_names_hash_bucket_size)/$1/;
tr/\d//d;
s/;/64;/;
' nginx.conf
The newlines are optional and included for readability. Line by line:
-p
means "print each line after running this code against it." This should be familiar since sed works exactly the same way.
-l
means "remove newlines before running the code and reinstate them before printing."
-i.bak
means "edit in-place and save the original as original.bak
".
-e
means the same thing as in sed.
- We're only interested in lines with
server_names_hash_bucket_size
, so skip to the next line if the replace fails (the unmodified line still prints; we're skipping the rest of this code but not the printing).
- We need to include lines that had no
#
, so make the #
match optional with ?
after (#\s*)
.
(?=...)
is a lookahead; the text must exist but is not included in the left side of the substitution, so we don't need to include it on the right side. Only the (optional) leading whitespace ($1
) needs to be reinstated on the RHS.
tr
works like y
in sed. This line is equivalent to s/[0-9]//g
(as far as we're concerned. Technically, there are digits other than 0-9 but they aren't going to appear in your nginx.conf.)
Same as sed.
You can simplify even further by recognizing that the only significant parts of a server_names_hash_bucket_size
line, after you throw away the number, are the leading whitespace and terminal comments.
Preserve the indent as before, throw away as many non-comment characters as possible ([^#]*
) and rewrite the line in one replace operation:
perl -pli.bak -e' s/^(\s*)(?:#\s*)?(server_names_hash_bucket_size)[^#]*/$1$2 64;/ ' nginx.conf
(?:
makes that group non-capturing, so the second capture ($2
) will be server_names_hash_bucket_size
.
At this point, we're back in "simple enough for sed" territory:
s/^\([[:blank:]]*\)\(#[[:blank:]]*\)*\(server_names_hash_bucket_size\)[^#]*/\1\3 64;/