Bash: interaktywny zdalny monit


16

Mam skrypt, który łączy się ze zdalnym serwerem i sprawdzam, czy jakiś pakiet jest zainstalowany:

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

Ten przykład można uprościć. Oto, myscript2.shktóry ma ten sam problem:

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

Mój problem polega na tym, że bash nie może odczytać moich odpowiedzi interaktywnie.

Czy istnieje sposób na zdalne wykonanie skryptu lokalnego bez utraty możliwości monitowania użytkownika?


Czy możesz rozwinąć? Nie jest do końca jasne, o co prosisz? Przydałoby się trochę więcej kodu.
slm

1
To tylko informacja, dlatego to nie działa, ponieważ przekazujesz skrypt przez STDIN. Tak więc, gdy bash przejdzie do odczytu ze STDIN, otrzyma twój skrypt (lub nic, ponieważ już przeczytał cały skrypt i nic nie zostało).
Patrick

1
Przydatne zasoby związane z tym: backreference.org/2011/08/10/…
slm

Odpowiedzi:


22

Wypróbuj coś takiego:

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

Te -tsiły przydział tty, $(<your_script)czyta cały plik i w takich przypadkach przekazuje treść jako jeden argument ssh, który będzie wykonywany przez shell zdalnego użytkownika.

Jeśli skrypt potrzebuje parametrów, przekaż je po skrypcie:

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

Działa dla mnie, nie jestem pewien, czy jest uniwersalny.


3
Właśnie tego szukałem, dziękuję!
Anthony Ananich,

Dobre rozwiązanie, ale czy istnieje sposób na przekazanie argumentu your script, przy jednoczesnym zachowaniu zalet składni użytej w odpowiedzi? Używanie ssh -t yourserver "$(<your_script --some_arg)"tylko wyników w bash: --some_arg: command not found.
sampablokuper

1
@sampablokuper: tryssh ... $(<script) script_arg1 script_arg2 ...
Mat

@ Mat, dzięki, WFM :) Może dodać to do treści swojej odpowiedzi?
sampablokuper

3

Problem polega na tym, że sshuruchamia się nieinteraktywna powłoka logowania na zdalnym komputerze. Oczywistym łatwym rozwiązaniem byłoby skopiowanie skryptu na zdalny serwer i uruchomienie go stamtąd:

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

Jeśli kopiowanie nie jest możliwe z jakiegokolwiek powodu, zmodyfikowałbym skrypt, aby najpierw się połączyć i sprawdzić, czy $1jest zainstalowany, a następnie ponownie podłączyć i zainstalować w razie potrzeby:

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

Tego się bałem. Połączenie z ssh jest najdłuższą operacją i starałem się tego uniknąć.
Anthony Ananich,

@AnthonyAnanich Możesz zaoszczędzić czas na ustanowienie połączenia, konfigurując połączenie główne .
Gilles „SO- przestań być zły”

@Gilles Myślałem o zasugerowaniu tego, ale pomyślałem, że to nie zadziała z moją sugestią. Jeśli pierwsze połączenia ssh działają jak master, zostaną zamknięte po $OUT=$(ssh root@server rpm -qa | grep "$1");wyjściu, a następnie drugie połączenie zajmie tyle samo czasu, co pierwsze. Czy się mylę?
terdon

1
@terdon Uruchom coś jak ssh -M foo.example.com sleep 99999999lub ssh -M foo.example.com read <somefifojako master i zabij to jawnie (za pomocą killlub echo done >somefifo), gdy skończysz.
Gilles „SO - przestań być zły”

0

Oto dobre wytłumaczenie .

Więc dostosowałem skrypt do

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

i to nie zadziałało.

więc przeniosłem skrypt do pilota, aby uniknąć lokalnego przekierowania na ssh. (Moje polecenia znajdują się w pliku o nazwie f )

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

To zadziałało. Oto wynik:

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

