使用%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
答案 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
基于替代的解决方案所做的假设:
href
和=
之间没有换行符。=
与值之间没有换行符。href
属性的值没有换行符。/href\s*=/
(包括CDATA部分)。/href\s*=/
。href
结尾。'
中没有单引号(href="..."
)。"
中没有双引号(href='...'
)。href=
,其中包含不带引号的值。href
属性中的空格未使用字符实体进行编码(例如 
)。
(SLePort做出类似的假设,即使他们没有记录它们。他们还认为href
属性不包含>
。)
答案 1 :(得分:1)
xml解析器更适合该作业(例如XMLStarlet,xmllint,...),但如果您的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
以下参数:
s/href\s*=\s*[][^'
,由单引号字符串's/href\s*=\s*['
和双引号字符串"][^'"
]*Kh
,未加引号,因为\K
和\h
不是shell中的特殊字符,所以它只将它们分别视为K
和h
然后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;'