Làm cách nào để sử dụng SSH để chạy tập lệnh shell trên máy từ xa?


1223

Tôi phải chạy một kịch bản shell (windows / Linux) trên một máy từ xa.

Tôi đã cấu hình SSH trên cả máy A và B. Tập lệnh của tôi nằm trên máy A sẽ chạy một số mã của tôi trên máy từ xa, máy B.

Các máy tính cục bộ và từ xa có thể là hệ thống dựa trên Windows hoặc Unix.

Có cách nào để chạy làm điều này bằng cách sử dụng plink / ssh không?


6
Câu hỏi tương tự đã có trên serverfault: serverfault.com/questions/215756/ Khăn Vì vậy, có lẽ không có điểm nào trong việc di chuyển câu hỏi này.
sleske

9
Câu hỏi trên Server Fault không có nhiều câu trả lời. Có lẽ câu hỏi này nên thay thế câu hỏi đó.
Big McLargeHuge

5
Tôi thích câu trả lời này một cách cá nhân: unix.stackexchange.com/questions/87405/ Khăn
mikevoermans

27
Hơn nữa, nó rõ ràng nên có chủ đề vì ssh là một công cụ chính để phát triển phần mềm.
static_rtti

4
Các câu hỏi về cà phê và ssh không chia sẻ cùng một mức độ lạc đề về SO. Bình chọn để mở lại.
Vincent Cantin

Câu trả lời:


1194

Nếu Máy A là hộp Windows, bạn có thể sử dụng Plink (một phần của PuTTY ) với tham số -m và nó sẽ thực thi tập lệnh cục bộ trên máy chủ từ xa.

plink root@MachineB -m local_script.sh

Nếu Máy A là một hệ thống dựa trên Unix, bạn có thể sử dụng:

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

Bạn không cần phải sao chép tập lệnh vào máy chủ từ xa để chạy tập lệnh.


11
Có một lợi thế để sử dụng -stùy chọn? trang hướng dẫn này khiến tôi tin rằng nó sẽ xử lý đầu vào tiêu chuẩn khi hoàn thành các tùy chọn xử lý, cho dù có -sđược sử dụng hay không.
aeroNotAuto

79
Đối với một kịch bản yêu cầu sudo, chạy ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
bradley.ayers

6
@ bradley.ayers nhớ bắt đầu lệnh bằng 'khoảng trắng' để bỏ qua lịch sử (PS bạn cần phải HISTCONTROL=ignoreboth or ignorespacelàm cho nó hoạt động)
derenio

8
@ bradley.ayers trong những tình huống bạn sẽ cần sudo nếu bạn đã đăng nhập với quyền root?
Brian Schlenker

21
@Agostino, bạn có thể thêm các tham số như thế này: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh Tín dụng hoàn toàn đi đến câu trả lời của @chubbsondub bên dưới.
Yves Van Broekhoven

635

Đây là một câu hỏi cũ và câu trả lời của Jason hoạt động tốt, nhưng tôi muốn thêm vào đây:

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

