我正在寻找清理(就地编辑)如下所示的文件
<id>474488</id>
<name>Shawn</name>
<nr>143385</nr>
<id>474490</id>
<name>Bob</name>
<nr>.27. 43-88</nr> # this is the line of interest
<id>474568</id>
<name>Jim</name>
<nr>
</nr> # sometimes there will be no value and a closing tag on a newline, this can be ignored
....
期望的输出:
<id>474488</id>
<name>Shawn</name>
<nr>143385</nr>
<id>474490</id>
<name>Bob</name>
<nr>274388</nr> # note that nr data has been cleaned to digits only
<id>474568</id>
<name>Jim</name>
<nr>
</nr>
....
换句话说,我想删除所有非数字字符/从<nr> </nr>
标签中包含的数据中删除某些字符。
我的代码:
sed -Ee '/<nr>/ s/>(.*)</>\1</g' test1.txt
这是做什么的:
仅选择
<nr>
其中包含的行替换其中的标签和内容(通过捕获组 1 的内容 = 没有更改,因为我不知道如何处理捕获组 1 的内容)。
另外,理想情况下,我不想替换> <
,但环视(告诉 sed 在 sed 之后>
和之前启动<
)在 sed 中似乎是不可能的。
我需要添加什么(但不知道如何添加):
在插入之前过滤捕获组 1 的内容(删除.
和-
/或仅允许数字)。whitespace
我该怎么做呢?
我需要使用不同的工具吗?
答案1
这看起来像是 XML 的一个片段。添加一个封闭<root/>
元素,这样我们就有了下面的 XML,然后我们可以使用 XML 编辑工具,
xmlstarlet ed -u '//nr' -x 'translate(text(), "- .", "")' file.xml
<?xml version="1.0"?>
<root>
<id>474488</id>
<name>Shawn</name>
<nr>143385</nr>
<id>474490</id>
<name>Bob</name>
<nr>274388</nr>
<id>474568</id>
<name>Jim</name>
<nr>
</nr>
</root>
这里重要的部分是 XPath translate()
函数。它的操作与 UNIX/Linux 命令类似tr
,它将一个字符串中的字符替换为另一个字符串中的字符(第一个参数是要操作的值)。
我用它nr
作为操作的钩子。如果需要,元素路径可以更精确(在我的示例中/root/nr
也可以使用)。
很少有过滤工具能够真正就地处理文件。他们编写一个临时文件,然后用它来替换原始文件。在这种情况下,我们必须自己实现
xmlstarlet ... file.xml >file.xml.tmp && mv -f file.xml.tmp file.xml
答案2
如果这是像 XML 这样的适当的结构化语言,那么您确实应该使用专用的解析器(例如xmlstarlet
,考虑 )。也就是说,只要您的文件足够小以适合内存,并假设正如您在注释中所说,只有在字段为空的情况下,字段中才会有换行符,因此不需要替换,您实际上可以这样做:
$ sed '/<nr>/{s/[. -]*//g}' file
<id>474488</id>
<name>Shawn</name>
<nr>143385</nr>
<id>474490</id>
<name>Bob</name>
<nr>274388</nr>#thisisthelineofinterest
<id>474568</id>
<name>Jim</name>
<nr>
</nr>
....
对于更复杂的情况,如果我无法使用合适的解析器,我会使用 perl:
$ perl -nle '$k=1 if /<nr>/; if($k){s/[. -]//g}; $k=0 if /<\/nr>/; print' file
<id>474488</id>
<name>Shawn</name>
<nr>143385</nr>
<id>474490</id>
<name>Bob</name>
<nr>274388</nr>#thisisthelineofinterest
<id>474568</id>
<name>Jim</name>
<nr>
</nr>
....
但请注意,如果同一行上可以有多个标签,则上述操作将会失败,如下所示:
<nr>143385</nr><name>Shawn - Mary</name>
在这种情况下,-
也将从 的值中删除<name>
。这种边缘情况就是为什么解析器确实是您最好的选择。
对于perl
和sed
选项,您可以使用-i
就地编辑文件:
sed -i '/<nr>/{s/[. -]*//g}' file
perl -i -nle '$k=1 if /<nr>/; if($k){s/[. -]//g}; $k=0 if /<\/nr>/; print' file