How to use sed to replace a multi-line string?

How can I use the bash sed command to change this line:

<Directory /var/www/> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> 

to the next line? (only changing the third line of the line)

 <Directory /var/www/> Options Indexes FollowSymLinks AllowOverride All Require all granted </Directory> 

NOTE 1: I don’t just want to target the “AllowOverride None” line , because there are other entries in the file that should not be changed. I need to configure a whole line starting with <Directory /var/www>

NOTE 2: I also need to overwrite the file. Therefore, consider this in your answer. And just in case, provide different versions for the GNU / non-GNU versions of sed.

+5
source share
6 answers

I understand that this is not what you asked, maybe you should not use sed?

What about python solution? It moves the directory passed as the first parameter to the script and replaces the element exactly with the <Directory , as you wrote it, but only changing None to All and writing the changes back to the file. It will also work with different levels of indentation, while maintaining the original indentation. Works on both python2 and python3.

In the end, I assume you have sed, you have python too.

 #!/usr/bin/env python import re r = re.compile(r'(<Directory /var/www/>\s+Options Indexes FollowSymLinks\s+AllowOverride )None(\s+Require all granted\s+</Directory>)', re.MULTILINE) for root, dirs, files in os.walk(sys.argv[1]): for file_name in files: if file_name.endswith('.conf'): file_path = os.path.join(root, file_name) with open(file_path) as fp: data = r.sub(r'\1All\2', fp.read()) with open(file_path, 'w+') as fp: fp.write(data) 
0
source

Since patterns contain slashes, use \% (for any % character) to mark search patterns. Then use:

 sed -e '\%^<Directory /var/www/>%,\%^</Directory>% s/AllowOverride None/AllowOverride All/' 

Search patterns within \%…% limit the search for strings between the corresponding patterns, and { s/…/…/; } { s/…/…/; } searches for the desired pattern within the range and makes the appropriate replacement.

If you do not want to limit it to one directory section, but for all directory sections, configure the launch template accordingly. For example, this will match any <Directory> section:

 sed -e '\%^<Directory [^>]*>%,\%^</Directory>% s/AllowOverride None/AllowOverride All/' 

You can make it more selective depending on your requirements.

+7
source

A simple version, relying on the AllowOverride line that appears in two lines after <Directory ...> and using the GNU sed extension, is as follows:

 sed '/^<Directory/,+2 { s/AllowOverride None/AllowOverride All/g; }' 

UPDATE Here is a version that is independent of any GNU extension (I tried it first, but made a typo and was surprised that it did not work, so I sent another version first):

 sed '/^<Directory/,/^<\/Directory>/ { s/AllowOverride None/AllowOverride All/; }' 
+3
source

Using Gnu Sed:

  sed -zie 's!\(<Directory /var/www/>[^<]*AllowOverride\) None!\1 All!' ex1.txt 
  • The -z option is for zero-split records: all files are one record, so just make a simple replacement.
  • [^<]* (multi-line) regular expression respects directory boundaries and allows for flexible format and order.
+1
source

Your question is a good illustration of the mantra, do not use sed. Indeed, you should not use any regex mechanism for a context-free language such as XML. But you can get close, perhaps close enough, with awk .

 #! /usr/bin/awk -f /<Directory \/var\/www\/>/ { line = NR } / AllowOverride None/ && line + 2 == NR { gsub( /None/, "All" ) } { print } 

This way, you don’t need to read any unusual, non-standard regular expression, and your code says exactly what it means: if you find “AllowOverride” 2 lines after the line “Directory”, replace it. The above regexes are very simple (and consistent with Posix) and should work with any version of awk.

+1
source

Your answer has already been set using user here .

Some link

In the simplest sed call, it has one line of text in the template space, i.e. 1 line of \ n separator text with input. The only line in the pattern space does not have \ n ... That's why your regular expression cannot find anything.

You can read a few lines in the template space and manage things surprisingly well, but with more than the usual effort. Sed has a set of commands that allow this type of thing ... Here is a link to the Summary command for sed. This is the best I found and made me ride.

However, forget about the "one liner" idea when you start using sed sed commands. It is useful to lay it out as a structured program until you feel it ... It is surprisingly simple and no less unusual. You might think of it as the "assembly language" of text editing.

Summary: use sed for simple things and maybe a little more, but overall, when it goes beyond working with a single line, most people prefer something else ... I will let someone else suggest something else. I'm really not sure what the best choice would be (I would use sed, but that is because I don't know perl well enough.)

 sed '/^a test$/{ $!{ N # append the next line when not on the last line s/^a test\nPlease do not$/not a test\nBe/ # now test for a successful substitution, otherwise #+ unpaired "a test" lines would be mis-handled t sub-yes # branch_on_substitute (goto label :sub-yes) :sub-not # a label (not essential; here to self document) # if no substituion, print only the first line P # pattern_first_line_print D # pattern_ltrunc(line+nl)_top/cycle :sub-yes # a label (the goto target of the 't' branch) # fall through to final auto-pattern_print (2 lines) } }' alpha.txt 

Here it is the same script, compressed into something that is obviously harder to read and work, but some of them doubt the one-line

 sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt 

Here is my cheat-sheet team

 : # label = # line_number a # append_text_to_stdout_after_flush b # branch_unconditional c # range_change d # pattern_delete_top/cycle D # pattern_ltrunc(line+nl)_top/cycle g # pattern=hold G # pattern+=nl+hold h # hold=pattern H # hold+=nl+pattern i # insert_text_to_stdout_now l # pattern_list n # pattern_flush=nextline_continue N # pattern+=nl+nextline p # pattern_print P # pattern_first_line_print q # flush_quit r # append_file_to_stdout_after_flush s # substitute t # branch_on_substitute w # append_pattern_to_file_now x # swap_pattern_and_hold y # transform_chars 
0
source

All Articles