Bagaimana cara menggunakan SSH untuk menjalankan skrip shell pada mesin jarak jauh?

1223

Saya harus menjalankan skrip shell (windows / Linux) pada mesin jarak jauh.

Saya telah mengonfigurasi SSH di kedua mesin A dan B. Script saya ada di mesin A yang akan menjalankan beberapa kode saya di mesin jarak jauh, mesin B.

Komputer lokal dan jarak jauh dapat berupa sistem berbasis Windows atau Unix.

Apakah ada cara untuk menjalankan melakukan ini menggunakan plink / ssh?

alpha_989
sumber
6
Pertanyaan yang sama sudah ada di serverfault: serverfault.com/questions/215756/... Jadi mungkin tidak ada gunanya memigrasi pertanyaan ini.
sleske
9
Pertanyaan tentang Kesalahan Server tidak memiliki banyak jawaban. Mungkin pertanyaan ini harus menggantikan yang itu.
Big McLargeHuge
5
Saya suka jawaban ini secara pribadi: unix.stackexchange.com/questions/87405/…
mikevoermans
27
Selain itu harus jelas pada topik karena ssh adalah alat utama untuk pengembangan perangkat lunak.
static_rtti
4
Kopi dan pertanyaan ssh tidak memiliki tingkat topik yang sama di SO. Memilih untuk dibuka kembali.
Vincent Cantin

Jawaban:

1194

Jika Mesin A adalah kotak Windows, Anda dapat menggunakan Plink (bagian dari Putty ) dengan parameter -m, dan itu akan mengeksekusi skrip lokal di server jauh.

plink root@MachineB -m local_script.sh

Jika Mesin A adalah sistem berbasis Unix, Anda dapat menggunakan:

ssh root@MachineB 'bash -s' < local_script.sh

Anda tidak perlu menyalin skrip ke server jauh untuk menjalankannya.

Jason R. Coombs
sumber
11
apakah ada keuntungan menggunakan -sopsi ini? halaman manual ini membuat saya percaya bahwa itu akan memproses input standar ketika opsi pemrosesan selesai, apakah -sdigunakan atau tidak.
aeroNotAuto
79
Untuk skrip yang memerlukan sudo, jalankan ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers
6
@ bradley.ayers ingat untuk memulai perintah dengan 'spasi' untuk melewati sejarah (PS Anda harus HISTCONTROL=ignoreboth or ignorespacemembuatnya bekerja)
derenio
8
@ bradley.ayers dalam situasi apa Anda membutuhkan sudo jika Anda sudah masuk sebagai root?
Brian Schlenker
21
@ Agostino, Anda dapat menambahkan parameter seperti ini: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh Kredit sepenuhnya menuju ke jawaban @chubbsondubs di bawah ini.
Yves Van Broekhoven
635

