XML 在 OS X 终端中解析 MobileConfig 文件
XML Parsing in OS X Terminal for MobileConfig file
我正在通过 bash 脚本生成(实际上是编辑)mobileconfig 文件(又名 iOS 配置文件,XML)。
脚本从 MS 数据库获取数据,现在 inject/replace 我的 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 "Password" 字段。
有人可以帮忙吗?
你可以这样做
sed -r "s#(<string>)SSID_STR(</string>)#AMD#g" File
就地替换:
sed -i -r "s#(<string>)SSID_STR(</string>)#AMD#g" File
(
、)
用于分组。 </code>和<code>
代表first
和second
这样的组。
将 AMD 替换为您的实际内容。同样,你可以为密码做。
使用纯文本工具编辑结构化数据(例如 XML)时,当文件格式以无人期望的方式改变(例如插入良性空白)时,总是会以痛苦告终。相反,使用可以正确解析 XML 并在树上工作的工具,例如 xmlstarlet
.
一般形式是
xmlstarlet ed -u xpath -v value filename.xml
其中 xpath
是一个 XPath 表达式,用于标识要更新的节点,value
是您要赋予它的新值。神奇之处在于构造一个 XPath 表达式,该表达式唯一且可靠地标识您要更新的节点。 MobileConfig XML 格式使这比平常更难;在评论中讨论后,我们最终得出
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
节点的任何后续兄弟节点,也就是说任何作为同一父节点的子节点且位于 key
节点之后的任何节点XML,
//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"]
指的是包含文本 PayloadDisplayName
的 dict
节点的 key
子节点,并且
key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"
如果包含文本 PayloadDisplayName
的 key
节点后面的 string
节点中的文本是 Exchange ActiveSync
,则 为真。所以我们将其放入我上面解释的简化表达式中并获得完整的过滤器。
我觉得有必要指出这个 XML 文件的结构使整个事情比必要的或通常的更困难。合理的结构 XML 可以用更简单的 XPath 表达式处理(大多数情况下)。
我正在通过 bash 脚本生成(实际上是编辑)mobileconfig 文件(又名 iOS 配置文件,XML)。
脚本从 MS 数据库获取数据,现在 inject/replace 我的 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 密码以及
有人可以帮忙吗?
你可以这样做
sed -r "s#(<string>)SSID_STR(</string>)#AMD#g" File
就地替换:
sed -i -r "s#(<string>)SSID_STR(</string>)#AMD#g" File
(
、)
用于分组。 </code>和<code>
代表first
和second
这样的组。
将 AMD 替换为您的实际内容。同样,你可以为密码做。
使用纯文本工具编辑结构化数据(例如 XML)时,当文件格式以无人期望的方式改变(例如插入良性空白)时,总是会以痛苦告终。相反,使用可以正确解析 XML 并在树上工作的工具,例如 xmlstarlet
.
一般形式是
xmlstarlet ed -u xpath -v value filename.xml
其中 xpath
是一个 XPath 表达式,用于标识要更新的节点,value
是您要赋予它的新值。神奇之处在于构造一个 XPath 表达式,该表达式唯一且可靠地标识您要更新的节点。 MobileConfig XML 格式使这比平常更难;在评论中讨论后,我们最终得出
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
、 的任何 //dict/key[text() = "Password"]/following-sibling
匹配此类key
节点的任何后续兄弟节点,也就是说任何作为同一父节点的子节点且位于key
节点之后的任何节点XML,//dict/key[text() = "Password"]/following-sibling::string
匹配任何string
节点是这样的后续兄弟节点,并且//dict/key[text() = "Password"]/following-sibling::string[1]
匹配作为此类key
节点的第一个后续兄弟string
节点的任何节点。
key
注释
我们已经在//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"]
指的是包含文本 PayloadDisplayName
的 dict
节点的 key
子节点,并且
key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"
如果包含文本 PayloadDisplayName
的 key
节点后面的 string
节点中的文本是 Exchange ActiveSync
,则 为真。所以我们将其放入我上面解释的简化表达式中并获得完整的过滤器。
我觉得有必要指出这个 XML 文件的结构使整个事情比必要的或通常的更困难。合理的结构 XML 可以用更简单的 XPath 表达式处理(大多数情况下)。