如何使用sed替换多行字符串?

时间:2016-06-14 02:13:19

标签: sed

如何使用bash 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 None'作为目标,因为文件中还有其他一些不应更改的事件。我需要定位以<Directory /var/www>

开头的整个字符串

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

6 个答案:

答案 0 :(得分:7)

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

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

\%…%内的搜索模式将搜索范围限制在匹配模式之间的行,{ s/…/…/; }在该范围内查找所需的模式并进行适当的替换。

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

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

根据您的要求,您可以使其更具选择性。

答案 1 :(得分:3)

简单版本依赖&lt; Directory ...&gt; 之后的两行内的 AllowOverride 行并使用GNU sed扩展名,这是:< / p>

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

更新:这是不依赖于任何GNU扩展的版本(我先尝试过,但是输入了一个错字,并且感到惊讶的是它不起作用,这就是为什么首先发布其他版本的原因):

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

答案 2 :(得分:1)

使用Gnu Sed:

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

答案 3 :(得分: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。

答案 4 :(得分: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

这是我的命令“cheat-sheet”

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

答案 5 :(得分: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)