Ini adalah pertanyaan lama, dan jawaban Jason berfungsi dengan baik, tetapi saya ingin menambahkan ini:

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Ini juga dapat digunakan dengan su dan perintah yang membutuhkan input pengguna. (perhatikan 'heredoc yang lolos)

Sunting: Karena jawaban ini terus mendapatkan sedikit lalu lintas, saya akan menambahkan lebih banyak info untuk penggunaan heredoc yang luar biasa ini:

Anda dapat menumpuk perintah dengan sintaks ini, dan itulah satu-satunya cara agar nesting berfungsi (dengan cara yang waras)

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

Anda sebenarnya dapat melakukan percakapan dengan beberapa layanan seperti telnet, ftp, dll. Tetapi ingat bahwa heredoc hanya mengirim stdin sebagai teks, ia tidak menunggu respons di antara baris

Sunting: Saya baru tahu bahwa Anda dapat membuat indentasi bagian dalam dengan tab jika Anda gunakan <<-END!

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(Saya pikir ini harus berhasil)

Juga lihat http://tldp.org/LDP/abs/html/here-docs.html

Yarek T
sumber
4
Anda dapat sedikit menyesuaikan waktu dengan menambahkan baris seperti: # $ (tidur 5)
Olivier Dulac
50
perhatikan bahwa dengan tanda kutip tunggal di sekitar terminator ( <<'ENDSSH'), string tidak akan diperluas, variabel tidak akan dievaluasi. Anda juga dapat menggunakan <<ENDSSHatau <<"ENDSSH"jika Anda ingin ekspansi.
maackle
3
Expectdapat digunakan ketika Anda perlu mengotomatisasi perintah interaktif seperti FTP.
programaths
5
Perhatikan bahwa saya punya Pseudo-terminal will not be allocated because stdin is not a terminal.pesan. Kita harus menggunakan ssh dengan -t -tparams untuk menghindari itu. Lihat utas
Buzut
8
Jika Anda mencoba menggunakan sintaks << - 'END', pastikan pembatas akhir heredoc Anda diindentasi menggunakan TAB, bukan spasi. Perhatikan bahwa salin / tempel dari stackexchange akan memberi Anda ruang. Ubah itu menjadi tab dan fitur indentasi harus berfungsi.
fbicknel
249

Juga, jangan lupa untuk keluar dari variabel jika Anda ingin mengambilnya dari host tujuan.

Ini telah menangkap saya di masa lalu.

Sebagai contoh:

user@host> ssh user2@host2 "echo \$HOME"

mencetak / home / user2

sementara

user@host> ssh user2@host2 "echo $HOME"

mencetak / home / user

Contoh lain:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

mencetak "halo" dengan benar.

dogbane
sumber
2
Namun waspadai hal-hal berikut: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info
1
Hanya untuk menambahkan bahwa dalam forloop berjalan di sshsesi, variabel loop tidak boleh diloloskan.
AlexeyDaryin
1
Dalam banyak situasi, cara yang waras untuk memperbaiki contoh terakhir Anda adalah ssh user2@host2 'echo hello world' | awk '{ print $1 }'menjalankan skrip Awk secara lokal. Jika perintah jarak jauh menghasilkan keluaran yang sangat besar, Anda tentu ingin menghindari menyalin semuanya kembali ke server lokal. Kebetulan, kutipan tunggal di sekitar perintah jarak jauh menghindari kebutuhan untuk melarikan diri.
tripleee
151

Ini adalah ekstensi untuk jawaban YarekT untuk menggabungkan perintah remote inline dengan meneruskan variabel ENV dari mesin lokal ke host jarak jauh sehingga Anda dapat membuat parameter skrip Anda di sisi jarak jauh:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

Saya menemukan ini sangat membantu dengan menyimpan semuanya dalam satu skrip sehingga sangat mudah dibaca dan dikelola.

Kenapa ini bekerja? ssh mendukung sintaks berikut:

ssh user @ host remote_command

Dalam bash kita bisa menentukan variabel lingkungan untuk didefinisikan sebelum menjalankan perintah pada satu baris seperti:

ENV_VAR_1 = 'value1' ENV_VAR_2 = 'value2' bash -c 'echo $ ENV_VAR_1 $ ENV_VAR_2'

Itu membuatnya mudah untuk mendefinisikan variabel sebelum menjalankan perintah. Dalam hal ini gema adalah perintah kami yang kami jalankan. Segala sesuatu sebelum gema mendefinisikan variabel lingkungan.

Jadi kami menggabungkan kedua fitur dan jawaban YarekT untuk mendapatkan:

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

Dalam hal ini kami menetapkan ARG1 dan ARG2 ke nilai lokal. Mengirim semuanya setelah pengguna @ host sebagai perintah remote_. Ketika mesin jarak jauh mengeksekusi perintah ARG1 dan ARG2 diatur nilai-nilai lokal, berkat evaluasi baris perintah lokal, yang mendefinisikan variabel lingkungan pada server jauh, kemudian mengeksekusi perintah bash -s menggunakan variabel-variabel tersebut. Voila.

chubbsondubs
sumber
1
Perhatikan bahwa jika Anda ingin memberikan argumen seperti -a maka Anda dapat menggunakan -. misalnya 'ssh user @ host - -a foo bar' bash -s '<script.sh'. Dan args juga dapat mengikuti redirect misalnya 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
gaoithe
8
Jika salah satu nilai env var berisi spasi, gunakan:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle
Seperti yang saya tulis di komentar lain, inti dari penggunaan -sadalah untuk dapat menerapkan argumen ke skrip yang bersumber melalui stdin. Maksud saya, Anda bisa menghilangkannya jika Anda tidak akan menggunakannya. Jika Anda menggunakannya, tidak ada alasan untuk menggunakan variabel lingkungan:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL
104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Itu akan meminta Anda untuk kata sandi, kecuali jika Anda telah menyalin kunci publik hostA pengguna ke file Authorized_key di rumah direktori .ssh pengguna. Itu akan memungkinkan otentikasi tanpa kata sandi (jika diterima sebagai metode auth pada konfigurasi server ssh)

Vinko Vrsalovic
sumber
3
Memilih Anda. Ini adalah solusi yang valid. Jelas, kunci harus dilindungi, tetapi mereka juga dapat dibatalkan seperti kata sandi melalui sisi server.
willasaywhat
8
Saya tidak berpikir ini menjawab pertanyaan. Contoh menunjukkan bagaimana menjalankan perintah jarak jauh, tetapi tidak bagaimana menjalankan skrip lokal pada mesin jarak jauh.
Jason R. Coombs
Tidak yakin tetapi tidak bisakah Anda mem-pipe script Anda pada hostA untuk berjalan di hostB menggunakan metode ini?
nevets1219
27

Saya sudah mulai menggunakan Fabric untuk operasi yang lebih canggih. Fabric membutuhkan Python dan beberapa dependensi lainnya, tetapi hanya pada mesin klien. Server hanya perlu server ssh. Saya menemukan alat ini jauh lebih kuat daripada skrip shell yang diserahkan ke SSH, dan layak kesulitan untuk mengatur (terutama jika Anda menikmati pemrograman dengan Python). Fabric menangani skrip yang berjalan pada beberapa host (atau host peran tertentu), membantu memfasilitasi operasi idempoten (seperti menambahkan baris ke skrip konfigurasi, tetapi tidak jika sudah ada di sana), dan memungkinkan konstruksi logika yang lebih kompleks (seperti Python bahasa dapat menyediakan).

Jason R. Coombs
sumber
11

Coba jalankan ssh user@remote sh ./script.unx.

Jeremy
sumber
8
Ini hanya berfungsi jika skrip berada di direktori default (home) pada remote. Saya pikir pertanyaannya adalah bagaimana menjalankan skrip yang disimpan secara lokal di remote.
metasim
1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "path ke sh file di host jarak jauh"
mani deepak
10
cat ./script.sh | ssh <user>@<host>
cglotr
sumber
1
Sangat menarik ...
Noah Broyles
5

Saya menggunakan ini untuk menjalankan skrip shell pada mesin jarak jauh (diuji pada / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh
Apakah Ma
sumber
3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

sangat disarankan untuk sumber file lingkungan (.bashrc / .bashprofile / .profile). sebelum menjalankan sesuatu di host jarak jauh karena target dan sumber variabel lingkungan host mungkin deffer.

Ankush Sahu
sumber
Ini tidak menjelaskan cara memindahkan skrip lokal ke host jarak jauh.
kirelagin
2

jika Anda ingin menjalankan perintah seperti ini temp=`ls -a` echo $temp di `` akan menyebabkan kesalahan.

perintah di bawah ini akan menyelesaikan masalah ini ssh user@host ''' temp=`ls -a` echo $temp '''

Jinmiao Luo
sumber
1

Jawabannya di sini ( https://stackoverflow.com/a/2732991/4752883 ) berfungsi dengan baik jika Anda mencoba menjalankan skrip pada mesin linux jarak jauh menggunakan plinkatau ssh. Ini akan berfungsi jika skrip memiliki beberapa baris aktif linux.

** Namun, jika Anda mencoba menjalankan skrip batch yang terletak di linux/windowsmesin lokal dan mesin jarak jauh Anda Windows, dan itu terdiri dari beberapa baris menggunakan **

plink root@MachineB -m local_script.bat

tidak akan bekerja.

Hanya baris pertama skrip yang akan dieksekusi. Ini mungkin batasan plink.

Solusi 1:

Untuk menjalankan skrip kumpulan multiline (terutama jika itu relatif sederhana, terdiri dari beberapa baris):

Jika skrip batch asli Anda adalah sebagai berikut

cd C:\Users\ipython_user\Desktop 
python filename.py

Anda dapat menggabungkan garis-garis tersebut bersama-sama menggunakan pemisah "&&" seperti berikut dalam local_script.batfile Anda : https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Setelah perubahan ini, Anda kemudian dapat menjalankan skrip seperti yang ditunjukkan di sini oleh @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 dengan:

`plink root@MachineB -m local_script.bat`

Solusi 2:

Jika skrip batch Anda relatif rumit, mungkin lebih baik menggunakan skrip batch yang merangkum perintah plink dan juga seperti yang ditunjukkan di sini oleh @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
sumber
1

Script bash ini ssh menjadi mesin remote target, dan menjalankan beberapa perintah di mesin remote, jangan lupa untuk menginstal berharap sebelum menjalankannya (di mac brew install expect)

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
Mohammed Rafeeq
sumber
1
ini bagus jika Anda harus menggunakan kata sandi ... namun untuk kepentingan siapa pun yang menonton di rumah perintah ssh harus menggunakan sepasang kunci publik + pribadi bukan kata sandi ... sekali perbarui server ssh Anda untuk mematikan kata sandi sepenuhnya
Scott Stensland
-1

Anda dapat menggunakan runoverssh :

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s menjalankan skrip lokal dari jarak jauh


Bendera yang berguna:
-g gunakan kata sandi global untuk semua host (prompt kata sandi tunggal)
-ngunakan SSH alih-alih sshpass, berguna untuk otentikasi kunci publik

sekarang jam
sumber
-24

Pertama, salin skrip ke Mesin B menggunakan scp

[user @ machineA] $ scp / path / ke / script user @ machineB: / home / user / path

Kemudian, jalankan saja skripnya

[user @ machineA] $ ssh user @ machineB "/ home / user / path / script"

Ini akan berfungsi jika Anda telah memberikan izin yang dapat dieksekusi ke skrip.

Baishampayan Ghose
sumber
hai saya menerapkan saran direkomendasikan tetapi memberi saya kesalahan berikut [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: Nama atau layanan tidak dikenal [oracle @ node1 ~] $
'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Baris perintah salah, nama perintah harus dipisahkan dari bagian user @ host dengan spasi, bukan titik dua.
bortzmeyer
5
Saya downvoting ini karena itu klaim utama bahwa itu tidak dapat dijalankan tanpa menyalinnya salah
Jason R. Coombs
Yang akan membantu adalah Jason menambahkan mengapa itu tidak benar dan bukan hanya menyatakan fakta. Tidak membantu.
Kris
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / ke / script
Oleksii Kyslytsyn