用于 MobileConfig 文件的 OS X 终端中的 XML 解析

Posted

技术标签:

【中文标题】用于 MobileConfig 文件的 OS X 终端中的 XML 解析【英文标题】:XML Parsing in OS X Terminal for MobileConfig file 【发布时间】:2015-06-23 06:24:41 【问题描述】:

我正在通过 bash 脚本生成(实际上是编辑)一个 mobileconfig 文件(又名 ios 配置文件,XML)。

脚本从 MS 数据库中获取数据,现在必须在我的 mobileconfig 文件 (XML) 中注入/替换这些数据。

XML 文件的结构如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>PayloadContent</key>
    <array>
        <dict>
            <key>Host</key>
            <string>outlook.office365.com</string>
            <key>MailNumberOfPastDaysToSync</key>
            <integer>7</integer>
            <key>Password</key>
            <string>ActiveSyncPassword</string>
            <key>PayloadDescription</key>
            <string>Configures an Exchange account</string>
            <key>PayloadDisplayName</key>
            <string>Exchange ActiveSync</string>
            <key>PayloadVersion</key>
            <integer>1</integer>
            <key>SSL</key>
            <true/>
            <key>UserName</key>
            <string>xxxxxxx@xxx.com</string>
            <key>disableMailRecentsSyncing</key>
            <false/>
        </dict>
        <dict>
            <key>AutoJoin</key>
            <true/>
            <key>EncryptionType</key>
            <string>WPA</string>
            <key>HIDDEN_NETWORK</key>
            <true/>
            <key>IsHotspot</key>
            <false/>
            <key>Password</key>
            <string>WEPWPAWPSPEAPTLS</string>
            <key>PayloadType</key>
            <string>com.apple.wifi.managed</string>
            <key>PayloadVersion</key>
            <real>1</real>
            <key>ProxyType</key>
            <string>None</string>
            <key>SSID_STR</key>
            <string>SSID</string>
        </dict>
        <dict>

我想使用任何本机(xmllint、sed)或非本机工具替换 之间的 WiFi 密码以及 ActiveSync“密码”字段。

有人可以帮忙吗?

【问题讨论】:

显然 /usr/libexec/PlistBuddy 是编辑 mobileconfig 的一个不错的选择(原生且专门用于处理 plist 文件) 【参考方案1】:

使用纯文本工具编辑结构化数据(例如 XML)时,当文件格式以没人期望的方式改变(例如插入良性空白)时,总是以痛苦告终。相反,请使用能够正确解析 XML 并在树上工作的工具,例如 xmlstarlet

这个的一般形式是

xmlstarlet ed -u xpath -v value filename.xml

其中xpath 是一个XPath 表达式,用于标识您要更新的节点,value 是您要为其赋予的新值。神奇之处在于构造一个 XPath 表达式,该表达式唯一且可靠地标识您要更新的节点。 MobileConfig XML 格式使这比平时更难。在 cmets 讨论后,我们最终得到了

xmlstarlet ed -u '//dict[key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"]/key[text() = "Password"]/following-sibling::string[1]' -v 'abc123' filename.xml

这个的核心是XPath表达式

//dict[key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"]/key[text() = "Password"]/following-sibling::string[1]

..这需要一些解释。我们使用以下功能:

//dict 匹配文档中的任何 dict 节点, //dict/key 匹配作为 dict 节点的子节点的任何 key 节点, //dict/key[text() = "Password"] 匹配作为 dict 节点的子节点并包含文本 Password 的任何 key 注释, //dict/key[text() = "Password"]/following-sibling 匹配此类 key 节点的任何后续兄弟节点,即任何属于同一父节点的子节点并且位于 XML 中 key 节点之后的任何节点, //dict/key[text() = "Password"]/following-sibling::string 匹配任何 string 节点,它是这样的后续兄弟节点,并且 //dict/key[text() = "Password"]/following-sibling::string[1] 匹配作为此类key 节点的第一个后续兄弟string 节点的任何节点。

我们已经在//dict/key[text() = "Password"] 中使用了一个条件;为了找到要更改密码条目的dict 节点,我们需要更多信息。我们要查找的dict节点由

标识
//dict[key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"]

即满足条件的dict节点

key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"

此条件下的 XPath 表达式都与正在测试的 dict 节点相关,所以

key[text() = "PayloadDisplayName"]

指的是包含文本PayloadDisplayNamedict 节点的key 子节点,并且

key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"

如果包含文本 PayloadDisplayNamekey 节点之后的 string 节点中的文本是 Exchange ActiveSync,则为真。因此,我们将其放入我上面解释过的简化表达式中并获得完整的过滤器。

我不得不指出,这个 XML 文件的结构使整个事情变得比必要或通常更困难。结构合理的 XML 可以用更简单的 XPath 表达式来处理(大多数时候)。

【讨论】:

谢谢 Wintermute,我会试试这个工具并回复你。我很高兴看到 Brew 有一个可以使用的瓶子 :) 嘿,伙计,你的解决方案听起来很完美,我试过了,它就像一个魅力,只有小故障,我有两个“密码”树(我编辑了我的原始帖子)。可以请教吗? 这只是在 XPath 中编码选择标准的问题。不过,根据已编辑问题中的描述,我并不完全确定您的选择标准是什么——比如更改dict 条目中的密码,其PayloadDisplayNameExchange ActiveSync?请澄清。 所以,我有两个 树,第一个用于 WiFi 设置,第二个用于 ActivySync 设置。如果我将上面的命令应用于“密码”,该命令将替换两个“密码”字段,但它必须不同(这两个字段的密码不同)我不知道我是否清楚:/跨度> 好吧,如果它总是第一个,您可以使用 XPath /plist/dict/array/dict[1]/key[text() = "Password"]/following-sibling::string[1],但我不确定我是否愿意依赖它。问题是:如何可靠地识别包含您要更改的密码的dict 节点?【参考方案2】:

你可以这样做

sed -r "s#(<string>)SSID_STR(</string>)#\1AMD\2#g" File

对于就地替换:

sed -i -r "s#(<string>)SSID_STR(</string>)#\1AMD\2#g" File

(, ) 用于分组。 \1\2 代表 firstsecond 这样的组。 用您的实际内容替换 AMD。同样,你可以为密码做。

【讨论】:

感谢您的努力,理想情况下我会寻找一个特定于 xml 的解析工具

以上是关于用于 MobileConfig 文件的 OS X 终端中的 XML 解析的主要内容,如果未能解决你的问题,请参考以下文章

当我单击网页上的按钮时生成 .mobileconfig 文件

mobileconfig 文件添加对位置服务的访问

在 Safari ios 中打开保存在应用程序中的 .mobileconfig 文件

在 iOS 7 上以编程方式安装 .mobileconfig 文件

不使用 Safari 安装 mobileconfig

IOS 10 beta的mobileconfig文件