Bash'de ssh ve çoklu komutları çalıştırmanın en temiz yolu nedir?


349

Zaten bir ssh aracısı ayarladım ve Bash komut dosyasında harici bir sunucuda komutları çalıştırabilirim:

ssh blah_server "ls; pwd;"

Şimdi, gerçekten yapmak istediğim şey, harici bir sunucuda birçok uzun komut çalıştırmak. Tüm bunları tırnak işaretleri arasına almak oldukça çirkin olurdu ve sadece bundan kaçınmak için birkaç kez ssh'ing yapmaktan kaçınmayı tercih ederim.

Peki, bunu parantez veya başka bir şeyle çevrili bir seferde yapabilmemin bir yolu var mı? Ben çizgileri boyunca bir şey arıyorum:

ssh blah_server (
   ls some_folder;
   ./someaction.sh;
   pwd;
)

Temel olarak, temiz olduğu sürece herhangi bir çözümden memnun olacağım.

Düzenle

Açıklığa kavuşturmak için, bunun daha büyük bir bash senaryosunun bir parçası olduğundan bahsediyorum. Diğer insanların senaryo ile başa çıkması gerekebilir, bu yüzden temiz tutmak istiyorum. Ben benziyor bir satır ile bir bash komut dosyası istemiyorum:

ssh blah_server "ls some_folder; ./someaction.sh 'some params'; pwd; ./some_other_action 'other params';"

çünkü çok çirkin ve okunması zor.


2
Hmm, tüm bunları sunucudaki bir senaryoya koyup tek bir sshçağrı ile çağırmaya ne dersiniz ?
Nikolai Fetissov

Komutlar istemci tarafında bağlıdır eğer @Nikolai, onlar bir kabuk komut dosyası, daha sonra içine yazılabilir scp, sshve kaç. Bence bu en temiz yol olacak.
khachik

6
Bu daha büyük bir bash betiğinin bir parçası, bu yüzden kişisel bilgisayarımda yarısı ve diğer yarısı sunucuda yaşayan ve ssh ile çalışan bölünmek istemem. Mümkünse, gerçekten kişisel bilgisayarımdan çalışan bir komut dosyası olarak tutmak istiyorum. Bir ssh'a bir sürü komut yerleştirmenin gerçekten temiz bir yolu yok mu?
Eli

En iyi yol bash değil Perl, Python, Ruby, vb.
Kullanmaktır

