Bash: etkileşimli uzaktan komut istemi


16

Uzak bir sunucuya bağlanan ve bazı paketin yüklü olup olmadığını kontrol eden bir komut dosyası var:

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

Bu örnek basitleştirilebilir. İşte myscript2.shaynı sorunu olan:

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

Benim sorunum bash cevaplarımı interaktif olarak okuyamıyor.

Yerel komut dosyasını kullanıcıya yönlendirme yeteneğini kaybetmeden uzaktan yürütmenin bir yolu var mı?


Detaylandırabilir misin? Ne istediğini tam olarak bilmiyor musun? Kodunuzun bir kısmı daha faydalı olabilir.
slm

1
Sadece bir FYI, bunun işe yaramamasının nedeni senaryonuzu STDIN üzerinden geçirmeniz. Böylece bash STDIN'den okumaya başladığında, senaryonuzu alır (ya da zaten tüm senaryoyu okuduğu ve geriye hiçbir şey kalmadığı için).
Patrick

Yanıtlar:


22

Bunun gibi bir şey deneyin:

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

-tKuvvetler tty tahsisi, $(<your_script)tüm dosyayı okur ve bu durumlarda bir argüman olarak içerik geçer sshuzak kullanıcının kabuk tarafından yürütülecektir.

Komut dosyasının parametrelere ihtiyacı varsa, komut dosyasından sonra iletin:

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

Benim için çalışıyor, ama evrensel olup olmadığından emin değilim.


3
Tam da aradığım şey bu, teşekkürler!
Anthony Ananich

Güzel bir çözüm, ancak your scriptcevabınızda kullandığınız sözdiziminin avantajlarını korurken bir argüman iletmenin bir yolu var mı? Kullanımı ssh -t yourserver "$(<your_script --some_arg)"sadece sonuçlanır bash: --some_arg: command not found.
sampablokuper

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

@ Mat, teşekkürler, WFM :) Belki cevabının gövdesine ekleyebilirsin?
sampablokuper

3

Sorununuz, sshuzak makinede oturum açma, etkileşimli olmayan bir kabuk başlatmasıdır. Açık olan kolay çözüm, komut dosyasını uzak sunucuya kopyalamak ve oradan çalıştırmak olacaktır:

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

Kopyalama herhangi bir nedenden dolayı bir seçenek değilse $1, önce bağlanıp yüklenip yüklenmediğini kontrol etmek için komut dosyasını değiştiririm , ardından yeniden bağlanın ve gerekirse kurun:

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

Ben de bundan korkuyordum. SSH bağlantısı en uzun işlemdir ve bundan kaçınmaya çalışıyordum.
Anthony Ananich

@AnthonyAnanich Sen tarafından bağlantı kurulması zamandan tasarruf edebilirsiniz bir ana bağlantı kurma .
Gilles 'SO- kötü olmayı bırak'

@Gilles bunu önermeyi düşünüyordum ama benim önerim ile çalışmayacağını düşündüm. 1. ssh bağlantıları master olarak işlev görürse, $OUT=$(ssh root@server rpm -qa | grep "$1");çıkışlardan sonra kapatılacak ve daha sonra 2. bağlantı birincisi kadar zaman alacaktır. Yanlış mıyım?
terdon

1
@terdon Efendi gibi ssh -M foo.example.com sleep 99999999veya ssh -M foo.example.com read <somefifoefendi olarak çalıştırın ve işiniz bittiğinde açıkça ( killveya ile echo done >somefifo) öldürün .
Gilles 'SO- kötü olmayı bırak'

0

İşte iyi bir açıklama .

Bu yüzden senaryoyu

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

ve bu işe yaramadı.

ssh üzerindeki yerel yönlendirmeyi önlemek için betiği uzaktan kumandaya taşıdım. (Komutlarım f adlı bir dosyada bulunuyor )

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

