0

I'm trying to script a change to my apache config file (httpd.conf). I'm trying to match the following string:

#
# DirectoryIndex: sets the file that Apache will serve if a directory

And PREPEND the following text:

#
# Allow server status reports generated by mod_status,
# with the URL of http://servername/server-status
# Change the ".example.com" to match your domain to enable.
#
<Location /server-status>
    SetHandler server-status
    Order deny,allow
    Deny from all
    Allow from localhost ip6-localhost 127.0.0.1 192.168.0.0/255.255.255.0
</Location>

My understanding is that sed doesn't support multiple line matches, awk seems difficult to do multiple line matches. I'm trying to get perl to work with perl -0777 -pi -e but I can't seem to figure out a regular expression that matches the original pattern.

I'd prefer to do this as a one liner - not a script because i want it to be portable (i.e. copy and paste as needed).

Any perl regex experts out there that can help me devise a solution?

Many thanks in advance Brad

EDIT

The following works:

 sed -i -e ':begin;$!N;s/#\n# DirectoryIndex/#\n# Allow server status reports generated by mod_status,\n# with the URL of http:\/\/servername\/server-status\n# Change the ".example.com" to match your domain to enable.\n#\n<Location \/server-status>\n\tSetHandler server-status\n\tOrder deny,allow\n\tDeny from all\n\tAllow from localhost ip6-localhost 192\.168\.0\.0\/255\.255\.255\.0\n<\/Location>\n\n#\n\#DirectoryIndex/;tbegin;P;‌​D' /etc/httpd/conf/httpd.conf 

But there's no space between the # and DirectoryIndex.

However if I try to change it to:

sed -i -e ':begin;$!N;s/#\n# DirectoryIndex/#\n# Allow server status reports generated by mod_status,\n# with the URL of http:\/\/servername\/server-status\n# Change the ".example.com" to match your domain to enable.\n#\n<Location \/server-status>\n\tSetHandler server-status\n\tOrder deny,allow\n\tDeny from all\n\tAllow from localhost ip6-localhost 192\.168\.0\.0\/255\.255\.255\.0\n<\/Location>\n\n#\n\# DirectoryIndex/;tbegin;P;‌​D' /etc/httpd/conf/httpd.conf 

The sed command hangs and never finishes. I can't seem to figure out why?

The only difference is a space between # and DirectoryIndex.

  • It look like a Stack over flow question ? – Froggiz Nov 25 '15 at 17:36
  • You can use replace #\n# DirectoryIndex: sets the file that Apache will serve if a directory by this + new text, you don't need a reg exp for that | or you can get the line of this text and append after what you want. – Froggiz Nov 25 '15 at 17:37
  • Thanks i've flagged it and proposed it be migrated to stackoverflow. I wasn't sure weather to use stackoverflow, serverfault, or linux/unix site. – Brad Nov 25 '15 at 17:52
  • It's perfectly fine here. This is a sysadmin task. – Sven Nov 25 '15 at 17:54
  • @Froggiz - sed won't do multiline matches though. And trying to do what you propose with perl is failing: – Brad Nov 25 '15 at 18:10
2

With awk, how about:

  • store every line in a variable for "the previous line"
  • if the current line matches the second one you're looking for (DirectoryIndex), check the variable with the previous line
  • If they both match
    • print the bumpf
    • print the 'current line'
    • print the swallowed '#'
  • Else
    • print the current line
  • update the 'previous line' variable with the current line.

This should work for you because you don't strictly need to PREPEND the text - since both the text you're looking for and the text you're inserting start with # you can leave the original #, insert your text minus the first # line in the middle, then print the original second line, then print another # to be the one you didn't prepend.

You'll have to fill in the full text, but there's enough here to convince me it can work ;)

gawk "{if (a==\"#\" && /^# DirectoryIndex/) {print \"# Allow Server\n#With the URL\n#\"; print $0} else {print $0}} {a=$0}" httpd.conf > ??