1
Uzak komutları tırnak içine almaktan neden kaçınmak istiyorsunuz? Tırnak içinde istediğiniz kadar satırsonu olabilir; ve standart giriş yerine bir dize kullanılması, örneğin uzak komut dosyasına girişi okumak için standart girişin mevcut olduğu anlamına gelir. (Unix'te olmasına rağmen, dizenin bazı bölümlerini değerlendirmek için özellikle yerel kabuğa ihtiyacınız olmadıkça, tek tırnaklar genellikle çift tırnaklara göre tercih edilir.)
üçlü

Yanıtlar:


457

Nasıl Bash hakkında Burada Belgesi :

ssh otherhost << EOF
  ls some_folder; 
  ./someaction.sh 'some params'
  pwd
  ./some_other_action 'other params'
EOF

Yorumlarda @Globalz tarafından belirtilen sorunlardan kaçınmak için (uzak sitede ne yaptığınıza bağlı olarak) ilk satırı değiştirerek

ssh otherhost /bin/bash << EOF

Here belgesinde değişken değiştirme yapabileceğinizi, ancak alıntı sorunları ile uğraşmanız gerekebileceğini unutmayın. Örneğin, "sınır dizesi" ni ( EOFyukarıda.) Alıntılarsanız, değişken değiştirme yapamazsınız. Ancak, sınır dizesini alıntılamadan değişkenler değiştirilir. Örneğin, $NAMEkabuk komut dosyanızda yukarıda tanımladıysanız ,

ssh otherhost /bin/bash << EOF
touch "/tmp/${NAME}"
EOF

ve hedefte otherhostatadığınız şeyin adıyla bir dosya oluşturur $NAME. Kabuk betiği alıntısı ile ilgili diğer kurallar da geçerlidir, ancak buraya girmek için çok karmaşıktır.


6
Bu tam olarak istediğim gibi görünüyor! Nasıl çalışır? Açıklayan bir sayfanın bağlantısı olur muydu?
Eli

9
+1 Sadece kendimi düşünüyordum - burada okuyabileceğimiz tek yer: tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
Bu çıktıyı yerelime de alıyorum: Sözde terminal ayrılmayacak çünkü stdin bir terminal değil.
Globalz

14
Kelimeyi alıntılamanız sizin için önemli olabilirKomut satırlarının genişlemesini önlemek (yani ilk 'EOF') . Bakınız: erkek bash | daha az + / 'Here Documents'
assem

6
Paul, bunu öneriyorum çünkü bu soruya yaklaşık 200 bin görüşle, ssh ile senaryo yazarken birçok insan buraya geliyor gibi görünüyor. Komut dosyası oluştururken değerleri enjekte etmek yaygındır. Diğer sorular ve yorumlarda gürültü için değilse, sadece bir tane yapmak ve yoluma devam ediyorum, ama bu aşamada görülmesi olası değildir. Tek satırlık bir dipnot, insanlara bazı büyük baş ağrılarını kurtarabilir.
koyae

123

Komut dosyanızı yerel olarak düzenleyin, ardından ssh içine ekleyin, ör.

cat commands-to-execute-remotely.sh | ssh blah_server

nerede commands-to-execute-remotely.sh görünüyor listenizi yukarıda istiyorum:

ls some_folder
./someaction.sh
pwd;

7
Bu, uzak komut dosyası tarafından ne yürütüldüğünü tam olarak bilmenin büyük avantajına sahiptir - alıntıda sorun yoktur. Dinamik komutlara ihtiyacınız varsa, alt kabuklu bir kabuk komut dosyası kullanabilirsiniz, yine de ssh içine borulama yapabilirsiniz, yani ( echo $mycmd $myvar ; ...) | ssh myhost- kedi kullanımında olduğu gibi, ssh komut akışına ne olduğunu tam olarak bilirsiniz. Ve elbette komut dosyasındaki alt kabuk, okunabilirlik için çok satırlı olabilir - bkz. Linuxjournal.com/content/bash-sub-shells
RichVel

6
Bunu içindeki argümanlarla yapabilir misin commands-to-execute-remotely.sh?
elaRosca

1
Bunun paultomblin çözeltisinden çok daha yararlı olduğunu düşünüyorum. Komut dosyası dosyayı açmak zorunda kalmadan herhangi bir ssh sunucusuna yönlendirilebilir. Ayrıca çok daha okunabilir.
Patrick Bassut

3
evet, ancak echo veya burada bir belge kullanarak (bkz. üst yanıt): kullanın: $localvaryerel olarak tanımlanmış bir değişkeni \$remotevaryorumlamak, uzaktan tanımlanmış bir değişkeni uzaktan yorumlamak \$(something with optionnal args), uzak sunucuda yürütülen bir şeyin çıktısını almak için. 1 ile ssh yapabileceğiniz bir örnek (veya burada gösterildiği gibi, birden fazla ssh komutu):echo " for remotedir in /*/${localprefix}* ; do cd \"\$remotedir\" && echo \"I am now in \$(pwd) on the remote server \$(hostname) \" ; done " | ssh user1@hop1 ssh user2@hop2 ssh user@finalserver bash
Olivier Dulac

2
Hmm ... bu giriş yeniden yönlendirmesini kullanmaktan nasıl farklı, yani ssh blah_server < commands-to-execute-remotely.sh?
flow2k

41

Örnek kodunuzla eşleştirmek için komutlarınızı tek veya çift qoutes içine alabilirsiniz. Örneğin

ssh blah_server "
  ls
  pwd
"

Bu biçimi seviyorum, ancak ne yazık ki std verileri bir değişkene depolamak için yararlı değildir.
Signus

1
Signus, "std verilerini bir değişkene depolamak" ile ne demek istiyorsun?
Andrei B

1
@Signus Açıkladığınız şeyi yapmak mükemmel bir şekilde mümkündür, ancak muhtemelen uzak komutların etrafında çift tırnak yerine tek tırnak kullanmak isteyeceksiniz (veya yerel kabuğunuzun araya girmesini önlemek için çift tırnak içine kaçması gereken operatörlerden kaçın ve enterpolasyon).
tripleee

Ben çift tırnak içine bu fileToRemove = $ (find .tyty f -name 'xxx.war') gibi bir komut koya koyamıyorum. fileToRemove içinde bir dosya adı olmalı, bunun yerine boş bir dize içermelidir. Bir şeyden kaçmak gerekiyor mu?
jkonst

37

İki yol görüyorum:

İlk önce böyle bir kontrol soketi yaparsınız:

 ssh -oControlMaster=yes -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip>

ve komutlarını çalıştır

 ssh -oControlMaster=no -oControlPath=~/.ssh/ssh-%r-%h-%p <yourip> -t <yourcommand>

Bu şekilde sunucuya yeniden bağlanmadan ssh komutu yazabilirsiniz.

İkincisi, komut dosyasını dinamik olarak oluşturmak scpve çalıştırmaktır.


20

Bu, aşağıdaki gibi de yapılabilir. Komutlarınızı bir betiğe koyun, hadi commandsinc.sh

#!/bin/bash
ls some_folder
./someaction.sh
pwd

Dosya 'yı kaydet

Şimdi uzak sunucuda çalıştırın.

ssh user@remote 'bash -s' < /path/to/commands-inc.sh

Benim için asla başarısız olmadı.


Aslında düşündüğüm gibi! Peki neden bash -sgereklidir?
flow2k

Ayrıca, #!/bin/bashgerçekten kullanılır?
flow2k

2
-S uyumluluk için oradadır. Man bash -s dizininden -s seçeneği varsa veya seçenek işlendikten sonra hiçbir bağımsız değişken kalmazsa, komutlar standart girdiden okunur. Bu seçenek, etkileşimli bir kabuk çağrılırken konumsal parametrelerin ayarlanmasını sağlar.
RJ

1
RJ, teşekkürler! İlk yorumumla, sadece yaptığımız ssh user@remote < /path/to/commands-inc.shgibi görünüyor (benim için çalışıyor gibi görünüyor). Sanırım sürümünüz bashkabuğu başka bir kabuğu değil, kullanmamızı sağlıyor - amaç bu, değil mi?
flow2k

2
Teşekkürler. Evet, bazılarımız notlarımız ve bash ve diğer kabukların eski versiyonlarından alışkanlıklarımız var. :-)
RJ