Bu işe yaradı. İşte çıktı:

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'un orijinal niyetin yerel komut dosyalarını uzaktan çalıştırmak olduğunu belirttiği gibi, uzak kopya otomatikleştirilebilir, bu sadece bir satırda sadece bir örnektir.

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

2
Nasıl "işe yaradı"? Bu betiği bash -s < script.shOP'nin yaptığı gibi çalıştırdınız mı? Açıklamayı cevabınıza bağlamak yerine cevabınıza ekleyebilir misiniz?
terdon

Üzgünüz, kritik bir şey eklemeyi unuttum. Yerel yönlendirmeyi önlemek için komut dosyasını uzaktan kumandaya kopyaladım. Bunu açıklığa kavuşturmak için cevabıma ekleyeceğim.
X Tian

1
Komut dosyasını uzaktan kumandaya kopyalarsanız sorun ortadan kalkar. OP'nin sorunu, uzaktan çalışan yerel bir komut dosyasına etkileşimli girdi vermeye çalışmasıdır. Elbette üzerinde çalışacak kopyalayın. OP, uzak bir sunucuya bağlanırken muhtemelen birçok uzak sunucuya bu komut dosyasını çalıştırmak istiyor. Otomatikleştirmediğiniz sürece kopyalamak pratiktir.
terdon

0

Geçmişte bu soruna birkaç kez çözüm aradım, ancak asla tatmin edici bir çözüm bulamadım. Ssh içine borulama etkileşimi kaybeder. İki bağlantı (scp / ssh) daha yavaştır ve geçici dosyanız etrafta kalabilir. Ve komut satırındaki tüm komut dosyası genellikle cehennemden kaçar.

Son zamanlarda komut satırı arabellek boyutu genellikle oldukça büyük (karşılaştığım 'getconf ARG_MAX> 2MB) ile karşılaştı. Ve bu bana bunu nasıl kullanabileceğimi ve kaçan sorunu hafifletebileceğimi düşündürdü.

Sonuç:

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

veya burada bir belge ve kedi kullanarak:

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

Ben sshxargümanlar ssh üzerinde yerel giriş dosyaları da olabilir, rasgele komut dosyaları (sadece BASH değil) çalıştırabilirsiniz tamamen çalışan bir BASH örnek komut dosyası üretmek için bu fikri genişlettim . Buraya bakın .


İlginç, ama bu Mat'ın cevabından nasıl ve / veya ne zaman daha iyi ?
Scott

1
Mat'ın cevabı iyi bir ilk geçiştir (sıklıkla kullanıyorum), ancak "kaçması gereken karakterler gibi rastgele bir içerik komut dosyası işleyemez . Ve ayrıca BASH betikleri ile sınırlıdır. Benim çözümüm yüklü herhangi bir komut dosyası dili herhangi bir komut dosyası içeriği için genel bir durumdur. Ayrıca, sshx'te ayrıca oldukça şık olan serileştirme argüman dosyalarını da göstereceğim.
SourceSimian

(1) Mat yanıtının başarısız olduğu (ve sizinkinin işe yaradığı) bir komut dosyasının MCVE'sini gönderebilir misiniz? (2) Bkz. “Echo $ (stuff)” veya “echo` stuff` ”ile ilgili sorun nedir?  Kişisel echo "$(cat my_script | base64)" | base64 --decodeeşdeğer (-ish) görünüyor cat my_script | base64 | base64 --decodeno-op çok benziyor.
Scott

Merhaba @Scott. (1): senaryoyu düşünün: 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. Burada (a) etkili bir şekilde çalışıyor: bash -c $'#!/bin/bash\necho "ARG1=$1, USER=$USER, END" foo'veya benzer bir şey. Arg işlevinin çalışmadığını unutmayın. Buradaki (b) çalışırken: bash <(echo IyEvY...5EIgo= | base64 --decode) foo. (2) Taşıma kodlaması olarak base64 kullandım.
SourceSimian
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.