Ponieważ @terdon wspomniał, że pierwotnym zamiarem było zdalne uruchamianie lokalnych skryptów, zdalną kopię można zautomatyzować, to tylko jeden przykład w jednym wierszu.

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

2
Jak to działało? Czy uruchomiłeś ten skrypt z bash -s < script.shOP, tak jak zrobiłeś? Czy możesz zamieścić wyjaśnienie w swojej odpowiedzi zamiast odsyłać do niego?
terdon

Ups, przepraszam, zapomniałem dodać coś krytycznego. Skopiowałem również skrypt do pilota, aby uniknąć lokalnego przekierowania. Dołączę to do mojej odpowiedzi, aby było jasne.
X Tian

1
Jeśli skopiujesz skrypt do pilota, problem zniknie. Problem PO polega na tym, że próbuje on wnieść interaktywny wkład do lokalnego skryptu działającego zdalnie. Oczywiście to się z tobą przekopiuje. OP chce uruchomić ten skrypt podczas łączenia się ze zdalnym serwerem, prawdopodobnie z wieloma zdalnymi serwerami. Wątpię, aby skopiowanie go było praktyczne, chyba że go zautomatyzujesz.
terdon

0

W przeszłości szukałem rozwiązań tego problemu, ale nigdy nie znalazłem rozwiązania w pełni satysfakcjonującego. Przeniesienie do ssh powoduje utratę interaktywności. Dwa połączenia (scp / ssh) są wolniejsze, a plik tymczasowy może pozostać w pobliżu. Cały skrypt w wierszu poleceń często kończy się ucieczką z piekła.

Ostatnio spotkałem się z tym, że rozmiar bufora wiersza poleceń jest zwykle dość duży ('getconf ARG_MAX> 2 MB, gdzie szukałem). I to sprawiło, że pomyślałem o tym, jak mogę to wykorzystać i złagodzić problem ucieczki.

Wynik to:

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

lub używając tutaj dokumentu i kota:

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

Rozwinęłem ten pomysł, aby stworzyć w pełni działający przykładowy skrypt BASH, sshxktóry może uruchamiać dowolne skrypty (nie tylko BASH), w których argumentami mogą być również lokalne pliki wejściowe, za pośrednictwem ssh. Zobacz tutaj .


Ciekawe, ale jak i / lub czy jest to lepsze niż odpowiedź Mat ?
Scott

1
Odpowiedź Mat jest dobrym pierwszym przejściem (z którego często korzystam), jednak nie jest w stanie obsłużyć skryptu o dowolnej zawartości, na przykład "znaków, które trzeba będzie uciec. A także jest ograniczony do skryptów BASH. Moje rozwiązanie to ogólny przypadek dowolnej zawartości skryptu dowolnego zainstalowanego języka skryptowego. Ponadto w sshx dalej demonstruję szeregowanie plików argumentów, co jest całkiem sprytne.
SourceSimian,

(1) Czy możesz opublikować MCVE skryptu, na który odpowiedź Matta nie powiedzie się (a twój działa)? (2) Zobacz, co jest nie tak z „echo $ (rzeczy)” lub „echo„ rzeczy ”?  Twój echo "$(cat my_script | base64)" | base64 --decodewygląd jest równoważny (-ish) cat my_script | base64 | base64 --decode, co wygląda bardzo podobnie do braku op.
Scott,

Cześć @Scott. (1): Rozważmy scenariusz: echo "ARG1=$1, USER=$USER, END". a) $ ssh host "$(<my_bash)" foo-> ARG1=, USER=user, END foo. b) $ ssh host bash "<(echo $(cat my_bash|base64) | base64 --decode)" foo-> ARG1=foo, USER=user, END. Tutaj (a) działa skutecznie: bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'lub coś podobnego. Zauważ, że arg nie działa. Gdzie (b) jest uruchomiony: bash <(echo IyEvY...5EIgo= | base64 --decode) foo. (2) Użyłem base64 jako kodowania transportowego.
SourceSimian
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.