10

Tüm komutları bir betiğe koyun ve komut dosyası gibi çalıştırılabilir.

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh

Biraz tuhaf ama iyi çalışıyor. Args betiğe aktarılabilir (yeniden yönlendirmeden önce veya sonra). örneğin, 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2' örn. 'ssh <remote-user> @ <remote-host> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
17'de gaoit

Yukarıda başka bir cevapla benzer bir sorum vardı, ama dahil etmenin amacı bash -snedir?
flow2k

1
@ flow2k -s, standart girdiden komutları okumak için bash'ı belirtmektir. İşte mansayfalardan açıklamaIf the -s option is present, or if no arguments remain after option processing, then commands are read from the standard input. This option allows the positional parameters to be set when invoking an interactive shell.
Jai Prakash

@JaiPrakash Bunun için teşekkürler, ama aslında bashkomutu tamamen ortadan kaldırabileceğimizi düşünüyordum , yani ssh remote-user@remote-host <./remote-commands.sh. Yolumu denedim ve bir şekilde eksik olup olmadığından emin olmasam da işe yaradı.
flow2k

@ flow2k benim adam sayfaları anlayışımdan bu seçenek olmadan herhangi bir eksiklik olmayacak. Herhangi bir argüman iletmeye çalışıyorsanız gereklidir. - teşekkürler
Jai Prakash

9

SSH ve Bash'de Birden Çok Komut Çalıştır.

Komutları yankıya geçirilen ve tümü ssh komutuna bağlanan bir dize içindeki noktalı virgüllerle ayırın. Örneğin:

echo "df -k;uname -a" | ssh 192.168.79.134

Pseudo-terminal will not be allocated because stdin is not a terminal.
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sda2       18274628 2546476  14799848  15% /
tmpfs             183620      72    183548   1% /dev/shm
/dev/sda1         297485   39074    243051  14% /boot
Linux newserv 2.6.32-431.el6.x86_64 #1 SMP Sun Nov 10 22:19:54 EST 2013 x86_64 x86_64 x86_64 GNU/Linux

arnab, gelecekte, kodunuzun kısaca ne yaptığını açıklayın. Sonra nasıl çalıştığı hakkında konuşun, sonra kodu girin. Daha sonra kodu bir kod bloğuna yerleştirin, böylece okunması kolaydır.
Eric Leschinski

Söz itibaren, sunulan tam OP önlemek istediği: "Ben bir çizgi ile bir bash script var istemiyorum böyle ... görünüyor"
JWW

6

Burada benim gibi tökezleyen herkes için - noktalı virgül ve yeni satırdan kaçma konusunda başarılı oldum:

