使用xmlstarlet按特定顺序解析列表

使用xmlstarlet按特定顺序解析列表,xml,bash,sorting,xslt,xmlstarlet,Xml,Bash,Sorting,Xslt,Xmlstarlet,我正在尝试使用xmlstarlet sel列出需要从xml文件创建的磁盘分区,该文件在磁盘上以升序块位置列出这些分区(示例:) 此文件是通过转储必须复制的已安装系统生成的。然后,用户可以用“*”替换他想要适应新磁盘的分区大小 现在我正在做以下工作: local IFS=; DISK_DEV=sda DISKS_LAYOUT_FILE=/tmp/disk-layout-complex.xml cd /tmp wget https://raw.githubus

我正在尝试使用xmlstarlet sel列出需要从xml文件创建的磁盘分区,该文件在磁盘上以升序块位置列出这些分区(示例:) 此文件是通过转储必须复制的已安装系统生成的。然后,用户可以用“*”替换他想要适应新磁盘的分区大小

现在我正在做以下工作:

    local IFS=;
    DISK_DEV=sda
    DISKS_LAYOUT_FILE=/tmp/disk-layout-complex.xml
    cd /tmp
    wget https://raw.githubusercontent.com/finley/SystemImager/initrd-from-imageserver-and-dont-package-initrd/doc/examples/disk-layout-complex.xml
    xmlstarlet sel -t -m "config/disk[@dev=\"${DISK_DEV}\"]/part" -v "concat(@num,';',@size,';',@p_type,';',@id,';',@p_name,';',@flags,';',@lvm_group,';',@raid_dev)" -n ${DISKS_LAYOUT_FILE} | sed '/^\s*$/d' |\
    while read P_NUM P_SIZE P_TYPE P_ID P_NAME P_FLAGS P_LVM_GROUP P_RAID_DEV
    do
        # process partitions creation.
        echo "Creating partition $P_NUM of size $P_SIZE tpye $P_TYPE"
    done

上述xmlstarlet将生成以下输出,然后由while read循环处理:

    1;500;primary;;;boot;;
    3;4096;primary;;;;;;
    4;*;extended;;;;;;
    7;4096;logical;;;;;;
    5;*;logical;;;;;;
    6;2048;logical;;;;;;
    2;1024;primary;;;swap;;
