Apa cara terbersih untuk ssh dan menjalankan banyak perintah di Bash?

349

Saya sudah menyiapkan ssh agent, dan saya bisa menjalankan perintah pada server eksternal dalam skrip Bash melakukan hal-hal seperti:

ssh blah_server "ls; pwd;"

Sekarang, yang ingin saya lakukan adalah menjalankan banyak perintah panjang di server eksternal. Menutup semua ini di antara tanda kutip akan sangat jelek, dan saya lebih suka menghindari ssh'ing beberapa kali hanya untuk menghindari ini.

Jadi, adakah cara saya bisa melakukan ini dalam satu tanda kurung atau semacamnya? Saya mencari sesuatu di sepanjang baris:

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

Pada dasarnya, saya akan senang dengan solusi apa pun asalkan bersih.

Edit

Untuk memperjelas, saya berbicara tentang ini menjadi bagian dari skrip bash yang lebih besar. Orang lain mungkin perlu berurusan dengan script di telepon, jadi saya ingin tetap bersih. Saya tidak ingin memiliki skrip bash dengan satu baris yang terlihat seperti:

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

karena sangat jelek dan sulit dibaca.

Eli
sumber
2
Hmm, bagaimana kalau memasukkan semua itu ke dalam skrip di server dan hanya memanggilnya dengan satu sshpermintaan?
Nikolai Fetissov
@Nikolai jika perintah tergantung pada sisi client, mereka dapat ditulis ke dalam shell script, kemudian scp, ssh, dan lari. Ini akan menjadi cara terbersih, saya pikir.
khachik
6
Ini adalah bagian dari skrip bash yang lebih besar, jadi saya lebih suka tidak membaginya dengan setengah hidup di komputer pribadi saya dan setengah lainnya tinggal di server dan dijalankan melalui ssh. Jika memungkinkan, saya benar-benar ingin menyimpannya sebagai satu skrip dijalankan dari komputer pribadi saya. Apakah benar-benar tidak ada cara bersih untuk membungkus sekelompok perintah dalam ssh?
Eli
Cara terbaik adalah tidak menggunakan bash tetapi Perl, Python, Ruby, dll.
salva
1
Mengapa Anda ingin menghindari menempatkan perintah jarak jauh dalam tanda kutip? Anda dapat memiliki baris baru di dalam tanda kutip, sebanyak yang Anda suka; dan menggunakan string sebagai ganti input standar berarti input standar tersedia untuk mis. membaca input ke skrip jarak jauh. (Meskipun di Unix, tanda kutip tunggal biasanya lebih disukai daripada tanda kutip ganda, kecuali jika Anda secara spesifik memerlukan shell lokal untuk mengevaluasi beberapa bagian dari string.)
tripleee

Jawaban:

456

Bagaimana dengan Dokumen Bash Here :

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

Untuk menghindari masalah yang disebutkan oleh @Globalz di komentar, Anda mungkin dapat (tergantung apa yang Anda lakukan di situs jarak jauh) untuk mengganti baris pertama dengan

ssh otherhost /bin/bash << EOF

Perhatikan bahwa Anda dapat melakukan substitusi variabel dalam dokumen Here, tetapi Anda mungkin harus berurusan dengan masalah mengutip. Misalnya, jika Anda mengutip "batas string" (mis. EOFDi atas), maka Anda tidak dapat melakukan pergantian variabel. Tetapi tanpa mengutip string batas, variabel diganti. Sebagai contoh, jika Anda telah mendefinisikan $NAMEskrip shell di atas, Anda dapat melakukannya

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

dan itu akan membuat file di tujuan otherhostdengan nama apa pun yang Anda ditugaskan$NAME . Aturan lain tentang mengutip skrip shell juga berlaku, tetapi terlalu rumit untuk masuk ke sini.

