Shell脚本读取平面文件并替换xml值

时间:2016-06-23 20:53:01

标签: shell awk sed ksh

我有一个像这样的平面文件:

File: 
# Environment
Application.Env~DEV
# Identity
Application.ID~999
# Name
Application.Name~appname

像这样的XML:

<name>Application/Env</name>
<value>XXX</value>
<name>Application/ID</name>
<value>000</value>
<name>Application/Name</name>
<value>AAA</value>

我正在寻找一个脚本(awk,sed等)来读取平面文件,并将xml中<value>标签中的所有数据替换为〜{{1后标签匹配〜之前的数据。最终生成的XML将如下所示:

<name>

感谢您的帮助!

2 个答案:

答案 0 :(得分:4)

使用XMLStarlet,看起来如下所示:

#!/bin/bash

# usage: [script] [flatfile-name] <in.xml >out.xml
flatfile=$1

# store an array of variables, and an array of edit commands
xml_vars=( )
xml_cmd=( )
count=0

while read -r line; do
  [[ $line = *"~"* ]] || continue
  key=${line%%"~"*}   # put everything before the ~ into key
  key=${key//"."/"/"} # change "."s to "/"s in key
  val=${line#*"~"}    # put everything after the ~ into val

  # assign key to an XMLStarlet variable to avoid practices that can lead to injection
  xml_vars+=( --var "var$count" "'$key'" )

  # update the first value following a matching name
  xml_cmd+=( -u "//name[.=\$var${count}]/following-sibling::value[1]" \
             -v "$val" )

  # increment the counter used to assign variable names
  (( ++count ))
done <"$flatfile"

if (( ${#xml_cmd[@]} )); then
  xmlstarlet ed "${xml_vars[@]}" "${xml_cmd[@]}"
else
  cat # no edits to do
fi

这将运行如下命令:

xmlstarlet ed \
  --var var0 "Application/Env" \
  --var var2 "Application/ID"  \
  --var var3 "Application/Name" \
  -u '//name[.=$var0]/following-sibling::value[1]' -v 'DEV' \
  -u '//name[.=$var1]/following-sibling::value[1]' -v '999' \
  -u '//name[.=$var2]/following-sibling::value[1]' -v 'appname'

...将名称Application/Env后的第一个值替换为DEV,名称Application/ID后的第一个值替换为999,第一个值替换为Application/Name后的第一个值使用appname命名//name[.="Application/Name"]/following-sibling::value[1]

稍微偏执的方法可能会生成像Application.Foo"or 1=1 or .="~bar 这样的查询;作为安全措施,正在遵循带外变量。如果输入文件包含:

,请考虑可能发生的情况
//name[.="Application/Foo" or 1=1 or .=""]/following-sibling::value[1]

...而得到的XPath是

1=1

由于bar始终为true,因此会匹配每个名称,从而将文件中的每个值更改为var a, b, c, d; var uva, uvb, uvc, uvd; var uvs = this.faceVertexUvs[ 0 ]; for ( i = 0; i < stacks; i ++ ) { for ( j = 0; j < slices; j ++ ) { a = i * sliceCount + j; b = i * sliceCount + j + 1; c = ( i + 1 ) * sliceCount + j + 1; d = ( i + 1 ) * sliceCount + j; uva = new THREE.Vector2( j / slices, i / stacks ); uvb = new THREE.Vector2( ( j + 1 ) / slices, i / stacks ); uvc = new THREE.Vector2( ( j + 1 ) / slices, ( i + 1 ) / stacks ); uvd = new THREE.Vector2( j / slices, ( i + 1 ) / stacks ); faces.push( new THREE.Face3( a, b, d ) ); uvs.push( [ uva, uvb, uvd ] ); faces.push( new THREE.Face3( b, c, d ) ); uvs.push( [ uvb.clone(), uvc, uvd.clone() ] ); } } 。< / p>

不幸的是,XMLStarlet的实现并没有有效地防范这种情况;但是,使用绑定变量使得成为可能以便实现提供这样的预防措施,因此在此背景下未来的发布可能是安全的。

答案 1 :(得分:1)

使用Perl和XML::XSH2XML::LibXML的包装:

#!/usr/bin/perl
use warnings;
use strict;
use XML::XSH2;

open my $IN, '<', 'flatfile' or die $!;
$XML::XSH2::Map::replace = { map { chomp; split /~/ } grep /~/, <$IN> };

xsh << 'end.';
    open 1.xml ;
    for //name {
        set following-sibling::value[1]
            xsh:lookup('replace', xsh:subst(., '/', '.')) ;
    }
    save :b ;
end.

我将XML打包到<root>标记中以使其格式良好。