使用shell脚本从xml标记获取属性值并转换为csv

时间:2017-02-10 13:48:42

标签: xml linux bash shell ksh

任务:

我正在尝试使用shell脚本从xml标记获取属性值,将值拆分并将其保存在.csv文件中。

这就是xml的样子:

<host>
  <servers>
    <server name="Type1Name1-Port1" >...</server>
    <server name="Type2Name2-Port2" >...</server>
    <server name="Type3Name3-Port3" >...</server>
    ...
    <server name="TypexNamex-Portx" >...</server>
  </servers>
</host>

我想从“名称” - 属性中获取值并将它们拆分如下:
Type;Name;Port

我想要的输出csv文件应如下所示:

Type1;Name1;Port1
Type2;Name2;Port2
Type3;Name3;Port3
...
Typex;Namex;Portx

问题:

  • 我无法在服务器上安装任何内容
  • 我只能使用“ksh-awk”/“xmllint wihtout --xpath”/“standard linux commands”

我可以使用我想要的任何shell语言。我更喜欢bash和ksh。

我的问题:

  • 你认为有可能解决我的任务吗?
  • 子任务的最佳方法是什么? (阅读,分裂,写作)

修改

服务器名称的示例数据:

T-TTT_AAA-A-SSS-PPPP

其中T表示类型,A表示应用程序名称,S表示服务器名称,P表示端口。 T,A和S的长度是可变的。 P是常数。

3 个答案:

答案 0 :(得分:1)

以下是我提出的建议,仅使用常用工具:Public Property LetDicOption.Add key:=OptName, Item:=OptValue

'Userform Class Module
Private DicOption As scripting.Dictionary

Public Property Get ProjectOption(ByVal OptName As String) As String
    ProjectOption = UBidStatus.ProjectOption(OptName)
End Property

Public Property Let ProjectOption(ByVal OptName As String, ByVal OptValue As String)
    If Not DicOption(OptName).Exists Then
        DicOption.Add key:=OptName, Item:=OptValue
    Else
        DicOption(OptName) = OptValue
    End If
End Property

Public Sub UserForm_Initialize()
    Set DicOption = New scripting.Dictionary
End Sub

Private Sub UserForm_Terminate()
    Set DicOption = Nothing
End Sub

Public Sub ExchangeToDicOption()
    Dim LR As Long
    Dim Rg As Range
    Dim ws As Worksheet
    Dim i As Long
    Dim a As String
    Dim b As String

    Set ws = ActiveWorkbook.Worksheets(2)
    Set Rg = ws.Columns(2)

    DicOption.RemoveAll

    LR = Rg.Find(What:="*", Lookat:=xlPart, LookIn:=xlFormulas, SearchOrder:=xlByRows, _
        SearchDirection:=xlPrevious, MatchCase:=False).Row

    If LR > 1 Then
        For i = 2 To LR
            a = Cells(i, 1)
            b = Cells(i, 2)
           UBidStatus.ProjectOption(a) = b
        Next i
    End If
End Sub

xmllint部分是根据OP发布时的示例完成的。

<强>故障:

  • sed:我们将此命令传递给echo 'cat //host/servers/server/@name' | xmllint --shell data.xml | sed -n 's: name=\"\([A-Z][a-z0-9]*\)\([A-Z][a-z0-9]*\)-\(.*\)\":\1,\2,\3:p' 。它将捕获sed
  • 中所有节点的echo 'cat //host/servers/server/@name'属性
  • xmllint:遍历name并执行在交互式shell中作为参数传递的命令。
  • <host><servers><server ...> ... </server></servers></hosts>:我们处理xmllint --shell data.xml的输出只保留我们感兴趣的数据
    • data.xml将生成以下输出:sed -n 's: name=\"\([A-Z][a-z0-9]*\)\([A-Z][a-z0-9]*\)-\(.*\)\":\1;\2;\3:p'
    • 我们定义了3个捕获组:大写字母后跟除了大写之外的任何字符(用于xmllint),另一个大写字母后跟除了大写之外的任何字符(用于xmllint),以及之间的任何字符name="Type1Name1-Port1"Type字符
    • 我们告诉sed只打印匹配的字符串,用分号
    • 分隔

输出:

Name

编辑:

要符合您在评论中指明的模式,您只需更改sed正则表达式,例如:

-

这将匹配格式",具有类型和服务器名称的任何长度。如果这不完全符合您的要求,请尽量摆弄正则表达式或在Type1;Name1;Port1 Type2;Name2;Port2 Type3;Name3;Port3 Typex;Namex;Portx 标记中提出其他问题。

答案 1 :(得分:1)

如果没有xmllint,您可以解析输入,如

<host>
  <servers>
    <server name="Type1_Name1-Port1" >...</server>
    <server name="Type-2_Name2-Port2" >...</server>
    <server name="Type3_Name-3-Port3" >...</server>
  </servers>
</host>

sed -n '/<server name=/ s/[^"]*"\([^_]*\)_\([^"]*\)-\([^"]*\)".*/\1;\2;\3/p' inputfile

答案 2 :(得分:0)

xidel -e '//server/@name' f.xml |  sed ...