Paul Tomblin
sumber
6
Ini persis seperti yang saya inginkan! Bagaimana cara kerjanya? Apakah Anda memiliki tautan ke halaman yang menjelaskannya?
Eli
9
+1 Sedang berpikir sendiri - inilah satu tempat untuk membacanya: tldp.org/LDP/abs/html/here-docs.html
bosmacs
14
Saya juga mendapatkan output ini ke lokal saya: Pseudo-terminal tidak akan dialokasikan karena stdin bukan terminal.
Globalz
14
Mungkin penting bagi Anda untuk mengutip kata (yaitu 'EOF' pertama) untuk mencegah perluasan baris perintah. Lihat: man bash | less + / 'Here Documents'
assem
6
Paul, saya hanya menyarankan itu karena dengan hampir 200k pandangan tentang pertanyaan ini, sepertinya banyak orang datang ke sini ketika menulis dengan ssh. Merupakan hal yang biasa untuk menyuntikkan nilai saat membuat skrip. Kalau bukan karena kebisingan di pertanyaan dan komentar lain saya hanya akan membuat satu dan berada di jalan saya, tetapi tidak mungkin terlihat pada tahap ini. Catatan kaki satu baris mungkin menyelamatkan orang dari sakit kepala besar.
koyae
122

Edit skrip Anda secara lokal, lalu kirimkan ke ssh, mis

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

di mana commands-to-execute-remotely.shterlihat seperti daftar Anda di atas:

ls some_folder
./someaction.sh
pwd;
bosmac
sumber
7
Ini memiliki keuntungan besar bahwa Anda tahu persis apa yang sedang dieksekusi oleh skrip jarak jauh - tidak ada masalah dengan mengutip. Jika Anda memerlukan perintah dinamis, Anda dapat menggunakan skrip shell dengan subshell, masih memipis ke ssh, yaitu ( echo $mycmd $myvar ; ...) | ssh myhost- seperti halnya dengan penggunaan cat, Anda tahu persis apa yang masuk ke aliran perintah ssh. Dan tentu saja subkulit dalam skrip dapat multi-line untuk keterbacaan - lihat linuxjournal.com/content/bash-sub-shells
RichVel
6
Bisakah Anda melakukan ini dengan argumen di commands-to-execute-remotely.sh?
elaRosca
1
Saya pikir ini jauh lebih berguna daripada solusi paultomblin. Karena skrip dapat dialihkan ke server ssh tanpa harus membuka file. Juga jauh lebih mudah dibaca.
Patrick Bassut
3
ya, tetapi menggunakan gema atau dokumen di sini (lihat jawaban atas): gunakan: $localvaruntuk menginterpretasikan variabel yang ditentukan secara lokal, \$remotevaruntuk menginterpretasikan dari jauh variabel yang ditentukan dari jarak jauh, \$(something with optionnal args)untuk mendapatkan output dari sesuatu yang dieksekusi pada server jauh. Contoh yang dapat Anda ssh melalui 1 (atau, seperti yang ditunjukkan di sini, beberapa perintah ssh):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 ... bagaimana ini berbeda dari menggunakan pengalihan input, yaitu ssh blah_server < commands-to-execute-remotely.sh?
flow2k
41

Untuk mencocokkan kode sampel Anda, Anda dapat membungkus perintah Anda di dalam qoutes tunggal atau ganda. Sebagai contoh

ssh blah_server "
  ls
  pwd
"
Andrei B
sumber
Saya suka format ini, namun sayangnya tidak berguna untuk menyimpan data std menjadi variabel.
Signus
1
Signus, apa yang Anda maksud dengan "menyimpan data std ke dalam variabel"?
Andrei B
1
@Signus Sangat mungkin untuk melakukan apa yang Anda gambarkan, meskipun Anda mungkin ingin menggunakan tanda kutip tunggal alih-alih tanda kutip ganda di sekitar perintah jarak jauh (atau melarikan diri dari operator yang perlu melarikan diri dalam tanda kutip ganda untuk mencegah shell lokal Anda menyadap dan interpolasi mereka).
tripleee
Saya tidak dapat memasukkan kode tanda kutip ganda perintah seperti ini fileToRemove = $ (temukan. -Type f -name 'xxx.war'). fileToRemove harus memiliki nama file di dalamnya tetapi sebaliknya memiliki string kosong. Apakah sesuatu perlu diloloskan?
jkonst
37