İlk adım: noktalı virgül. Bu şekilde ssh komutunu kırmıyoruz:

ssh <host> echo test\;ls
                    ^ backslash!

Uzak ana makineleri / ana dizini (kök olarak oturum açmış) listeledi

ssh <host> echo test;ls
                    ^ NO backslash

geçerli çalışma dizinini listeledi.

Sonraki adım: hattı bölmek:

                      v another backslash!
ssh <host> echo test\;\
ls

Bu, uzaktan çalışma dizinini tekrar listeledi - gelişmiş biçimlendirme:

ssh <host>\
  echo test\;\
  ls

Gerçekten burada daha güzel eğer kırık çizgiler etrafında belge veya tırnak - iyi, ben karar değil ...

(Bash kullanarak, Ubuntu 14.04 LTS.)


1
Daha iyi ne olabilir && ve || bu şekilde de - yankı testi \ & \ & ls
Miro Kropacek

@MiroKropacek Bu da hoş. Daha iyi? Ne istediğinize bağlıdır; ikinci komutu koşullu olarak çalıştır , sonra evet, koşulsuz olarak çalıştır (ilk başarılı veya başarısız olursa olsun), o zaman değil ...
Aconcagua

@MiroKropacek, komutların etrafında çift tırnak koyarken &&'den kaçmak zorunda ssh host "echo test && ls"
kalmadım

5

Çok satırlı dizeler ve çoklu bash komut dosyaları kullanarak gönderilen yanıtlar benim için işe yaramadı.

  • Uzun çok satırlı dizelerin bakımı zordur.
  • Ayrı bash komut dosyaları yerel değişkenleri korumaz.

Yerel bağlamı korurken birden fazla komutu ssh ve çalıştırmanın işlevsel bir yolu.

LOCAL_VARIABLE=test

run_remote() {
    echo "$LOCAL_VARIABLE"
    ls some_folder; 
    ./someaction.sh 'some params'
    ./some_other_action 'other params'
}

ssh otherhost "$(set); run_remote"

3
Bunu birisinin bunu yapmak istemesinin tek nedeni bir komut satırında oturuyor olmaları gibi düşünüyorsunuz. Bu sorunun diğer nedenleri de vardır, örneğin dağılmış ortamlarda rundeck, jenkins, vb.Gibi araçlarla komutları yürütmek gibi
Charles Addis

5

Uzun komutlar için en temiz ama kesinlikle en kolay olup olmadığından emin değilim:

ssh user@host "cmd1; cmd2; cmd3"

Sadelikte en iyisi!
not2savvy

4

Bu, diğer dosyaları dahil etmek zorunda olmadığınız için komut dosyaları oluşturmak için iyi çalışır:

#!/bin/bash
ssh <my_user>@<my_host> "bash -s" << EOF
    # here you just type all your commmands, as you can see, i.e.
    touch /tmp/test1;
    touch /tmp/test2;
    touch /tmp/test3;
EOF

"$ (Hangi bash) -s" bölümü size uzak makineden ziyade yerel makinedeki bash konumunu verir. Bunun yerine '$ (hangi bash) -s' istediğinizi düşünüyorum (yerel parametre yerine koymak için tek tırnak).
jsears

1
Paul Tomblin 6 yıl önce Bash Here Dokümanı yanıtını verdi. Bu cevaba hangi değer katılıyor?
jww

-sBayrağı, ilk satırı değiştirerek kurtulmayı başarabileceğinizi söylüyorum ... ve sadece hatırladığım bash'ı arayarak kaçamadım.
sjas

4

Sisteminizi varsayılan olarak çoğullama ile tekli ssh oturumlarını kullanacak şekilde yapılandırmanın en kolay yolu.

Bu, soketler için bir klasör oluşturarak yapılabilir:

mkdir ~/.ssh/controlmasters

Ve sonra .ssh yapılandırmanıza aşağıdakileri ekleyin:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/controlmasters/%r@%h:%p.socket
    ControlMaster auto
    ControlPersist 10m

Artık kodunuzu değiştirmenize gerek yok. Bu, birden fazla oturum oluşturmadan ssh ve scp'ye birden fazla çağrı yapılmasına izin verir; bu, yerel ve uzak makineleriniz arasında daha fazla etkileşim olması gerektiğinde yararlıdır.

@ Terminus'un cevabı sayesinde, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ ve https://en.wikibooks.org / wiki / OpenSSH / Yemek Kitabı / Çoğullama .

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.