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

Моя проблема полягає в тому, що Баш не може читати мої відповіді інтерактивно.

Чи існує спосіб дистанційного виконання локального скрипту, не втрачаючи можливості підказувати користувача?


Чи можете ви докладно? Не зовсім зрозуміло, що ви просите? Ще трохи вашого коду буде корисним.
slm

1
Просто FYI, тому це не працює в тому, що ви передаєте свій сценарій через 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.
sampablokuper

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

@Mat, спасибі, WFM :) Може бути, додати це до своєї відповіді?
sampablokuper

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 Ви можете заощадити час встановлення з'єднання, встановивши головне з'єднання .
Жил "ТАК - перестань бути злим"

@Gilles Я думав запропонувати це, але подумав, що це не спрацює з моєю пропозицією. Якщо 1-е ssh-з'єднання виступає головним, воно буде закрито після $OUT=$(ssh root@server rpm -qa | grep "$1");виходів, і тоді 2-е з'єднання зайняє стільки ж часу, скільки і перше. Я помиляюся?
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як це робив ОП? Чи можете ви включити пояснення у відповідь замість посилання на нього?
terdon

На жаль, вибачте, що забув додати щось критичне. Я також скопіював сценарій у віддалений, щоб уникнути локального переадресації. Я додаю це до своєї відповіді, щоб було зрозуміло.
X Tian

1
Якщо ви скопіюєте скрипт на пульт, проблема усунеться. Проблема ОП полягає в тому, що він намагається дати інтерактивний вклад в локальний сценарій, який працює дистанційно. Звичайно, ви запустили з нього копію. ОП хочуть запустити цей сценарій під час підключення до віддаленого сервера, імовірно, до багатьох віддалених серверів. Я сумніваюся, що копіювати це практично, якщо ви не автоматизуєте його.
terdon

0

Я кілька разів шукав рішення цієї проблеми в минулому, проте ніколи не знаходжу повністю задовільного. Передача в ssh втрачає вашу інтерактивність. Два з'єднання (scp / ssh) проходять повільніше, і ваш тимчасовий файл може залишатися навколо. І весь сценарій у командному рядку часто закінчується в пекло.

Нещодавно я зіткнувся з тим, що розмір буфера командного рядка зазвичай досить великий ('getconf ARG_MAX> 2 Мб, де я дивився). І це змусило мене замислитися над тим, як я можу це використати і пом’якшити проблему, що втече.

Результат:

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 я додатково демонструю файли аргументів, що серіалізують, що досить чудово.
ДжерелоСиміан

(1) Чи можете ви опублікувати MCVE сценарію, на який відповідь Мата не відповідає (і ваші роботи)? (2) Див. Що не так з "echo $ (stuff)" або "echo` stuff` "?  Ваш echo "$(cat my_script | base64)" | base64 --decodeзовнішній вигляд еквівалентний (-іш) cat my_script | base64 | base64 --decode, який дуже схожий на не-оп
Скотт

Привіт @Scott (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 як транспортне кодування.
ДжерелоСимян
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.