Saya melihat dua cara:

Pertama, Anda membuat soket kontrol seperti ini:

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

dan jalankan perintah Anda

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

Dengan cara ini Anda dapat menulis perintah ssh tanpa benar-benar terhubung kembali ke server.

Yang kedua adalah menghasilkan skrip secara dinamis, scpdan menjalankannya.

ujung
sumber
20

Ini juga dapat dilakukan sebagai berikut. Masukkan perintah Anda dalam sebuah skrip, beri nama itu perintah-inc.sh

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

Simpan file

Sekarang jalankan di server jauh.

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

Tidak pernah gagal untukku.

RJ
sumber
Mirip dengan apa yang saya pikirkan awalnya! Tetapi mengapa bash -sdibutuhkan?
flow2k
Juga, apakah #!/bin/bashbenar - benar digunakan?
flow2k
2
-S ada untuk kompatibilitas. Dari man bash -s Jika opsi -s hadir, atau jika tidak ada argumen yang tersisa setelah pemrosesan opsi, maka perintah dibaca dari input standar. Opsi ini memungkinkan parameter posisi diatur ketika menjalankan shell interaktif.
RJ
1
RJ, terima kasih! Dengan komentar pertama saya, sepertinya kami baru saja melakukannya ssh user@remote < /path/to/commands-inc.sh(sepertinya berhasil untuk saya). Yah, saya kira versimu memastikan kita menggunakan bashshell, dan bukan shell lain - itulah tujuannya, bukan?
flow2k
2
Terima kasih. Yap, beberapa dari kita memiliki catatan kita, dan kebiasaan dari versi lama bash dan cangkang lainnya. :-)
RJ
10

Masukkan semua perintah ke skrip dan itu bisa dijalankan seperti

ssh <remote-user>@<remote-host> "bash -s" <./remote-commands.sh
Jai Prakash
sumber
Agak aneh tapi berfungsi dengan baik. Dan args dapat diteruskan ke skrip (baik sebelum atau sesudah redirect). misalnya 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2' misalnya 'ssh <remote-user> @ <remote-host> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
gaoithe
Saya memiliki pertanyaan serupa dengan jawaban lain di atas, tetapi apa tujuan memasukkan bash -s?
flow2k
1
@ flow2k -s adalah untuk menunjukkan bash untuk membaca perintah dari input standar. Berikut adalah deskripsi dari manhalamanIf 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 Terima kasih untuk ini, tapi saya benar-benar berpikir jika kita bisa menghapus bashperintahnya sama sekali, yaitu ssh remote-user@remote-host <./remote-commands.sh. Saya mencoba cara saya dan tampaknya berhasil, meskipun saya tidak yakin apakah itu kurang dalam beberapa cara.
flow2k
@ flow2k dari pemahaman saya tentang halaman manual tidak akan ada kekurangan tanpa opsi itu. Diperlukan jika Anda mencoba menyampaikan argumen apa pun. - terima kasih
Jai Prakash
9

SSH dan Jalankan Banyak Perintah di Bash.

Pisahkan perintah dengan titik koma di dalam string, diteruskan ke gema, semua disalurkan ke perintah ssh. Sebagai contoh:

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
sumber
arnab, di masa depan, tolong jelaskan apa yang kode Anda lakukan secara singkat. Kemudian bicarakan cara kerjanya, lalu masukkan kodenya. Kemudian masukkan kode ke dalam blok kode sehingga mudah dibaca.
Eric Leschinski
Dari pertanyaan, Anda menawarkan persis apa yang ingin OP hindari: "Saya tidak ingin memiliki skrip bash dengan satu baris yang terlihat seperti ..."
jww
6

Bagi siapa pun yang tersandung di sini seperti saya - Saya berhasil lolos dari titik koma dan baris baru:

Langkah pertama: titik koma. Dengan cara ini, kami tidak melanggar perintah ssh:

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

