查找文件中的行并清理标签中包含的数据

查找文件中的行并清理标签中包含的数据

我正在寻找清理(就地编辑)如下所示的文件

<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>。这种边缘情况就是为什么解析器确实是您最好的选择。

对于perlsed选项,您可以使用-i就地编辑文件:

sed -i '/<nr>/{s/[. -]*//g}' file
perl -i -nle '$k=1 if /<nr>/; if($k){s/[. -]//g}; $k=0 if /<\/nr>/; print' file

相关内容