Bash: интерактивная удаленная подсказка


16

У меня есть скрипт, который подключается к удаленному серверу и проверяет, установлен ли какой-либо пакет:

ssh root@server 'bash -s' < myscript.sh

myscript.sh:

OUT=`rpm -qa | grep ntpdate`
if [ "$OUT" != "" ] ; then
    echo "ntpdate already installed"
else
    yum install $1
fi

Этот пример может быть упрощен. Вот myscript2.shчто имеет ту же проблему:

read -p "Package is not installed. Do you want to install it (y/n)?" choise

Моя проблема в том, что Bash не может читать мои ответы в интерактивном режиме.

Есть ли способ выполнить локальный сценарий удаленно, не теряя возможность подсказывать пользователю?


Можете ли вы уточнить? Не совсем понятно, о чем ты просишь? Еще немного вашего кода будет полезно.
SLM

1
Просто к вашему сведению, причина этого не в том, что вы передаете свой сценарий через STDIN. Таким образом, когда bash отправляется на чтение из STDIN, он получает ваш сценарий (или ничего, поскольку он уже прочитал весь сценарий и ничего не осталось).
Патрик

1
Полезный ресурс, связанный с этим: backreference.org/2011/08/10/…
slm

Ответы:


22

Попробуйте что-то вроде этого:

$ ssh -t yourserver "$(<your_script)"

Эти -tсилы на выделении терминала, $(<your_script)считывает весь файл и в этом случае передает содержимое в качестве одного аргумента ssh, который будет выполняться оболочкой удаленного пользователя.

Если сценарию нужны параметры, передайте их после сценария:

$ ssh -t yourserver "$(<your_script)" arg1 arg2 ...

Работает на меня, не уверен, что это универсально, хотя.


3
Это именно то, что я искал, спасибо!
Энтони Ананич

Хорошее решение, но есть ли способ передать аргумент your script, сохранив при этом преимущества синтаксиса, который вы использовали в своем ответе? Используя ssh -t yourserver "$(<your_script --some_arg)"только результаты в bash: --some_arg: command not found.
Сампаблокупер

1
@sampablokuper: попробуйssh ... $(<script) script_arg1 script_arg2 ...
Мат

@ Мат, спасибо, WFM :) Может быть, добавить это в текст вашего ответа?
Сампаблокупер

3

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

scp myscript.sh root@server:/tmp && ssh root@server /tmp/myscript.sh

Если по какой-либо причине копирование невозможно, я бы изменил сценарий, чтобы сначала подключиться и проверить, $1установлено ли, а затем повторно подключиться и установить, если необходимо:

OUT=$(ssh root@server rpm -qa | grep "$1");
if [ "$OUT" != "" ] ; then
    echo "$1 already installed"
else
   read -p "Package $1 is not installed. Do you want to install it (y/n)?" choice
   if [ "$choice" -eq "y" ]; then
       ssh root@server yum install "$1"
   fi
fi

Вот чего я боялся. Подключение к ssh - самая длительная операция, и я пытался ее избежать.
Энтони Ананич

@AnthonyAnanich Вы можете сэкономить время установления соединения, установив мастер-соединение .
Жиль "ТАК - перестань быть злым"

@ Жиль, я думал о том, чтобы предложить это, но я думал, что это не сработает с моим предложением. Если первое ssh-соединение действует как ведущее, оно будет закрыто после $OUT=$(ssh root@server rpm -qa | grep "$1");выхода, а затем второе соединение займет столько же времени, сколько и первое. Я ошибаюсь?
Terdon

1
@terdon Запустите что-нибудь вроде ssh -M foo.example.com sleep 99999999или ssh -M foo.example.com read <somefifoкак мастер и убейте это явно (с помощью killили echo done >somefifo), когда вы закончите.
Жиль "ТАК - перестань быть злым"

0

Вот хорошее объяснение .

Поэтому я адаптировал скрипт к

hostname
echo -n "Make your choice :"
read choice
echo "You typed " ${choice}
echo done

и это не сработало.

поэтому я переместил скрипт на удаленный компьютер, чтобы избежать локального перенаправления на ssh. (Мои команды находятся в файле с именем f )

cat f | ssh user@remotehost.com 'cat >remf'
ssh user@remotehost bash remf