Điều này cũng có thể được sử dụng với su và các lệnh yêu cầu đầu vào của người dùng. (lưu ý các di sản 'đã trốn thoát)

Chỉnh sửa: Vì câu trả lời này tiếp tục nhận được một chút lưu lượng truy cập, tôi sẽ thêm nhiều thông tin hơn vào việc sử dụng tuyệt vời này của heredoc:

Bạn có thể lồng các lệnh với cú pháp này và đó là cách duy nhất lồng nhau dường như hoạt động (theo cách lành mạnh)

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

Bạn thực sự có thể có một cuộc trò chuyện với một số dịch vụ như telnet, ftp, v.v. Nhưng hãy nhớ rằng heredoc chỉ gửi stdin dưới dạng văn bản, nó không chờ phản hồi giữa các dòng

Chỉnh sửa: Tôi vừa phát hiện ra rằng bạn có thể thụt lề bên trong bằng các tab nếu bạn sử dụng <<-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

(Tôi nghĩ rằng điều này sẽ làm việc)

Đồng thời xem http://tldp.org/LDP/abs/html/here-docs.html


4
bạn có thể tạm thời một chút bằng cách thêm các dòng như: # $ (ngủ 5)
Olivier Dulac

50
lưu ý rằng với các dấu ngoặc đơn xung quanh terminator ( <<'ENDSSH'), các chuỗi sẽ không được mở rộng, các biến sẽ không được đánh giá. Bạn cũng có thể sử dụng <<ENDSSHhoặc <<"ENDSSH"nếu bạn muốn mở rộng.
bẻ khóa

3
Expectcó thể được sử dụng khi bạn cần tự động hóa các lệnh tương tác như FTP.
chương trình

5
Lưu ý rằng tôi đã có một Pseudo-terminal will not be allocated because stdin is not a terminal.tin nhắn. Người ta phải sử dụng ssh với -t -tparams để tránh điều đó. Xem chủ đề
Buzut

8
Nếu bạn đang cố gắng sử dụng cú pháp << - 'END', hãy đảm bảo dấu phân cách kết thúc di truyền của bạn được thụt lề bằng TAB, không phải dấu cách. Lưu ý rằng sao chép / dán từ stackexchange sẽ cung cấp cho bạn không gian. Thay đổi chúng thành các tab và tính năng thụt lề sẽ hoạt động.
fbicknel

249

Ngoài ra, đừng quên thoát các biến nếu bạn muốn chọn chúng từ máy chủ đích.

Điều này đã bắt tôi ra trong quá khứ.

Ví dụ:

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

in ra / nhà / người dùng2

trong khi

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

in ra / nhà / người dùng

Một vi dụ khac:

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

in ra "xin chào" chính xác.


2
Tuy nhiên, hãy lưu ý những điều sau: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info

1
Chỉ cần thêm rằng trong forcác vòng lặp chạy trong sshphiên, biến vòng lặp không được thoát.
AlexeyDaryin

1
Trong nhiều tình huống, cách lành mạnh để sửa ví dụ cuối cùng của bạn sẽ ssh user2@host2 'echo hello world' | awk '{ print $1 }'là chạy tập lệnh Awk cục bộ. Tất nhiên, nếu lệnh từ xa tạo ra vô số đầu ra, bạn muốn tránh sao chép tất cả trở lại máy chủ cục bộ. Ngẫu nhiên, các trích dẫn đơn xung quanh lệnh từ xa tránh sự cần thiết cho bất kỳ thoát.
tripleee

151

Đây là phần mở rộng cho câu trả lời của YarekT để kết hợp các lệnh từ xa nội tuyến với việc chuyển các biến ENV từ máy cục bộ sang máy chủ từ xa để bạn có thể tham số hóa các tập lệnh của mình ở phía xa:

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

Tôi thấy điều này đặc biệt hữu ích bằng cách giữ tất cả trong một tập lệnh để nó rất dễ đọc và có thể duy trì.

Tại sao điều này hoạt động. ssh hỗ trợ cú pháp sau:

người dùng ssh @ host remote_command

Trong bash, chúng ta có thể chỉ định các biến môi trường để xác định trước khi chạy lệnh trên một dòng như vậy:

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

Điều đó giúp dễ dàng xác định các biến trước khi chạy lệnh. Trong trường hợp này echo là lệnh của chúng tôi đang chạy. Tất cả mọi thứ trước khi echo xác định các biến môi trường.

Vì vậy, chúng tôi kết hợp hai tính năng đó và câu trả lời của YarekT để có được:

người dùng ssh @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

Trong trường hợp này, chúng tôi đang đặt ARG1 và ARG2 thành giá trị cục bộ. Gửi tất cả mọi thứ sau khi người dùng @ lưu trữ dưới dạng remote_command. Khi máy từ xa thực thi lệnh ARG1 và ARG2 được đặt các giá trị cục bộ, nhờ đánh giá dòng lệnh cục bộ, xác định các biến môi trường trên máy chủ từ xa, sau đó thực hiện lệnh bash -s bằng các biến đó. Voila.


1
Lưu ý rằng nếu bạn muốn vượt qua các đối số như -a thì bạn có thể sử dụng -. ví dụ: 'ssh user @ host - -a foo bar' bash -s '<script.sh'. Và các đối số cũng có thể đi sau chuyển hướng, ví dụ: 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
gaoithe

8
Nếu bất kỳ giá trị var env nào chứa khoảng trắng, hãy sử dụng:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

Giống như tôi đã viết trong một bình luận khác, điểm chính của việc sử dụng -slà có thể áp dụng các đối số cho các tập lệnh có nguồn gốc thông qua stdin. Ý tôi là, bạn cũng có thể bỏ qua nó nếu bạn sẽ không sử dụng nó. Nếu bạn sử dụng nó, không có lý do để sử dụng các biến môi trường:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL

104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Điều đó sẽ nhắc bạn nhập mật khẩu, trừ khi bạn đã sao chép khóa công khai của máy chủ lưu trữ của bạn vào tệp ủy quyền trên thư mục của người dùng .ssh. Điều đó sẽ cho phép xác thực không mật khẩu (nếu được chấp nhận là phương thức xác thực trên cấu hình của máy chủ ssh)


3
Bình chọn bạn lên. Đây là một giải pháp hợp lệ. Rõ ràng, các khóa phải được bảo vệ, nhưng chúng cũng có thể bị vô hiệu giống như mật khẩu thông qua phía máy chủ.
willasaywhat

8
Tôi không nghĩ rằng điều này trả lời câu hỏi. Ví dụ cho thấy cách chạy lệnh từ xa, nhưng không phải cách thực thi tập lệnh cục bộ trên máy từ xa.
Jason R. Coombs

Không chắc chắn nhưng bạn không thể chuyển tập lệnh của mình trên hostA để chạy trên hostB bằng phương pháp này?
nevets1219

27

Tôi đã bắt đầu sử dụng Fabric cho các hoạt động phức tạp hơn. Fabric yêu cầu Python và một vài phụ thuộc khác, nhưng chỉ trên máy khách. Máy chủ chỉ cần là một máy chủ ssh. Tôi thấy công cụ này mạnh hơn nhiều so với các tập lệnh shell được truyền cho SSH và rất đáng để cài đặt (đặc biệt nếu bạn thích lập trình bằng Python). Fabric xử lý các tập lệnh đang chạy trên nhiều máy chủ (hoặc máy chủ của một số vai trò nhất định), giúp tạo điều kiện thuận lợi cho các hoạt động bình thường (chẳng hạn như thêm một dòng vào tập lệnh cấu hình, nhưng không phải nếu nó đã có) và cho phép xây dựng logic phức tạp hơn (như Python ngôn ngữ có thể cung cấp).


11

Hãy thử chạy ssh user@remote sh ./script.unx.


8
Điều này chỉ hoạt động nếu tập lệnh nằm trong thư mục (nhà) mặc định trên điều khiển từ xa. Tôi nghĩ câu hỏi là làm thế nào để chạy một tập lệnh được lưu trữ cục bộ trên điều khiển từ xa.
metasim

1
ssh tên người dùng @ ip "chmod + x script.sh" <br/> tên người dùng ssh @ ip "đường dẫn đến tệp sh trong máy chủ từ xa"
mani deepak


8

Giả sử bạn có nghĩa là bạn muốn thực hiện việc này tự động từ máy "cục bộ", mà không cần đăng nhập thủ công vào máy "từ xa", bạn nên xem xét một tiện ích mở rộng TCL có tên là Expect, nó được thiết kế chính xác cho loại tình huống này. Tôi cũng đã cung cấp một liên kết đến một tập lệnh để đăng nhập / tương tác qua SSH.

https://www.nist.gov/service-resource/software/Exect

http://bash.cyberciti.biz/security/Exect-ssh-login-script/


5

Tôi sử dụng tập lệnh này để chạy tập lệnh shell trên máy từ xa (đã thử nghiệm trên / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh

3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

rất khuyến khích tìm nguồn tệp môi trường (.bashrc / .bashprofile / .profile). trước khi chạy một cái gì đó trong máy chủ từ xa bởi vì các biến môi trường của máy chủ đích và nguồn có thể bị lỗi.


Điều này không giải thích làm thế nào để di chuyển một tập lệnh cục bộ đến máy chủ từ xa.
kirelagin

2

nếu bạn muốn thực thi lệnh như thế này temp=`ls -a` echo $temp lệnh trong `` sẽ gây ra lỗi.

lệnh dưới đây sẽ giải quyết vấn đề này ssh user@host ''' temp=`ls -a` echo $temp '''


1

Câu trả lời ở đây ( https://stackoverflow.com/a/2732991/4752883 ) hoạt động rất tốt nếu bạn đang cố chạy tập lệnh trên máy linux từ xa bằng cách sử dụng plinkhoặc ssh. Nó sẽ hoạt động nếu tập lệnh có nhiều dòng trênlinux .

** Tuy nhiên, nếu bạn đang cố chạy một tập lệnh bó nằm trên một linux/windowsmáy cục bộ và máy từ xa của bạn Windows, và nó bao gồm nhiều dòng sử dụng **

plink root@MachineB -m local_script.bat

sẽ không làm việc

Chỉ dòng đầu tiên của tập lệnh sẽ được thực thi. Đây có lẽ là một hạn chế củaplink .

Giải pháp 1:

Để chạy tập lệnh bó đa dòng (đặc biệt nếu nó tương đối đơn giản, bao gồm một vài dòng):

Nếu tập lệnh bó ban đầu của bạn như sau

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

bạn có thể kết hợp các dòng lại với nhau bằng cách sử dụng dấu phân cách "&&" như sau trong local_script.battệp của mình : https://stackoverflow.com/a/8055390/4752883 :

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

Sau khi thay đổi này, bạn có thể chạy tập lệnh như được chỉ ra ở đây bởi @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 với:

`plink root@MachineB -m local_script.bat`

Giải pháp 2:

Nếu tập lệnh bó của bạn tương đối phức tạp, có thể tốt hơn là sử dụng tập lệnh bó để đóng gói lệnh plink cũng như được chỉ ra ở đây bởi @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

1

Tập lệnh bash này ssh vào một máy từ xa đích và chạy một số lệnh trong máy từ xa, đừng quên cài đặt mong đợi trước khi chạy nó (trên 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"

1
Điều này thật tuyệt nếu bạn phải sử dụng mật khẩu ... tuy nhiên vì lợi ích của bất kỳ ai xem tại nhà, bất kỳ lệnh ssh nào cũng nên sử dụng một cặp khóa công khai + khóa riêng không phải mật khẩu ... một lần cập nhật máy chủ ssh của bạn để tắt hoàn toàn mật khẩu
Scott Stensland

-1

Bạn có thể sử dụng runoverssh :

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

-s chạy một kịch bản cục bộ từ xa


Cờ hữu ích:
-g sử dụng mật khẩu chung cho tất cả máy chủ (dấu nhắc mật khẩu đơn)
-nsử dụng SSH thay vì sshpass, hữu ích cho xác thực khóa công khai


-24

Đầu tiên, sao chép tập lệnh qua Máy B bằng scp

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

Sau đó, chỉ cần chạy kịch bản

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

Điều này sẽ hoạt động nếu bạn đã cấp quyền thực thi cho tập lệnh.


hi tôi đã áp dụng suggession được đề xuất nhưng nó cho tôi lỗi sau [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / Conn.sh ssh: node2: ./ home / oracle / au / fs / Conn.sh: Tên hoặc dịch vụ không được biết đến [oracle @ node1 ~] $

'ssh oracle @ node2: ./ home / oracle / au / fs / Conn.sh'? Dòng lệnh sai, tên lệnh phải được tách khỏi phần user @ host với khoảng trắng, không phải dấu hai chấm.
bortzmeyer

5
Tôi đánh giá thấp điều này bởi vì tuyên bố chính rằng nó không thể chạy mà không sao chép nó là không chính xác.
Jason R. Coombs

Sẽ rất hữu ích khi Jason thêm vào lý do tại sao điều đó không chính xác thay vì chỉ nêu thực tế. Vô ích.
Kris

[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.