2016-06-14 93 views
5

如何使用bash的sed的命令更改此字符串:如何使用sed替换多行字符串?

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

到下面的字符串? (只改变串3号线)

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

注1:我不只是要定位的字符串“的AllowOverride无”,因为还有其他的文件中出现不应该被改变。我需要针对整个字符串开始<Directory /var/www>

注2:我也需要覆盖该文件。所以,在你的答案中考虑到这一点。以防万一,为GNU /非GNU版本的sed提供不同的版本。

+1

如果有一个更通用的[mcve]来查看什么是常量,哪些不是:是否必须在'/ var/www'上?另外,使用'sed'作为多行似乎不是最干净的方式:'awk'可以更好地处理这种方式。 – fedorqui

回答

0

我知道这是不是你所说的,也许它的价值不是用SED问?

python解决方案如何?它将作为第一个参数传递给目录的目录移动到,正好代替<Directory元素,而您只写None更改为All并将更改写回文件。它还可以在保留原始缩进的同时使用不同的缩进级别。适用于python2和python3。

毕竟我假设如果你有sed你可能也有python。

#!/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) 
7

由于图案包含斜线,因此请使用\%(对于任何字符%)来标记搜索图案。然后使用:

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

搜索模式内\%…%限制搜索到的匹配图案之间的线,并且{ s/…/…/; }查找范围内的所期望的图案,并且使适当更换。

如果您不想将其限制为单个目录部分,而是限制为所有目录部分,请适当调整启动模式。例如,这将匹配任何<Directory>部分:

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

你可以使它更具有选择性根据您的要求。

+0

我不认为OP旨在限制替换为一个** **部分(但是所有** <目录...> **部分)。除此之外,我认为这个解决方案仍然会取代(第一个)** **和** **标记的最后一次出现之间的所有** ** AllowOverride None **。 – Leon

+0

@Leon:如果目录名称无关紧要,请将其从搜索范围中的第一个模式中删除。否:它将单独处理每个“'到''范围;它不会使用第一个“'作为单个范围。 –

+0

是的,你是对的。我在我的初始版本中有一个拼写错误,试图这样做! – Leon

3

简单的版本,依靠的AllowOverride线两线内的后<目录...>和使用GNU sed的延伸,是这样的:

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

UPDATE:这里是不依赖于任何GNU扩展的版本(我先试了一下,但是发了一个错字,很惊讶它没有用,这就是为什么先发布其他版本的原因):

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

利用GNU桑达:

sed -zie 's!\(<Directory /var/www/>[^<]*AllowOverride\) None!\1 All!' ex1.txt 
  • 选项-z是空隔开的记录:所有的文件是一个记录, 所以只是做一个简单的替换。
  • [^<]*(多行)正则表达式尊重目录边界,并允许灵活的格式和顺序。
1

你的问题是口头禅的好例子,不要用sed。真的,你不应该使用任何正则表达式引擎来实现上下文无关的语言,比如XML。但你可以接近,也许够接近,awk

#! /usr/bin/awk -f 

/<Directory \/var\/www\/>/ { 
    line = NR 
} 

/ AllowOverride None/ && line + 2 == NR { 
    gsub(/None/, "All") 
} 

{ print } 

这样,你没有任何花哨,非标准正则表达式来阅读,你的代码表示,正是它的意思是:如果你发现了“目录”行之后有“AllowOverride” 2号线,取代它。上面的正则表达式非常简单(和Posix兼容),并且可以与任何版本的awk一起工作。

0

您的回答已经由user提供,只是check here

一些参考

在sed的最简单的调用,它在模式空间,即一行文本。来自输入的1行\ n分隔文本。模式空间中的单行没有\ n ...这就是为什么你的正则表达式没有找到任何东西。

您可以在模式空间中读取多行,并且操作起来令人惊讶,但使用超过正常的努力.. Sed有一组允许这种类型的命令的命令...这是一个指向sed的命令摘要。这是我找到的最好的一个,让我滚动。

但是,一旦开始使用sed的微指令,忘记了“单行”的想法。将它像结构化程序一样布置,直到你感觉到它为止是非常有用的......它非常简单,同样很不寻常。您可以将其视为文本编辑的“汇编语言”。简介:使用sed来做简单的事情,也许更多一些,但是总的来说,当它超出单行使用时,大多数人更喜欢别的东西... 我会让别人建议别的东西..我真的不知道什么是最好的选择将是(我会用sed,但那是因为我不知道perl的不够好。)

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 

这是同一个脚本,凝结成显然难以阅读和处理,但有些人会把这种可疑的称为单线式

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

这是我的命令“作弊表”

: # 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