Это сработало. Вот вывод:

christian@clafujiu:~/tmp$ ssh localhost bash tmp/f
christian@localhost's password: 
Linux clafujiu 2.6.32-52-generic #114-Ubuntu SMP Wed Sep 11 19:00:15 UTC 2013 i686 GNU/Linux
Sun Nov 10 14:58:56 GMT 2013
Make your choice :abc
You typed  abc
done

Как упомянул @terdon, первоначальная цель заключалась в удаленном запуске локальных сценариев, удаленное копирование может быть автоматизировано, это всего лишь один пример, все в одной строке.

REMID=`cat f |ssh user@remotehost 'cat > remf_$$; echo $$'` ;ssh root@redtoadservices.com "bash remf_${REMID} ; rm -v remf_${REMID}"

2
Как это "работает"? Вы запускали этот скрипт, bash -s < script.shкак это делал OP? Не могли бы вы включить объяснение в свой ответ вместо ссылки на него?
Terdon

Ой, извините, я забыл добавить что-то критическое. Я также скопировал скрипт на пульт, чтобы избежать локального перенаправления. Я добавлю это к своему ответу, чтобы было понятно.
X Тянь

1
Если вы скопируете скрипт на пульт, проблема исчезнет. Проблема ОП заключается в том, что он пытается дать интерактивный ввод локальному сценарию, работающему удаленно. Конечно, он побежит от вас скопировать его. Оператор хочет запустить этот сценарий при подключении к удаленному серверу, предположительно ко многим удаленным серверам. Я сомневаюсь, что копирование это практично, если вы не автоматизируете это.
Тердон

0

В прошлом я несколько раз искал решения этой проблемы, но так и не нашел полностью удовлетворительного. Проникновение в ssh теряет вашу интерактивность. Два соединения (scp / ssh) медленнее, и ваш временный файл может остаться без дела. И весь сценарий в командной строке часто заканчивается выходом из ада.

Недавно я столкнулся с тем, что размер буфера командной строки обычно довольно большой ('getconf ARG_MAX> 2MB, где я посмотрел). И это заставило меня задуматься о том, как я мог бы использовать это и смягчить проблему побега.

Результат:

ssh -t <host> /bin/bash "<(echo "$(cat my_script | base64 | tr -d '\n')" | base64 --decode)" <arg1> ...

или используя здесь документ и кошку:

ssh -t <host> /bin/bash $'<(cat<<_ | base64 --decode\n'$(cat my_script | base64)$'\n_\n)' <arg1> ...

Я расширил эту идею, чтобы создать полностью работающий пример сценария BASH, sshxкоторый может запускать произвольные сценарии (не только BASH), где аргументы также могут быть локальными входными файлами, поверх ssh. Смотрите здесь .


Интересно, но как и / или когда это лучше ответа Мэтта ?
Скотт

1
Ответ Мата - хороший первый проход (который я часто использую), однако он не может обработать сценарий произвольного содержимого, например "символов, которые необходимо экранировать. А также это ограничено скриптами BASH. Мое решение - это общий случай для любого содержимого сценария любого установленного языка сценариев. Далее, в sshx я дополнительно демонстрирую сериализацию файлов аргументов, что довольно изящно.
SourceSimian

(1) Можете ли вы опубликовать MCVE сценария, для которого ответ Мэта не получается (и ваш работает)? (2) См. Что не так с «echo $ (вещи)» или «эхо` вещи` »?  Вы echo "$(cat my_script | base64)" | base64 --decodeвыглядите эквивалентно (-ish) cat my_script | base64 | base64 --decode, что выглядит как неоперативный.
Скотт

Привет @ Скотт. (1): Рассмотрим сценарий: echo "ARG1=$1, USER=$USER, END". а) $ ssh host "$(<my_bash)" foo-> ARG1=, USER=user, END foo. б) $ ssh host bash "<(echo $(cat my_bash|base64) | base64 --decode)" foo-> ARG1=foo, USER=user, END. Здесь (а) эффективно работает: bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'или что-то подобное. Обратите внимание, что арг не работает. Где (б) работает: bash <(echo IyEvY...5EIgo= | base64 --decode) foo. (2) Я использовал base64 в качестве транспортной кодировки.
SourceSimian
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.