1

I'm adding a line of Perl code to a Makefile that searches for something like the following block in an httpd.conf and replaces "None" with "All" for the AllowOverride.

<Directory "/var/www/html">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    AllowOverride None

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

The code I'm trying to run from the command line is as follows:

sudo perl -p  -i -e 's/(<Directory "\/var\/www\/html">.*AllowOverride )(None)/\1 All/' httpd.conf

But I cannot get it to work. I'm using the two capture groups for keeping the first group the same and replacing the second one.

Any help is greatly appreciated.

EDIT: this resolved it

sudo perl -0777 -p -i -e 's/(<Directory \"\/var\/www\/html\">.*?AllowOverride) (None)/\1 All/s' httpd.conf
lorenzo
  • 494
  • 6
  • 23
  • 1
    The `-p` flag only read a line at a time by default. Try slurping more than a line at a time by adding `-0777`. See also [How to replace multiple any-character (including newline) in Perl RegEx?](http://stackoverflow.com/q/36533282/2173773) – Håkon Hægland Mar 14 '17 at 20:25
  • 1
    Also, make sure to use non-greedy `.*?`, otherwise it will match all the way to the very last `AllowOverride`. Use `$1`, not `\1` -- or `\K` (a form of _positive lookbehind_). – zdim Mar 14 '17 at 20:33
  • This resolved it: sudo perl -0777 -p -i -e 's/(.*?AllowOverride) (None)/\1 All/s' httpd.conf – lorenzo Mar 14 '17 at 21:01
  • Great. Again: not `\1`, but instead `$1`. The `\1` has long been not been in use for this, and while it shouldn't get you in trouble _it is_ used for other things in regex. – zdim Mar 14 '17 at 21:13
  • 2
    There's a module, [Apache::Admin::Config](https://metacpan.org/pod/Apache::Admin::Config), for reading and editing Apache config files. – Schwern Mar 14 '17 at 21:31
  • @l0r3nz0: If you have a solution then you must write it up as an answer and accept it. However your command line will work only with very specific instances of `httpd.conf` that are semantically identical. You should consider using the module that [**Schwern** recommends](http://stackoverflow.com/a/42797213/622310), otherwise your Makefile could mysteriously stop working one day when someone changes the Apache configuration. – Borodin Mar 14 '17 at 22:13
  • @l0r3nz0: Please change your handle to something that can be easily typed. On Stack Overflow it is essential to tag comments correctly so that the target is notified, and it is very awkward to distinguish between `l` and `I`, `0` and `O` in names like yours. In any case, I thought *1337 speak* belonged to teenage *n00b haxxors* last decade? – Borodin Mar 14 '17 at 22:20

1 Answers1

3

In general, parsing and modifying anything nested with regexes rapidly gets complicated an error prone. If you can, use a full parser.

Fortunately there is one for reading and modifying Apache config files, Apache::Admin::Config. It's a little weird at first, so here's an example.

#!/usr/bin/env perl

use strict;
use warnings;
use v5.10;

use Apache::Admin::Config;

# Load and parse the config file.
my $config = Apache::Admin::Config->new(shift)
    or die $Apache::Admin::Config::ERROR;

# Find the <Directory "/var/www/html"> section
# NOTE: This is a literal match, /var/www/html is different from "/var/www/html".
my $section = $config->section(
    "Directory",
    -value => q["/var/www/html"]
);

# Find the AllowOverride directive inside that section.
my $directive = $section->directive("AllowOverride");

# Change it to All.
$directive->set_value("All");

# Save your changes.
$config->save;

You're drilling into the structure one level at a time. First finding the section, then the directive inside that.

You can do this in loops. For example, finding all Directory sections...

for my $section ($config->section("Directory")) {
    ...
}
Schwern
  • 153,029
  • 25
  • 195
  • 336