30

I'm trying to edit my nginx.conf file programmatically, which contains a line like this:

        access_log /var/log/nginx/access.log;

which I want to look like this:

        access_log /dev/stdout;

I believe the regex ^\s*access_log([^;]*); will capture the /var/log/nginx/access.log part in a capture group, but I'm not sure how to correctly replace the capture group with sed?

I've tried

echo "        access_log /var/log/nginx/access.log;" | sed 's/^\s*access_log([^;]*);/\1\\\/dev\\\/stdout/'

but I'm getting the error:

sed: -e expression #1, char 45: invalid reference \1 on `s' command's RHS

if I try sed -r then there is no error, but the output is not what I expect:

 /var/log/nginx/access.log\/dev\/stdout

I'm trying to be smart with the capture group and whatnot and not search directly for "access_log /var/log/nginx/access.log;" in case the distribution changes the default log file location.

3 Answers3

37

A couple of mistakes there.

First, since sed uses basic regular expressions, you need \( and \) to make a capture group. The -r switch enables extended regular expressions which is why you don't get the error. See Why do I need to escape regex characters in sed to be interpreted as regex characters?.

Second, you are putting the capture group in the wrong place. If I understand you correctly, you can do this:

sed -e 's!^\(\s*access_log\)[^;]*;!\1 /dev/stdout;!' your_file

Note the use of ! as regex delimiters to avoid having to escape the forward slashes in /dev/stdout.

Joseph R.
  • 39,549
7

In your regexp you should know that \No. is the back reference to pattern within brackets (). So regexp should be 's@^(\s*access_log ).*$@\1/dev/stdout/;@'

I can offer

sed '/access_log/s|/[^;]\+|/dev/stdout|'

HINT: Whether you intend to use / inside the patern you are free to change s/// for every symbol you'd like s### for example

Costas
  • 14,916
6

Rather than capture it, why not just find it and do a substitution? I.e.:

sed "/access_log/ s| /.*;| /dev/stdout;|"

It searches for lines that match "access_log" then on any lines that do it will replace " /whatever/path/is/there;" with " /dev/stdout;"

  • 1
    The /access_log/ string in Jeremy's is known as an "address" for the sed command. Often, numbers (indicating line numbers) are used as addresses to sed but today I learned that regular expressions can be used as addresses too. Thanks Jeremy! – dgtc Mar 17 '21 at 16:58
  • 1
    Thanks for the note dgtc - I learned something too! I knew that the line numbers were an "address", but it never occurred to me that a search string was also an "address" (it seems obvious now, I just hadn't made the connection...). :) – Jeremy Davis Mar 17 '21 at 19:34