(My double quote escapes are for Windows' command prompt. Adjust as necessary).

Edited quoting for bash:

gawk '{if (a=="#" && /^# DirectoryIndex/) {print "# Allow Server\n# With the URL\n#"; print $0} else {print $0}} {a=$0}' httpd.conf
  • This just outputs a string of zeros for me: 0 0 0 0 0 0 – Brad Nov 25 '15 at 21:10
  • That's part of the "adjust the quoting for your shell" bit.... I've edited my answer with a different quoting, and it seems to work on bash like that. – TessellatingHeckler Nov 25 '15 at 21:18
  • I have this working sorta. When I run the gawk statement it empties my config file. – Brad Nov 25 '15 at 22:12
  • gawk '{if (a=="#" && /^# DirectoryIndex/) {print "# Allow server status reports generated by mod_status,\n# with the URL of servername/server-status\n# Change the \".example.com\" to match your domain to enable.\n#\n<Location /server-status>\n\tSetHandler server-status\n\tOrder deny,allow\n\tDeny from all\n\tAllow from localhost ip6-localhost 127.0.0.1 192.168.0.0/255.255.255.0\n</Location>\n\n#"; print $0} else {print $0}} {a=$0}' /etc/httpd/conf/httpd.conf > /etc/httpd/conf/httpd.conf – Brad Nov 25 '15 at 22:13
  • Maybe you can't read and write the same file at once - one of these stackoverflow.com/q/16529716/478656 answers could help? – TessellatingHeckler Nov 25 '15 at 22:17
2

You should try

perl -0777 -i.original -pe 's/#\n# DirectoryIndex: sets the file that Apache will serve if a directory/#\n# DirectoryIndex: sets the file that Apache will serve if a directory\n#\n# Allow server status reports generated by mod_status,\n# with the URL of http:\/\/servername\/server-status\n# Change the ".example.com" to match your domain to enable.\n#\n<Location \/server-status>\n    SetHandler server-status\n    Order deny,allow\n    Deny from all\n    Allow from localhost ip6-localhost 127.0.0.1 192.168.0.0\/255.255.255.0\n<\/Location>/igs' /etc/httpd/conf/httpd.conf

Don't forget to escape your string replace (\/ for /) when you are using / delimiter in regexp tool

If you are running it through shell, you can do a script like that

#Set serach delimiter
search='#\n# DirectoryIndex: sets the file that Apache will serve if a directory'

#Set replace string from file
replace=$search"\n"$(cat newConfFile)

#Escape "/" char
replace=${replace//\//\\\/ }

#Launch the script
perl -0777 -i.original -pe 's/${search}/${replace}/igs' /etc/httpd/conf/httpd.conf

With newConfFile containt the virtual host configuration you want to add

This is a Bash Function to test if a function exist

#return 0 if command exist else return 1
canExec()
{
type "$1" &> /dev/null ;
}

for example canExec sed test if sed command exist on your system

  • This part is actually a block of text which I truncated: # # DirectoryIndex: sets the file that Apache will serve if a directory So really its: # # DirectoryIndex: sets the file that Apache will serve if a directory # More Stuff # And More So what the above does is sandwich it right in the middle of the statement: # # DirectoryIndex: sets the file that Apache will serve if a directory {Puts contents here} # More Stuff # And More – Brad Nov 25 '15 at 20:59
  • How you get the default string where to put your data after, can it be set as variable or even external file ? how you will launch your command, in a program ? – Froggiz Nov 25 '15 at 21:12
  • I'm looking to create a one-liner (mind you a very long one liner) which can be copied and pasted (as needed) into a bash prompt. Assuming the user has sed/awk/perl already installed. – Brad Nov 25 '15 at 21:14
  • I'm indifferent as to weather it uses sed or awk or perl - i have it ALMOST working in sed (per the comments in the original post) but the spacing isn't quite right and when I try and correct it sed hangs. – Brad Nov 25 '15 at 21:15
  • sed & hawk are almost all the time on any linux system, but perl need a package for it. If you need you can test if command exist in bash i addeded a func in my post – Froggiz Nov 25 '15 at 21:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.