在处理第3行(分区#4)后,磁盘上没有剩余空间,循环将处理第4行(分区#7),并在磁盘上没有剩余空间的情况下失败

问题在于可变大小分区(在文件中使用100%(“*”)。如果一个列在其他剩余的列之前(在上面的例子中是第4部分),那么创建它时会有完整的剩余空间,磁盘上没有空间来处理最后一个。因此,例如,不可能将主交换分区放在具有可变大小的/分区的磁盘的末尾

问:有没有一种聪明的方法可以使用xmlstarlet sel按以下顺序列出分区:

按照写入xml文件的相同顺序列出所有主分区和扩展分区,直到看到大小为“*”的分区

  • 记住这个可变大小的分区
  • 然后从末尾按相反顺序列出其他分区
  • 最后打印可变大小的分区
  • 对逻辑分区重复操作(如果有)
对于所有分区,添加一个字段,说明它是按顺序列出的还是按相反顺序列出的,这样我就可以知道是否必须相对于可用空间的开始还是相对于可用空间的结束创建分区。(变量分区将被标记为正常顺序,因为它们将从可用空间开始创建)

对于列出的示例(disk layout complex.xml),这将按以下顺序列出要创建的分区: (下面是xmlstarlet的输出,该输出随后将由类似于上述代码的while read循环处理,但前面还有一个读取参数OFFSET_CREATE,该参数将读取正常/反向值)

如果有一些分区要创建,而磁盘没有剩余空间,则处理上述xmlstarlet输出不会触发这种情况,因为创建的分区剩余空间为100%

我在一个特制的initrd中处理这个问题,所以我只能访问最常见的util,比如sed/grep/bash2/xmlstarlet/awk。没有perl,没有python,没有一般需要库的语言


我非常确信有一种解决方案可以完成大部分工作(如果不是全部的话),但我的技能远远不够,甚至无法评估它是否可以通过这种方式完成。我认为我可以在纯bash中实现这一点,但这将远远不够优雅。

在广泛搜索和学习了xslt的基础知识之后,似乎这正是我想要的

(此处提供要处理的xml文件的完整源:)

递归算法如下所示:

do_partition_template(index, type)
  if partition(index) is same type and if variable_partition is not yet seen:
    print "normal;line"
    do_partition_template(index+1,type)
  if partition(index) is same type and if variable partition has been seen:
    do_partition_template(index+1, type)
    print "reverse;line"
  if(partition(index) is same type and if  size(partition)="*":
    do_partition_template(index+1, type)
    print "normal;line"
  if partition is not the same type:
    do_partition_template(index+1, type)
  fi
call template 1st partition type='primary|extended'
call template 1st partition type='logical'
我不确定算法的递归形式,但需要按顺序创建的是: -从磁盘开始创建的主/扩展分区列表 -要从磁盘末尾创建的主/扩展分区列表 -具有size=“”的主/扩展分区 -从扩展分区开始创建的逻辑分区列表 -要从扩展分区末尾创建的逻辑分区列表 -大小为“”的逻辑分区

作为xsl的新手,解决这个问题对我来说非常复杂。 我使用了xmlstarlet sel-C-t-m“config/disk[@dev=\'”/dev/sda\“]/part“-v”concat(@num,“;”,@size,“;”,@p_type,“;”,@id,“;”,@p_name,“;”,@flags,“;”,@lvm_group,“,@raid_dev)”-n./disk-layout-complex.xml>do_part.xslt 首先是我的问题,但是对于我来说很难形式化一个模板,它将在两次扫描中命中所有分区(一次用于主/扩展分区,另一次用于逻辑分区)


在没有变量的情况下工作并不容易。

在广泛搜索和学习了xslt的基础知识之后,我似乎正在寻找它

(此处提供要处理的xml文件的完整源:)

递归算法如下所示:

do_partition_template(index, type)
  if partition(index) is same type and if variable_partition is not yet seen:
    print "normal;line"
    do_partition_template(index+1,type)
  if partition(index) is same type and if variable partition has been seen:
    do_partition_template(index+1, type)
    print "reverse;line"
  if(partition(index) is same type and if  size(partition)="*":
    do_partition_template(index+1, type)
    print "normal;line"
  if partition is not the same type:
    do_partition_template(index+1, type)
  fi
call template 1st partition type='primary|extended'
call template 1st partition type='logical'
我不确定算法的递归形式,但需要按顺序创建的是: -从磁盘开始创建的主/扩展分区列表 -要从磁盘末尾创建的主/扩展分区列表 -具有size=“”的主/扩展分区 -从扩展分区开始创建的逻辑分区列表 -要从扩展分区末尾创建的逻辑分区列表 -大小为“”的逻辑分区

作为xsl的新手,解决这个问题对我来说非常复杂。 我使用了xmlstarlet sel-C-t-m“config/disk[@dev=\'”/dev/sda\“]/part“-v”concat(@num,“;”,@size,“;”,@p_type,“;”,@id,“;”,@p_name,“;”,@flags,“;”,@lvm_group,“,@raid_dev)”-n./disk-layout-complex.xml>do_part.xslt 首先是我的问题,但是对于我来说很难形式化一个模板,它将在两次扫描中命中所有分区(一次用于主/扩展分区,另一次用于逻辑分区)


在没有变量的情况下工作并不容易。

最终答案如下

<!— Sample cut, relevant for this question —>
<config>
      <disk dev="/dev/sda" label_type="msdos" unit_of_measurement="MiB">
              <part num="1" size="500" p_type="primary" flags="boot" />
              <part num="3" size="4096" p_type="primary" />
              <part num="4" size="*" p_type="extended" />
              <part num="7" size="4096" p_type="logical" />
              <part num="5" size="*" p_type="logical" />
              <part num="6" size="2048" p_type="logical" />
              <part num="2" size="1024" p_type="primary" flags="swap" />
      </disk>
</config>
可通过以下方式运行sxl转换文件获得:

xmlstarlet tr do_part.xsl ./disk-layout-complex.xml
现在是代码:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Call me with:
     xmlstarlet tr do_part.xsl disk-layout.xml

     Output: List of partitions to create in order.
        Each line list the following values separated by semicolons:
        - disk device
        - creation reference
        - partition number
        - partition size
        - partition size unit
        - partition type
        - partition id
        - partition name
        - partition flags
        - lvm group it belongs to
        - raid device it belongs to

      Author: Olivier LAHAYE (c) 2019
      Licence: GPLv2
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/config/disk"> <!-- We are loking for disk informations only -->
    <!-- For each disk block -->
    <xsl:call-template name="PrintPartition"> <!-- Compute primary or extended partitions to create -->
      <xsl:with-param name="index"><xsl:value-of select="count(part)"/></xsl:with-param>
      <xsl:with-param name="reference">end</xsl:with-param>
      <xsl:with-param name="type">primary|extended</xsl:with-param>
    </xsl:call-template>
    <xsl:call-template name="PrintPartition"> <!-- then, compute logical partitions to create -->
      <xsl:with-param name="index"><xsl:value-of select="count(part)"/></xsl:with-param>
      <xsl:with-param name="reference">end</xsl:with-param>
      <xsl:with-param name="type">logical</xsl:with-param>
    </xsl:call-template>
  </xsl:template> <!-- We're done -->

  <!-- Main recursive template that will dump partitions to create for the matched disk -->
  <xsl:template name="PrintPartition">
    <xsl:param name="index"/> <!-- partition node number within disk item-->
    <xsl:param name="reference"/> <!-- beginning or end: should we create partition relative from beginning or from end of free space -->
    <xsl:param name="type"/> <!-- type of partitions -->
    <xsl:choose>
      <xsl:when test="$index=1">
        <xsl:if test="contains($type,part[position()=$index]/@p_type)">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
      </xsl:when>
      <xsl:when test="contains($type,part[position()=$index]/@p_type) and part[position()=$index]/@size!='*'">
        <xsl:if test="$reference='end'">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference"><xsl:value-of select="$reference"/></xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
        <xsl:if test="$reference='beginning'">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
      </xsl:when>
      <xsl:when test="contains($type,part[position()=$index]/@p_type) and part[position()=$index]/@size='*'">
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference">beginning</xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
        <xsl:value-of select="concat(@dev,';','beginning;',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference"><xsl:value-of select="$reference"/></xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

结束
初级|扩展
结束
符合逻辑的
开始
瞧,它工作得很好

当然有更好或更优雅的解决方案。(fe)
/dev/sda;end;2;1024;MiB;primary;;;swap;;
/dev/sda;beginning;1;500;MiB;primary;;;boot;;
/dev/sda;beginning;3;4096;MiB;primary;;;;;
/dev/sda;beginning;4;*;MiB;extended;;;;;
/dev/sda;end;6;2048;MiB;logical;;;;;
/dev/sda;beginning;7;4096;MiB;logical;;;;;
/dev/sda;beginning;5;*;MiB;logical;;;;;
xmlstarlet tr do_part.xsl ./disk-layout-complex.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- Call me with:
     xmlstarlet tr do_part.xsl disk-layout.xml

     Output: List of partitions to create in order.
        Each line list the following values separated by semicolons:
        - disk device
        - creation reference
        - partition number
        - partition size
        - partition size unit
        - partition type
        - partition id
        - partition name
        - partition flags
        - lvm group it belongs to
        - raid device it belongs to

      Author: Olivier LAHAYE (c) 2019
      Licence: GPLv2
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
  <xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
  <xsl:strip-space elements="*"/>
  <xsl:template match="/config/disk"> <!-- We are loking for disk informations only -->
    <!-- For each disk block -->
    <xsl:call-template name="PrintPartition"> <!-- Compute primary or extended partitions to create -->
      <xsl:with-param name="index"><xsl:value-of select="count(part)"/></xsl:with-param>
      <xsl:with-param name="reference">end</xsl:with-param>
      <xsl:with-param name="type">primary|extended</xsl:with-param>
    </xsl:call-template>
    <xsl:call-template name="PrintPartition"> <!-- then, compute logical partitions to create -->
      <xsl:with-param name="index"><xsl:value-of select="count(part)"/></xsl:with-param>
      <xsl:with-param name="reference">end</xsl:with-param>
      <xsl:with-param name="type">logical</xsl:with-param>
    </xsl:call-template>
  </xsl:template> <!-- We're done -->

  <!-- Main recursive template that will dump partitions to create for the matched disk -->
  <xsl:template name="PrintPartition">
    <xsl:param name="index"/> <!-- partition node number within disk item-->
    <xsl:param name="reference"/> <!-- beginning or end: should we create partition relative from beginning or from end of free space -->
    <xsl:param name="type"/> <!-- type of partitions -->
    <xsl:choose>
      <xsl:when test="$index=1">
        <xsl:if test="contains($type,part[position()=$index]/@p_type)">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
      </xsl:when>
      <xsl:when test="contains($type,part[position()=$index]/@p_type) and part[position()=$index]/@size!='*'">
        <xsl:if test="$reference='end'">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference"><xsl:value-of select="$reference"/></xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
        <xsl:if test="$reference='beginning'">
          <xsl:value-of select="concat(@dev,';',$reference,';',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
        </xsl:if>
      </xsl:when>
      <xsl:when test="contains($type,part[position()=$index]/@p_type) and part[position()=$index]/@size='*'">
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference">beginning</xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
        <xsl:value-of select="concat(@dev,';','beginning;',part[position()=$index]/@num,';',part[position()=$index]/@size,';',@unit_of_measurement,';',part[position()=$index]/@p_type,';',part[position()=$index]/@id,';',part[position()=$index]/@p_name,';',part[position()=$index]/@flags,';',part[position()=$index]/@lvm_group,';',part[position()=$index]/@raid_dev,'&#10;')"/> <!-- write partition information -->
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="PrintPartition">
          <xsl:with-param name="index"><xsl:value-of select="number($index)-1"/></xsl:with-param>
          <xsl:with-param name="reference"><xsl:value-of select="$reference"/></xsl:with-param>
          <xsl:with-param name="type"><xsl:value-of select="$type"/></xsl:with-param>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
1;500;primary;boot
3;4096;primary;
2;1024;primary;swap
4;*;extended;
7;4096;logical;
6;2048;logical;
5;*;logical;