Синтаксический анализ XML в терминале OS X для файла MobileConfig
Я работаю над созданием (фактически редактированием) файла mobileconfig (он же профиль iOS, XML) с помощью bash-скрипта.
Скрипт извлекает данные из базы данных 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>
Я хотел бы заменить пароль Wi-Fi, а также поля "Пароль" ActiveSync между
Может кто-нибудь, пожалуйста, помогите?
2 ответа
Редактирование структурированных данных (таких как 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
соответствует любомуkey
узел, который является дочернимdict
узел,//dict/key[text() = "Password"]
соответствует любомуkey
обратите внимание, что это ребенок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]
соответствует любому узлу, который является первым последующим родным братом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"]
относится к key
подузел этого dict
узел, который содержит текст PayloadDisplayName
, а также
key[text() = "PayloadDisplayName"]/following-sibling::string[1] = "Exchange ActiveSync"
верно, если текст в string
узел, который следует за key
узел, который содержит текст PayloadDisplayName
является Exchange ActiveSync
, Поэтому мы добавим это в упрощенное выражение, которое я объяснил выше, и получим полный фильтр.
Я чувствую себя обязанным указать, что структура этого XML-файла делает все это более сложным, чем необходимо или обычно. Разумно структурированный XML может обрабатываться с помощью более простых выражений XPath (большую часть времени).
Вы можете сделать это так
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
выступает за first
а также second
такие группы. Замените AMD вашим реальным контентом. Точно так же вы можете сделать для пароля.