linux 在一个文件夹中使用不同的文件类型编辑多个xml文件-使用预定的顺序

l3zydbqr  于 2023-04-20  发布在  Linux
关注(0)|答案(4)|浏览(133)

我这里有一个小情况,我希望一些Linux魔术师可以帮助我。我有一个文件夹/assets,其中有.jpg.xml文件-一吨。
原始xml结构:

<annotation>
    <folder></folder>
    <filename>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</filename>
    <path>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</path>
    <source>
        <database>roboflow.ai</database>
    </source>
    <size>
        <width>416</width>
        <height>416</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>cheese</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>53</xmin>
            <xmax>371</xmax>
            <ymin>224</ymin>
            <ymax>391</ymax>
        </bndbox>
    </object>
</annotation>

如果你看一下,你可以看到在<bndbox>标记中,我定义了以下参数:xmin, xmax, ymin and ymax-在该序列中。它是错误的,我想用以下序列更新所有我的.xml文件:xmin, ymin, xmax, ymax。因此文件将如下所示:

<annotation>
    <folder></folder>
    <filename>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</filename>
    <path>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</path>
    <source>
        <database>roboflow.ai</database>
    </source>
    <size>
        <width>416</width>
        <height>416</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>cheese</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>53</xmin>
            <ymin>224</ymin>
            <xmax>371</xmax>
            <ymax>391</ymax>
        </bndbox>
    </object>
</annotation>

有什么想法可以做到这一点吗?提前感谢!
编辑(我目前拥有的):所以我打开终端,导航到文件夹:cd desktop并调用此块。所有文件都位于desktop/cheese 1文件夹中:

for file in cheese1/*.xml; do
    xmlstarlet edit -L --delete "//bndbox/xmax" --insert "//bndbox/ymin" --to "//bndbox/xmin" \
        --insert "//bndbox/xmax" --to "//bndbox/ymax" \
        "$file"
done

什么都没有发生,所以不确定是语法错误,是逻辑错误还是两者兼而有之。

ddarikpa

ddarikpa1#

Sed解决方案,它假设所有输入数据的格式都与示例中相同,并且错误的序列总是按xmin > xmax > ymin > ymax排序:

sed '/<bndbox>/,/<\/bndbox>/{/<xmax>/{h;d};/<ymin>/G}' cheese1/*.xml

使用sed -i就地编辑您的文件,如果输出满足您的要求。
它的工作方式非常简单,在<bndbox>...</bndbox>行之间,我们保存<xmax>行并删除它,然后当我们遇到<ymin>行时,我们将之前保存的<xmax>行粘贴到<ymin>行之后。

zqry0prt

zqry0prt2#

假设:

  • 输入文件的格式始终如图所示(例如,感兴趣的最小/最大行位于单独的行上)
  • 除了给定的标记外,OP还对其他解决方案开放

一个awk的想法:

awk '
flag        {      if ($0 ~ "xmin") lines[1]=$0    # if flag==1 then
              else if ($0 ~ "ymin") lines[2]=$0    # test for indvidual
              else if ($0 ~ "xmax") lines[3]=$0    # strings and if found
              else if ($0 ~ "ymax") lines[4]=$0    # then save this line and ...
              next                                 # skip to next input line
            }
/<bndbox>/  { flag=1                               # set flag
              delete lines                         # clear array if there may be multiple blocks to process
            }
/<\/bndbox/ { for (i=1;i<=4;i++)                   # loop through lines[] array and ...
                  if (i in lines)                  # if popoulated then ...
                     print lines[i]                # print saved lines to stdout
              flag=0                               # clear flag
            }
1                                                  # print input line to stdout
' file.xml

这产生:

<annotation>
    <folder></folder>
    <filename>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</filename>
    <path>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</path>
    <source>
        <database>roboflow.ai</database>
    </source>
    <size>
        <width>416</width>
        <height>416</height>
        <depth>3</depth>
    </size>
    <segmented>0</segmented>
    <object>
        <name>cheese</name>
        <pose>Unspecified</pose>
        <truncated>0</truncated>
        <difficult>0</difficult>
        <bndbox>
            <xmin>53</xmin>
            <ymin>224</ymin>
            <xmax>371</xmax>
            <ymax>391</ymax>
        </bndbox>
    </object>
</annotation>

一旦输出被验证正确,有几个选项可用于更新输入文件:

  • 如果使用GNU awk,我们可以添加inplace选项,例如:awk -i inplace 'flag ... <see_rest_of_script_above> ...' file.xml否则...
  • 将输出定向到tmpfile,然后将它们mv tmpfile file.xml
lyr7nygr

lyr7nygr3#

通过xmlstarlet使用xslt转换:
XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()[not(self::xmax)]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="ymin">
    <xsl:copy-of select="."/>
    <xsl:copy-of select="../xmax"/>
  </xsl:template>

</xsl:stylesheet>

通过xmlstarlet在循环中使用bash globbing进行XSLT转换:

for f in *.xml ; do xmlstarlet tr t.xslt "$f" > updated_"$f" ; done

转换前的目录内容:

a.xml  b.xml t.xslt

转换后的目录:

a.xml  b.xml t.xslt updated_a.xml  updated_b.xml

更新的XML示例:

<?xml version="1.0"?>
<annotation>
  <folder/>
  <filename>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</filename>
  <path>Pro_jpeg.rf.77216510eeb475f923d5bb3bdb22ee11.jpg</path>
  <source>
    <database>roboflow.ai</database>
  </source>
  <size>
    <width>416</width>
    <height>416</height>
    <depth>3</depth>
  </size>
  <segmented>0</segmented>
  <object>
    <name>cheese</name>
    <pose>Unspecified</pose>
    <truncated>0</truncated>
    <difficult>0</difficult>
    <bndbox>
      <xmin>53</xmin>
      <ymin>224</ymin>
      <xmax>371</xmax>
      <ymax>391</ymax>
    </bndbox>
  </object>
</annotation>
fbcarpbf

fbcarpbf4#

使用xmlstarlet edit将一个XML元素移动到指定的位置,如下所示:
1.插入一个新的空节点作为目标,-i用于前面的兄弟节点,-a用于后面的兄弟节点
1.将源的内容复制到新节点
1.复制后删除源

# shellcheck shell=sh disable=SC2016
xmlstarlet edit --inplace \
  --var B 'annotation/object/bndbox' \
  --var N '$B/ymin' \
  -i '$B/xmax' -t 'elem' -n 'ymin' \
  -u '$prev' -x '$N/node() | $N/@*' \
  -d '$N' \
cheese1/*.xml
  • 为简洁起见,源节点由变量N引用
  • $B/xmax可替换为annotation/object/bndbox/xmax$N/preceding-sibling::*[1]或等效值
  • -x '$N/node() | $N/@*'在子轴和属性轴上创建源节点的深层副本(使用联合)(因为ymin实际上没有任何属性,所以可以剥离| $N/@*
  • -L(又名--inplace)更新所有已处理XML文件的时间戳,即使输出与输入相同

--var定义了一个命名变量,而$prev变量引用了由最新的-s-i-a选项创建的节点,这些选项都定义或重新定义了它(有关--var$prev的示例,请参见xmlstarlet.txt)。

相关问题