Установка и использование переменной в однострочной команде Expect в скрипте Bash

Вопрос или проблема

У меня есть массив, $HOSTS, который содержит набор имен хостов.
У меня есть таблица базы данных, в которой есть набор IP-адресов. Я хочу пройти по каждому IP-адресу в этой таблице и отправить команду (hostname -s) на этот адрес, а затем добавить вывод в $HOSTS. У удаленных серверов не настроена PKI, и скрипт должен работать автоматически без человеческого вмешательства. Это закрытая сеть, а пароль root хранится в виде переменных в скрипте ($SERVERPASSWORD). И прежде чем кто-нибудь скажет это, я понимаю, что хранить пароль root в такой переменной — не лучшая идея, но в данном случае важнее отсутствие человеческого вмешательства, чем защита пароля root.

Вот соответствующая часть моего текущего (очищенного) скрипта:

 for IP in $(sql query that returns a list of IP addresses);do
    HOSTS=($(expect -c 'spawn ssh root@$IP "hostname -s"; expect "assword:";send ""$SERVERPASSWORD"\r";interact' ${HOSTS[@]}))
done

Результатом этого является сбой с сообщением:

Can’t read “IP”: no such variable while executing “spawn ssh root@$IP
“hostname -s””

Я могу поставить echo $IP вместо этого, и это работает, как ожидалось, так что я знаю, что переменная IP заполняется в цикле.

После некоторых поисков в Google я понял, что expect должен иметь свою собственную переменную и не может использовать переменные Bash, но все решения, которые я нашел, относились к настоящим скриптам Expect, а не к использованию expect внутри Bash-скрипта. Я пытался изменить свою команду на expect set IP $IP -c 'spawn ....', но это, похоже, не сработало.

Что я могу сделать, чтобы expect считал значение $IP в этом цикле For?

Избавьтесь от привычки использовать ИМЕНА ПЕРЕМЕННЫХ В ВЕРХНЕМ РЕГИСТРЕ, оставьте их зарезервированными для оболочки. Однажды вы напишете PATH=something, а затем зададитесь вопросом, почему ваш скрипт не работает.

Также вы можете использовать array+=( "new element" ) для добавления в массив.

Ниже я расширил однострочное решение для читаемости.


Это всего лишь проблема кавычек в оболочке. Есть несколько способов ее решить:

  1. использовать двойные кавычки для тела expect. Это значит, что вам придется по-другому обрабатывать кавычки expect: вы можете экранировать все двойные кавычки (неудобно) или использовать фигурные скобки, которые являются механизмом одинарных кавычек Tcl:

    hosts+=( "$(
        expect -c "
            spawn ssh root@$ip {hostname -s}
            expect {assword:}
            send {$serverPassword}; send \\r
            expect eof
        "
    )" )
    
  2. передавать переменные через окружение. Здесь я использую цитируемый heredoc, чтобы вам не пришлось беспокоиться о кавычках:

    hosts+=( "$(
       ip="$ip" pw="$serverPassword" expect <<<'END_EXPECT'
            spawn ssh root@$env(ip) "hostname -s"
            expect "assword:"
            send "$env(serverPassword)\r"
            expect eof
    END_EXPECT
    )" )
    

Ответ или решение

Чтобы настроить и использовать переменные в однострочной команде Expect в Bash-скрипте эффективно, необходимо учитывать некоторые особенности работы между оболочкой Bash и языком Expect. В данном случае вы пытаетесь передать переменную $IP и $SERVERPASSWORD в блок кода Expect. Давайте разберёмся, как это сделать корректно.

Проблема

Ошибка, с которой вы столкнулись, связана с тем, что Expect не может напрямую интерпретировать переменные, определённые в Bash. Это происходит потому, что кусок кода Expect выполняется в отдельной среде. Чтобы передать значение переменной, используйте один из следующих подходов:

Решения

1. Использование двойных кавычек

Одним из простейших решений является использование двойных кавычек для обёртывания тела команды Expect. Однако в этом случае вам придётся позаботиться о правильном экранировании переменных внутри Expect, используя фигурные скобки. Вот как это можно сделать:

HOSTS+=("$(
    expect -c "
        spawn ssh root@$IP {hostname -s}
        expect {assword:}
        send \"${SERVERPASSWORD}\\r\"
        expect eof
    "
)")

Здесь корректно передаются значения переменных $IP и $SERVERPASSWORD в тело Expect-кода. Обратите внимание на экранирование внутри строки.

2. Использование окружения

Другим подходом является передача переменных посредством окружения. Вы можете использовать heredoc для упрощения этой стратегии:

HOSTS+=("$(
    ip="$IP" pw="$SERVERPASSWORD" expect <<<'END_EXPECT'
        spawn ssh root@$ip "hostname -s"
        expect "assword:"
        send "$pw\r"
        expect eof
END_EXPECT
)")

В этом случае переменные ip и pw передаются в среду выполнения Expect, и вы можете легко к ним обращаться. Это значительно упрощает управление кавычками и экранированием.

Завершение

Когда вы используете один из этих методов, ваш скрипт будет обрабатывать IP-адреса и запрашивать имя хоста с эдекватным выводом. Таким образом, вы сможете беспрепятственно обновлять массив $HOSTS, не сталкиваясь с ошибками.

Не забудьте, что использование переменных, содержащих секретные данные, таких как пароли, может подвергать систему риску. Подумайте о возможных методах защиты этой информации в будущем, даже в условиях, когда автоматизация важнее безопасности.

Оцените материал
Добавить комментарий

Капча загружается...