2

I have the following conf file for nginx:

user       www www;  ## Default: nobody
...

events {
  worker_connections  4096;  ## Default: 1024
}

http {
  include    conf/mime.types;
  ...

  server { # php/fastcgi
    listen       80;
    server_name  domain1.com www.domain1.com;
    access_log   logs/domain1.access.log  main;
    root         html;

    location ~ \.php$ {
      fastcgi_pass   127.0.0.1:1025;
    }
  }

  server { # simple reverse-proxy
    listen       80;
    server_name  domain2.com www.domain2.com;
    access_log   logs/domain2.access.log  main;

    # serve static files
    location ~ ^/(images|javascript|js|css|flash|media|static)/  {
      root    /var/www/virtual/big.server.com/htdocs;
      expires 30d;
    }

    # pass requests for dynamic content to rails/turbogears/zope, et al
    location / {
      proxy_pass      http://127.0.0.1:8080;
    }
  }
}

This is a foo example that I found on www.nginx.com.

The goal is to extract all the servers' blocks for various treatments. In this particular case, there are two servers' blocks that I am interested in.

Because I work on very limited environments, I can only use sed/grep/awk/unix systems commands. No python, no pearl, ...

The problem with that kind of configuration files is that it is maybe possible that the server block itself contains some sub-blocks (i.e server { ... directive { ... }}). Considering that, it is impossible to simply use grep -oP "server {.*?}".

Can grep do the job? I've tried many different regex but didn't find the good one. For now, I worked with the above file without backslash (i.e cat $FILE | tr -d "\n"). I've tried things like grep -oP "server\s{1,}{.*?({.*?}){0,}}" but it does not fit my needs...

The reason I want to use grep is that I think that awk is not very readable afterward, and to maintain the code should be easier with grep - but I could make an exception if it is way easier with awk!

Thanks :)

EDIT:

Output should looks like:

"server { # php/fastcgi
  listen       80;
  server_name  domain1.com www.domain1.com;
  access_log   logs/domain1.access.log  main;
  root         html;

  location ~ \.php$ {
    fastcgi_pass   127.0.0.1:1025;
  }
}",
"server { # simple reverse-proxy
  listen       80;
  server_name  domain2.com www.domain2.com;
  access_log   logs/domain2.access.log  main;

  # serve static files
  location ~ ^/(images|javascript|js|css|flash|media|static)/  {
    root    /var/www/virtual/big.server.com/htdocs;
    expires 30d;
  }

  # pass requests for dynamic content to rails/turbogears/zope, et al
  location / {
    proxy_pass      http://127.0.0.1:8080;
  }
}"

Something that I can treat using commands like echo $OUTPUT | tr -d ... | grep -v .... If possible, I want to have each server block extracted on a single line to loop on the result!

0

1 Answer 1

3

If I understand you correctly, you are looking for something like:

$ awk '/server *{/{c=1; print;next} c&&/{/{c++} c&&/}/{c--} c' file
  server { # php/fastcgi
    listen       80;
    server_name  domain1.com www.domain1.com;
    access_log   logs/domain1.access.log  main;
    root         html;

    location ~ \.php$ {
      fastcgi_pass   127.0.0.1:1025;
    }
  }

  server { # simple reverse-proxy
    listen       80;
    server_name  domain2.com www.domain2.com;
    access_log   logs/domain2.access.log  main;

    # serve static files
    location ~ ^/(images|javascript|js|css|flash|media|static)/  {
      root    /var/www/virtual/big.server.com/htdocs;
      expires 30d;
    }

    # pass requests for dynamic content to rails/turbogears/zope, et al
    location / {
      proxy_pass      http://127.0.0.1:8080;
    }
  }

Explanation

  • /server *{/{c=1; print; next;} : if this line matches server, 0 or more spaces and then a {, set the variable c to 1. So, c will be 1 each time we find a new server{ block. Then print the line and skip to the next one.
  • c&&/{/{c++} : if the c is defined and not 0, and this line matches a {, increment c by 1 (c++). This could have been written as if(c && /{/){ c=c+1}. So, the value of c will increase for each nested { block found.

  • c&&/}/{c--} : if c is defined and not 0, and this line matches a }, decrement c by 1. This could have been written as if(c && /}/){c=c-1}. This ensures that the value of c goes down as each nested {} block is closed.

  • c : this is an awk trick. The default action when something evaluates to true in awk is to print the current line. Here, it will print if c is defined and not 0 (true). Because of the commands above which increment and decrement c depending on whether we're in a server block, this will result in printing only the lines you're after.

11
  • It is not working for me; I've tested with both original file and with a file where I did a copy-n-paste of the content given in this post! Commented Jul 1, 2016 at 14:55
  • @ColinLeverger ah, I think the \s is a GNUism. Try the updated answer.
    – terdon
    Commented Jul 1, 2016 at 15:02
  • 1
    Just be careful of commented out lines that may cause incorrect behaviour... Commented Jul 1, 2016 at 15:17
  • 1
    @ColinLeverger no, grep is not the right tool here. It's designed for printing lines matching specific patterns. Matching multiline patterns is possible with GNU grep using the -Z flag to slurp the entire file, but here you also need to keep track of nested brackets so no, it won't work.
    – terdon
    Commented Jul 4, 2016 at 8:35
  • 1
    @ColinLeverger see updated answer. To get the location ~ \.php$ { } block in your example, you could just change the /server *{/ to /location.*php\$/.
    – terdon
    Commented Jul 12, 2016 at 8:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .