Синтаксический анализ 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 между , используя любой собственный (xmllint, sed) или не собственный инструмент.

Может кто-нибудь, пожалуйста, помогите?

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 вашим реальным контентом. Точно так же вы можете сделать для пароля.

Другие вопросы по тегам