我有这个测试文件。
[root@localhost ~]# cat f.txt "a aa" MM "bbb b" MM MM MM"b b " [root@localhost ~]#
我想替换引号中的所有空格字符,注意,仅在引号中。不应触及引号中的所有字符。也就是说,我想要的是类似的东西:
"a_aa" MM "bbb__b" MM MM MM"b_b_"
可以使用 sed ?
来实现谢谢,
答案 0 :(得分:8)
这是一个完全不重要的问题。
这可以用引号替换引号内的第一个空格:
$ sed 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa" MM "bbb_ b"
MM MM
MM"b_b "
$
对于此示例,如果任何引号内部的空格不超过两个,则很容易重复该命令,但结果不正确:
$ sed -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' \
> -e 's/\("[^ "]*\) \([^"]*"\)/\1_\2/g' f.txt
"a_aa"_ MM "bbb_ b"
MM MM
MM"b_b_"
$
如果您的sed
版本支持“扩展正则表达式”,那么这适用于示例数据:
$ sed -E \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> -e 's/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/' \
> f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
你必须为双引号内的每个空格重复那个可怕的正则表达式 - 因此对于第一行数据来说是三次。
正则表达式可以解释为:
由于启动锚点,每个空格必须重复一次......但是sed
有一个循环结构,所以我们可以用:
$ sed -E -e ':redo
> s/^(([^"]*("[^ "]*")?)*)("[^ "]*) ([^"]*")/\1\4_\5/
> t redo' f.txt
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
$
:redo
定义标签; s///
命令与以前一样;如果自上次读取行或跳转到标签以来已完成任何替换,t redo
命令将跳转到标签。
鉴于评论中的讨论,有几点值得一提:
-E
选项适用于MacOS X上的sed
(已测试10.7.2)。 GNU版sed
的相应选项是-r
(或--regex-extended
)。 -E
选项与grep -E
(也使用扩展正则表达式)一致。 “经典Unix系统”不支持sed
的ERE(Solaris 10,AIX 6,HP-UX 11)。
您可以用?
替换我使用的*
(这是强制使用ERE而不是BRE的唯一字符),然后处理括号(在BRE中需要在它们前面使用反斜杠以使它们成为捕获括号),离开脚本:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)*\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
这会在同一输入上产生相同的输出 - 我在输入中尝试了一些稍微复杂的模式:
"a aa" MM "bbb b"
MM MM
MM"b b "
"c c""d d""e e" X " f "" g "
"C C" "D D" "E E" x " F " " G "
这给出了输出:
"a_aa" MM "bbb__b"
MM MM
MM"b_b_"
"c_c""d_d""e__e" X "_f_""_g_"
"C_C" "D_D" "E__E" x "_F_" "_G_"
即使使用BRE表示法,sed
也支持\{0,1\}
表示法指定前一个RE术语的0或1次出现,因此?
版本可以转换为BRE使用:
sed -e ':redo
s/^\(\([^"]*\("[^ "]*"\)\{0,1\}\)*\)\("[^ "]*\) \([^"]*"\)/\1\4_\5/g
t redo' f.txt
这产生与其他替代品相同的输出。
答案 1 :(得分:0)
XSLT 2.0中一个不寻常的答案:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output method="text"></xsl:output>
<xsl:template name="init">
<xsl:for-each select="tokenize(unparsed-text('f.txt'),' ')">
<xsl:for-each select="tokenize(.,'"')">
<xsl:value-of select="if (position() mod 2 = 0)
then concat('"',translate(.,' ','_'),'"') else ."></xsl:value-of>
</xsl:for-each>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
要测试是否,只需在sourceforge上获取saxon.jar并使用以下命令行:
java -jar saxon9.jar -it:init regexp.xsl
xslt文件包含对f.txt的引用,文本文件必须与xslt文件位于同一目录中。通过为样式表提供参数可以很容易地改变它。
一次通过。
答案 2 :(得分:0)
如果引用的文字全部在不同的行上,这将非常容易。因此,一种方法是拆分文本,这样你就可以进行简单的转换,然后重建线条。
分割文本很简单,但我们需要区分
的换行符为此,我们可以用一个表示它所属的类的符号结束每一行。我只使用1和2,直接对应上面的内容。在sed中,我们有:
sed -e 's/$/1/' -e 's/"[^"]*"/2\n&2\n/g'
这会产生:
2
"a aa"2
MM 2
"bbb b"2
1
MM MM1
MM2
"b b "2
1
这很容易转换,只需使用
sed -e '/".*"/ s/ /_/g'
给
2
"a_aa"2
MM 2
"bbb__b"2
1
MM MM1
MM2
"b_b_"2
1
最后,我们需要将它重新组合在一起。这在sed中实际上非常可怕,但使用保持空间是可行的:
sed -e '/1$/ {s/1$//;H;s/.*//;x;s/\n//g}' -e '/2$/ {s/2$//;H;d}'
(这会更加清晰,例如,awk。)
将这三个步骤连接起来,你就完成了。
答案 3 :(得分:0)
这些可能适合您:
sed 's/^/\n/;:a;s/\(\n[^"]*"[^ "]*\) \([^"]*"\)\n*/\1_\2\n/;ta;s/\n//;ta;s/\n//' file
说明:
将\n
添加到行的开头,这将用于沿着替换进行碰撞。将替换为
_
内的"
,并在\n
处准备好进行下一轮替换。替换了所有
\n
后,删除\n
并重复。发生所有替换后,删除sed -r ':a;s/"/\n/;s/"/\n/;:b;s/(\n[^\n ]*) ([^\n]*\n)/\1_\2/g;tb;s/\n/%%%/g;ta;s/%%%/"/g' file
分隔符。
或者这个:
""
说明:
将第一组\n
替换为_
。用\n
替换换行符之间的第一个空格,重复。将%%%
替换为唯一分隔符(%%%
),从头开始重复。最后将所有"
替换为sed 's/"[^"]*"/\n&\n/g;$!s/$/@@@/' file |
sed '/"/y/ /_/;1{h;d};H;${x;s/\n//g;s/@@@/\n/g;p};d'
来整理。
第三种方式:
"..."
说明:
使用换行符(\n
)覆盖所有引用的表达式(@@@
)。在除最后一行之外的所有行上插入行尾分隔符sed
。将结果传递给第二个命令。将所有
_
的{{1}}翻译为"
,其中包含\n
的行。将每一行存储在保留空间(HS)中。在文件末尾,切换到HS并删除所有\n
并用sed 's/\("[^"]*"\)/$(tr '"' ' '_'"'<<<'"'"'\1'"'"')/g;s/^/echo /' file | sh
替换行尾分隔符
最后:
sed 's/\("[^"]*"\)/$(tr '"' ' '_'"'<<<'"'"'\1'"'"')/g;s/^/echo /e' file
或GNU sed:
{{1}}
留给读者解决。