命令行中的正则表达式用法 - 用href中的%20替换空格

时间:2017-01-10 19:36:46

标签: html regex bash perl sed

使用%20查找/替换空格

我必须使用*.html替换href="something something .pdf"内的%20个文件中的所有空格。 我找到了该任务的正则表达式:

find    : href\s*=\s*['"][^'" ]*\K\h|(?!^)\G[^'" ]*\K\h
replace : %20

该正则表达式适用于Notepad ++或Geany等文本编辑器。 我想从Linux命令行使用sed或perl的正则表达式。 解决方案(1):

    cat test002.html | perl -ne 's/href\s*=\s*['\''"][^'\''" ]*\K\h|(?!^)\G[^'\''" ]*\K\h/%20/g; print;' > Work_OK01.html

解决方案(2):

    cat test002.html | perl -ne 's/href\s*=\s*[\x27"][^\x27" ]*\K\h|(?!^)\G[^\x27" ]*\K\h/%20/g; print;' > Work_OK02.html

3 个答案:

答案 0 :(得分:3)

问题是你没有正确地逃避程序中的单引号。

如果您的计划是

...[^'"]...

shell文字可能是

'...[^'\''"]...'

'...[^'"'"'"]...'

'...[^\x27"]...'    # Avoids using a single quote to avoid escaping it.

所以,你要去

perl -ne 's/href\s*=\s*['\''"][^'\''" ]*\K\h|(?!^)\G[^'\''" ]*\K\h/%20/g; print;'

不要尝试一次做所有事情。以下是一些更清晰(即更易读)的解决方案:

perl -pe's{href\s*=\s*['\''"]\K([^'\''"]*)}{ $1 =~ s/ /%20/rg }eg'                # 5.14+

perl -pe's{href\s*=\s*['\''"]\K([^'\''"]*)}{ (my $s = $1) =~ s/ /%20/g; $s }eg'

请注意,-p-n相同,不同之处在于它会导致每行执行print

上述解决方案对可能遇到的文件进行了大量假设 [1] 。如果您使用正确的解析器,所有这些假设都会消失。

如果你有HTML文件:

perl -MXML::LibXML -e'
   my $doc = XML::LibXML->new->parse_file($ARGV[0]);
   $_->setValue( $_->getValue() =~ s/ /%20/gr )
      for $doc->findnodes(q{//@href});
   binmode(STDOUT);
   print($doc->toStringHTML());
' in_file.html >out_file.html

如果你有XML(包括XHTML)文件:

perl -MXML::LibXML -e'
   my $doc = XML::LibXML->new->parse_file($ARGV[0]);
   $_->setValue( $_->getValue() =~ s/ /%20/gr )
      for $doc->findnodes(q{//@href});
   binmode(STDOUT);
   $doc->toFH(\*STDOUT);
' in_file.html >out_file.html
  1. 基于替代的解决方案所做的假设:

    • 文件使用基于ASCII的编码(例如UTF-8,iso-latin-1,但不使用UTF-16le)。
    • href=之间没有换行符。
    • =与值之间没有换行符。
    • href属性的值没有换行符。
    • 在文本中没有匹配/href\s*=/(包括CDATA部分)。
    • 评论中没有匹配/href\s*=/
    • 没有其他属性的名称以href结尾。
    • '中没有单引号(href="...")。
    • "中没有双引号(href='...')。
    • 没有href=,其中包含不带引号的值。
    • href属性中的空格未使用字符实体进行编码(例如 )。
    • 也许更多?

    (SLePort做出类似的假设,即使他们没有记录它们。他们还认为href属性不包含>。)

答案 1 :(得分:1)

xml解析器更适合该作业(例如XMLStarletxmllint,...),但如果您的a标记中没有换行符,以下sed应该工作。

使用t命令和反向引用,它会遍历并替换"代码中最后a的所有空格:

$ sed ':a;s/\(<a [^>]*href=[^"]*"[^ ]*\) \([^"]*">\)/\1%20\2/;ta' <<< '<a href="http://url with spaces">'
<a href="http://url%20with%20spaces">

答案 2 :(得分:0)

你似乎忽略了逃避传递给Perl的字符串中的引号。所以Bash看到你给perl以下参数:

  1. s/href\s*=\s*[][^',由单引号字符串's/href\s*=\s*['和双引号字符串"][^'"
  2. 的串联产生
  3. ]*Kh,未加引号,因为\K\h不是shell中的特殊字符,所以它只将它们分别视为Kh
  4. 然后Bash看到一个管道符|,然后是一个子shell调用(?!^),其中!^被调用的最后一个命令的第一个参数替换。 (请参阅Bash man page中的“历史记录扩展&gt; Word指示符”。)例如,如果您的上一个命令是echo myface,那么(?!^)将查找名为?myface的命令,在子shell中运行它。

    最后,Bash进入序列\G[^'" ]*\K\h/%20/g; print;',该序列被解释为G(来自\G),[^和单引号字符串的串联" ]*\K\h/%20/g; print;。 Bash不知道如何处理G[^" ]*\K\h/%20/g; print;,因为它刚刚完成解析子shell调用并期望在获得另一个任意字符串之前看到分号,换行符或逻辑运算符(等等)。

    解决方案:正确引用您提供给perl的表达式。您需要使用单引号和双引号的组合将其拉出来,例如

    perl -ne 's/href\s*=\s*['"'\"][^'\" ]*"'\K\h|(?!^)\G[^'"'\" ]*"'\K\h/%20/g; print;'