Mencantumkan direktori host / home jarak jauh (login sebagai root), sedangkan

ssh <host> echo test;ls
                    ^ NO backslash

terdaftar direktori kerja saat ini.

Langkah selanjutnya: memecah baris:

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

Ini sekali lagi mencantumkan direktori kerja jarak jauh - pemformatan yang ditingkatkan:

ssh <host>\
  echo test\;\
  ls

Jika benar-benar lebih bagus daripada di sini, dokumentasikan atau kutip di sekitar garis terputus - yah, bukan saya yang memutuskan ...

(Menggunakan bash, Ubuntu 14.04 LTS.)

Aconcagua
sumber
1
Apa yang lebih baik lagi yang bisa Anda gunakan && dan || dengan cara ini juga - gema test \ & \ & ls
Miro Kropacek
@MiroKropacek Itu bagus juga. Lebih baik? Tergantung pada apa yang Anda inginkan; jalankan perintah kedua secara kondisional , lalu ya, jalankan tanpa syarat (tidak peduli apakah yang pertama berhasil atau gagal), maka tidak ...
Aconcagua
@MiroKropacek, saya tidak perlu melarikan diri dari && ketika meletakkan tanda kutip ganda di sekitar perintah:ssh host "echo test && ls"
not2savvy
5

Jawaban yang diposting menggunakan string multiline dan beberapa skrip bash tidak berfungsi untuk saya.

  • Senar multiline panjang sulit dipertahankan.
  • Skrip bash terpisah tidak mempertahankan variabel lokal.

Berikut adalah cara fungsional untuk ssh dan menjalankan beberapa perintah sambil menjaga konteks lokal.

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"
Michael Petrochuk
sumber
3
Anda berpikir tentang ini seolah-olah satu-satunya alasan seseorang ingin melakukan ini adalah jika mereka duduk di baris perintah. Ada alasan umum lainnya untuk pertanyaan ini juga, seperti mengeksekusi perintah di lingkungan terdistribusi dengan alat-alat seperti rundeck, jenkins, dll.
Charles Addis
ini berfungsi tetapi saya menerima pesan kesalahan yang tidak diinginkan
Annahri
5

Tidak yakin apakah yang terbersih untuk perintah yang panjang tetapi yang paling mudah:

ssh user@host "cmd1; cmd2; cmd3"
DimiDak
sumber
Terbaik dalam kesederhanaan!
not2savvy
4

Ini berfungsi baik untuk membuat skrip, karena Anda tidak harus memasukkan file lain:

#!/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
sja
sumber
Bagian "$ (yang bash) -s" memberi Anda lokasi bash pada mesin lokal, bukan mesin jarak jauh. Saya pikir Anda ingin '$ (yang bash) -s' sebagai gantinya (tanda kutip tunggal untuk menekan substitusi parameter lokal).
jsears
1
Paul Tomblin memberikan jawaban Bash Here Document 6 tahun sebelumnya. Nilai apa yang ditambahkan oleh jawaban ini?
jww
-sbendera, saya mengutip dia Anda mungkin bisa pergi dengan mengganti baris pertama dengan ... , dan saya tidak bisa pergi dengan hanya memanggil bash saya samar-samar ingat.
sjas
4

Cara termudah untuk mengonfigurasi sistem Anda untuk menggunakan sesi ssh tunggal secara default dengan multiplexing.

Ini dapat dilakukan dengan membuat folder untuk soket:

mkdir ~/.ssh/controlmasters

Dan kemudian menambahkan yang berikut ini ke konfigurasi .ssh Anda:

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

Sekarang, Anda tidak perlu mengubah kode apa pun. Ini memungkinkan beberapa panggilan ke ssh dan scp tanpa membuat beberapa sesi, yang berguna ketika perlu ada lebih banyak interaksi antara mesin lokal dan jarak jauh Anda.

Terima kasih untuk jawaban @ terminus, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/ dan https://en.wikibooks.org / wiki / OpenSSH / Cookbook / Multiplexing .

Jonathan
sumber