จะใช้ SSH เพื่อรันเชลล์สคริปต์บนเครื่องระยะไกลได้อย่างไร


1220

ฉันต้องรันเชลล์สคริปต์ (windows / Linux) บนเครื่องระยะไกล

ฉันมี SSH ที่กำหนดค่าไว้ในทั้งเครื่อง A และ B สคริปต์ของฉันอยู่บนเครื่อง A ซึ่งจะรันโค้ดบางส่วนของฉันบนเครื่องรีโมตเครื่อง B

คอมพิวเตอร์ในระบบและระยะไกลสามารถเป็นได้ทั้งระบบ Windows หรือ Unix

มีวิธีการทำเช่นนี้โดยใช้ plink / ssh หรือไม่


6
คำถามเดียวกันนี้มีอยู่ใน serverfault แล้ว: serverfault.com/questions/215756/ ......ดังนั้นอาจไม่มีประเด็นในการโยกย้ายคำถามนี้
sleske

9
คำถามเกี่ยวกับ Server Fault นั้นไม่มีคำตอบมากมาย บางทีคำถามนี้ควรแทนที่คำถามนั้น
Big McLargeHuge

5
ฉันชอบคำตอบนี้เป็นการส่วนตัว: unix.stackexchange.com/questions/87405/…
mikevoermans

27
นอกจากนี้ควรเห็นได้ชัดในหัวข้อเนื่องจาก ssh เป็นเครื่องมือสำคัญสำหรับการพัฒนาซอฟต์แวร์
static_rtti

4
คำถาม Coffee และ ssh ไม่ได้มีการแบ่งปันเนื้อหานอกหัวข้อในระดับเดียวกัน โหวตให้เปิดใหม่
Vincent Cantin

คำตอบ:


1192

หาก Machine A เป็นกล่อง Windows คุณสามารถใช้ Plink (ส่วนหนึ่งของPuTTY ) กับพารามิเตอร์ -m และมันจะรันสคริปต์โลคัลบนเซิร์ฟเวอร์ระยะไกล

plink root@MachineB -m local_script.sh

หาก Machine A เป็นระบบที่ใช้ Unix คุณสามารถใช้:

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

คุณไม่ควรคัดลอกสคริปต์ไปยังเซิร์ฟเวอร์ระยะไกลเพื่อเรียกใช้


11
มีข้อได้เปรียบในการใช้-sตัวเลือกนี้หรือไม่? หน้าคนนี้ทำให้ฉันเชื่อว่ามันจะประมวลผลอินพุตมาตรฐานเมื่อมันเสร็จสิ้นตัวเลือกการประมวลผลไม่ว่า-sจะใช้หรือไม่ก็ตาม
aeroNotAuto

79
สำหรับสคริปต์ที่ต้องวิ่งsudo ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh
bradley.ayers

6
@ bradley.ayers จำที่จะเริ่มต้นคำสั่งที่มีพื้นที่ 'เพื่อข้ามประวัติศาสตร์ (PS คุณจำเป็นต้องมีHISTCONTROL=ignoreboth or ignorespaceเพื่อให้ทำงาน)
derenio

8
@ bradley.ayers ในสถานการณ์ใดบ้างที่คุณต้องใช้ sudo หากคุณเข้าสู่ระบบในฐานะ root แล้ว?
Brian Schlenker

21
@Agostino คุณสามารถเพิ่มพารามิเตอร์เช่นนี้: ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh เครดิตอย่างเต็มที่ไปที่ @chubbsondubs 'คำตอบด้านล่าง
Yves Van Broekhoven

633

นี่เป็นคำถามเก่าและคำตอบของ Jason ทำงานได้ดี แต่ฉันต้องการเพิ่มสิ่งนี้:

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

สิ่งนี้สามารถใช้กับ su และคำสั่งที่ต้องการอินพุตผู้ใช้ (บันทึก'heredoc ที่หลบหนี)

แก้ไข: เนื่องจากคำตอบนี้ยังคงได้รับปริมาณการใช้งานฉันจะเพิ่มข้อมูลเพิ่มเติมเพื่อใช้ heredoc:

คุณสามารถซ้อนคำสั่งด้วยไวยากรณ์นี้และนั่นเป็นวิธีเดียวในการซ้อนที่ดูเหมือนว่าจะทำงาน (ในลักษณะที่มีสติ)

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

คุณสามารถสนทนากับบริการบางอย่างเช่น telnet, ftp และอื่น ๆ ได้ แต่โปรดจำไว้ว่า heredoc เพียงแค่ส่ง stdin เป็นข้อความมันไม่รอการตอบสนองระหว่างบรรทัด

แก้ไข: ฉันเพิ่งพบว่าคุณสามารถเยื้อง insides ด้วยแท็บถ้าคุณใช้<<-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

(ฉันคิดว่านี่น่าจะใช้ได้)

ดูที่ http://tldp.org/LDP/abs/html/here-docs.html ด้วย


4
คุณสามารถทำให้เป็นการเปลี่ยนแปลงเล็กน้อยโดยการเพิ่มบรรทัดเช่น: # $ (sleep 5)
Olivier Dulac

50
โปรดทราบว่าด้วยเครื่องหมายอัญประกาศเดี่ยวรอบตัวสิ้นสุด ( <<'ENDSSH') สตริงจะไม่ถูกขยายตัวแปรจะไม่ถูกประเมิน คุณสามารถใช้<<ENDSSHหรือ<<"ENDSSH"ถ้าคุณต้องการขยาย
maackle

3
Expectสามารถใช้เมื่อคุณต้องการทำให้คำสั่งแบบโต้ตอบอัตโนมัติเช่น FTP
โปรแกรม

5
โปรดทราบว่าฉันมีPseudo-terminal will not be allocated because stdin is not a terminal.ข้อความ หนึ่งต้องใช้ ssh กับ-t -tparams เพื่อหลีกเลี่ยงที่ ดูกระทู้
Buzut

8
หากคุณกำลังพยายามใช้ไวยากรณ์ << - 'END' ตรวจสอบให้แน่ใจว่าตัวคั่นสิ้นสุด heredoc ของคุณมีการย่อหน้าโดยใช้ TAB ไม่ใช่การเว้นวรรค โปรดทราบว่าการคัดลอก / วางจาก stackexchange จะให้ช่องว่างกับคุณ เปลี่ยนสิ่งเหล่านี้เป็นแท็บและคุณลักษณะการเยื้องควรทำงาน
fbicknel

249

นอกจากนี้อย่าลืมที่จะหลีกเลี่ยงตัวแปรหากคุณต้องการรับตัวแปรจากโฮสต์ปลายทาง

สิ่งนี้ทำให้ฉันเบื่อในอดีต

ตัวอย่างเช่น:

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

พิมพ์ / home / user2

ในขณะที่

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

พิมพ์ออกมา / home / ผู้ใช้

ตัวอย่างอื่น:

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

พิมพ์ "hello" อย่างถูกต้อง


2
อย่างไรก็ตามโปรดระวังสิ่งต่อไปนี้: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info

1
เพียงแค่เพิ่มเข้าไปในforลูปที่กำลังรันในsshเซสชันตัวแปรลูปจะต้องไม่ถูกยกเว้น
AlexeyDaryin

1
ในหลาย ๆ สถานการณ์วิธีที่มีสติในการแก้ไขตัวอย่างสุดท้ายของคุณssh user2@host2 'echo hello world' | awk '{ print $1 }'คือเรียกใช้สคริปต์ Awk ในเครื่อง หากคำสั่งรีโมตสร้างเอาต์พุตจำนวนมหาศาลคุณต้องการหลีกเลี่ยงการคัดลอกทั้งหมดกลับไปที่โลคัลเซิร์ฟเวอร์แน่นอน อนึ่งคำพูดเดียวรอบคำสั่งระยะไกลหลีกเลี่ยงความต้องการหลบหนีใด ๆ
tripleee

151

นี่คือส่วนขยายของคำตอบของ YarekT เพื่อรวมคำสั่งรีโมตแบบอินไลน์กับการส่งผ่านตัวแปร ENV จากเครื่องโลคัลไปยังรีโมตโฮสต์เพื่อให้คุณสามารถกำหนดพารามิเตอร์สคริปต์ของคุณในด้านรีโมต:

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

ฉันพบว่าสิ่งนี้มีประโยชน์เป็นพิเศษโดยทำให้ทุกอย่างไว้ในสคริปต์เดียวดังนั้นจึงสามารถอ่านและบำรุงรักษาได้ดีมาก

ทำไมถึงใช้งานได้ ssh รองรับไวยากรณ์ต่อไปนี้:

ssh user @ host remote_command

ในทุบตีเราสามารถระบุตัวแปรสภาพแวดล้อมเพื่อกำหนดก่อนที่จะใช้คำสั่งในบรรทัดเดียวดังนี้:

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

ทำให้ง่ายต่อการกำหนดตัวแปรก่อนเรียกใช้คำสั่ง ในกรณีนี้ก้องคือคำสั่งของเราที่เรากำลังทำงานอยู่ ทุกอย่างก่อนที่เสียงก้องจะกำหนดตัวแปรสภาพแวดล้อม

ดังนั้นเราจึงรวมคุณสมบัติทั้งสองเข้ากับคำตอบของ YarekT เพื่อรับ:

ผู้ใช้ ssh @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

ในกรณีนี้เรากำลังตั้งค่า ARG1 และ ARG2 เป็นค่าท้องถิ่น ส่งทุกอย่างหลังจาก user @ host เป็น remote_command เมื่อรีโมตเครื่องเรียกใช้งานคำสั่ง ARG1 และ ARG2 ถูกตั้งค่าโลคัลเนื่องจากการประเมินบรรทัดคำสั่งโลคัลซึ่งกำหนดตัวแปรสภาวะแวดล้อมบนเซิร์ฟเวอร์รีโมตจากนั้นเรียกใช้งานคำสั่ง bash -s โดยใช้ตัวแปรเหล่านั้น voila


1
โปรดทราบว่าหากคุณต้องการผ่าน args เช่น -a คุณสามารถใช้ - เช่น 'ssh user @ host - -a foo bar' bash -s '<script.sh' และ args สามารถไปหลังจากการเปลี่ยนเส้นทางเช่น 'ssh user @ host' bash -s '<script.sh - -a foo bar'
gaoithe

8
หากค่า env var ใด ๆ มีช่องว่างให้ใช้:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

เช่นเดียวกับที่ฉันเขียนในความคิดเห็นอื่นจุดที่ใช้มาก-sคือสามารถใช้อาร์กิวเมนต์กับสคริปต์ที่มาจาก stdin ฉันหมายความว่าคุณอาจละเว้นได้หากคุณไม่ได้ใช้งาน หากคุณใช้มันจะไม่มีเหตุผลที่จะใช้ตัวแปรสภาพแวดล้อม:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
18'18

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

ที่จะให้คุณใส่รหัสผ่านนอกจากว่าคุณได้คัดลอกกุญแจสาธารณะของผู้ใช้ hostA ไปยังไฟล์ authorized_keys ในไดเรกทอรีบ้านของผู้ใช้. ssh ที่จะช่วยให้การตรวจสอบรหัสผ่าน (ถ้ายอมรับว่าเป็นวิธีการรับรองความถูกต้องในการกำหนดค่าเซิร์ฟเวอร์ ssh)


3
โหวตให้คุณ นี่คือทางออกที่ถูกต้อง เห็นได้ชัดว่าปุ่มต้องได้รับการปกป้อง แต่ก็สามารถยกเลิกได้เช่นเดียวกับรหัสผ่านทางฝั่งเซิร์ฟเวอร์
willasaywhat

8
ฉันไม่คิดว่านี่จะตอบคำถามได้ ตัวอย่างแสดงวิธีเรียกใช้คำสั่งระยะไกล แต่ไม่ใช่วิธีดำเนินการสคริปต์ท้องถิ่นบนเครื่องระยะไกล
Jason R. Coombs

ไม่แน่ใจ แต่คุณไม่สามารถวางสคริปต์บน hostA ให้ทำงานบน hostB โดยใช้วิธีนี้ได้หรือไม่
nevets1219

27

ฉันเริ่มใช้Fabricเพื่อการทำงานที่ซับซ้อนยิ่งขึ้น Fabric ต้องการ Python และการพึ่งพาอื่น ๆ สองสามอย่าง แต่ใช้กับเครื่องไคลเอ็นต์เท่านั้น เซิร์ฟเวอร์ต้องการเซิร์ฟเวอร์ ssh เท่านั้น ฉันพบว่าเครื่องมือนี้มีประสิทธิภาพมากกว่าเชลล์เชลล์ที่ส่งมอบให้กับ SSH และคุ้มค่ากับปัญหาในการตั้งค่า (โดยเฉพาะถ้าคุณชอบการเขียนโปรแกรมใน Python) Fabric จัดการสคริปต์ที่รันบนโฮสต์หลาย ๆ โฮสต์ (หรือโฮสต์ที่มีบทบาทบางอย่าง) ช่วยอำนวยความสะดวกในการดำเนินงาน idempotent (เช่นการเพิ่มบรรทัดลงในสคริปต์การกำหนดค่า แต่ไม่ใช่ถ้ามีอยู่แล้ว) และอนุญาตการสร้างตรรกะที่ซับซ้อนมากขึ้น ภาษาที่สามารถให้ได้)


11

ssh user@remote sh ./script.unxลองใช้


8
ใช้งานได้ก็ต่อเมื่อสคริปต์อยู่ในไดเรกทอรีเริ่มต้น (โฮม) บนรีโมท ฉันคิดว่าคำถามคือวิธีการเรียกใช้สคริปต์ที่เก็บไว้ในระยะไกล
metasim

1
ssh ชื่อผู้ใช้ @ ip "chmod + x script.sh" <br/> ชื่อผู้ใช้ ssh @ ip "เพื่อไปยังไฟล์ sh ในโฮสต์ระยะไกล"
mani deepak


8

สมมติว่าคุณหมายความว่าคุณต้องการทำสิ่งนี้โดยอัตโนมัติจากเครื่อง "ท้องถิ่น" โดยไม่ต้องลงชื่อเข้าใช้เครื่อง "ระยะไกล" ด้วยตนเองคุณควรมองเข้าไปในส่วนขยายของ TCL ที่รู้จักกันในชื่อ Expect มันถูกออกแบบมาอย่างแม่นยำสำหรับสถานการณ์เช่นนี้ ฉันได้ให้ลิงก์ไปยังสคริปต์สำหรับการเข้าสู่ระบบ / การโต้ตอบผ่าน SSH

https://www.nist.gov/services-resources/software/expect

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


5

ฉันใช้อันนี้เพื่อรันเชลล์สคริปต์บนเครื่องรีโมต (ทดสอบบน / bin / bash):

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

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

ขอแนะนำให้แหล่งที่มาของไฟล์สภาพแวดล้อม (.bashrc / .bashprofile / .profile) ก่อนที่จะเรียกใช้บางสิ่งในโฮสต์ระยะไกลเพราะตัวแปรสภาพแวดล้อมโฮสต์และเป้าหมายอาจเป็น deffer


นี่ไม่ได้อธิบายวิธีย้ายสคริปต์ภายในเครื่องไปยังโฮสต์ระยะไกล
kirelagin

2

หากคุณต้องการรันคำสั่งเช่น temp=`ls -a` echo $temp คำสั่งนี้ ใน `` จะทำให้เกิดข้อผิดพลาด

คำสั่งด้านล่างจะแก้ปัญหานี้ ssh user@host ''' temp=`ls -a` echo $temp '''


1

คำตอบที่นี่ ( https://stackoverflow.com/a/2732991/4752883 ) ทำงานที่ดีถ้าคุณกำลังพยายามที่จะเรียกใช้สคริปต์บนเครื่องลินุกซ์ระยะไกลโดยใช้หรือplink มันจะทำงานถ้าสคริปต์ที่มีหลายบรรทัดบนsshlinux

** อย่างไรก็ตามหากคุณพยายามที่จะเรียกใช้สคริปต์แบทช์ที่อยู่บนlinux/windowsเครื่องท้องถิ่น และเครื่องระยะไกลของคุณWindowsและมันประกอบด้วยหลายบรรทัดโดยใช้ **

plink root@MachineB -m local_script.bat

ไม่เคยทำงาน

บรรทัดแรกของสคริปต์เท่านั้นที่จะถูกเรียกใช้งาน นี่อาจจะเป็นข้อ จำกัด plinkของ

โซลูชันที่ 1:

ในการรันสคริปต์แบตช์หลายบรรทัด (โดยเฉพาะถ้าค่อนข้างง่ายประกอบด้วยสองสามบรรทัด):

หากชุดสคริปต์ต้นฉบับของคุณมีดังต่อไปนี้

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

คุณสามารถรวมบรรทัดเข้าด้วยกันโดยใช้ตัวคั่น "&&" ดังต่อไปนี้ในlocal_script.batไฟล์ของคุณ : https://stackoverflow.com/a/8055390/4752883 :

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

หลังจากการเปลี่ยนแปลงนี้คุณสามารถเรียกใช้สคริปต์ตามที่อธิบายไว้ที่นี่โดย @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883ด้วย:

`plink root@MachineB -m local_script.bat`

โซลูชันที่ 2:

หากสคริปต์ชุดงานของคุณค่อนข้างซับซ้อนอาจเป็นการดีกว่าถ้าคุณใช้สคริปต์ชุดงานซึ่งสรุปคำสั่ง plink และตามที่อธิบายไว้ที่นี่โดย @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

สคริปต์ทุบตีนี้จะ ssh ไปยังเครื่องรีโมตเป้าหมายและรันคำสั่งบางอย่างในเครื่องรีโมตอย่าลืมที่จะติดตั้งก่อนที่จะรัน (บน 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
นี่เป็นสิ่งที่ดีถ้าคุณต้องใช้รหัสผ่าน ... อย่างไรก็ตามเพื่อประโยชน์ของใครก็ตามที่ดูที่บ้านคำสั่ง ssh ควรใช้คู่ของคีย์สาธารณะ + ส่วนตัวไม่ใช่รหัสผ่าน ... เมื่ออัปเดตเซิร์ฟเวอร์ ssh ของคุณเพื่อปิดรหัสผ่านทั้งหมด
Scott Stensland

-1

คุณสามารถใช้runoverssh :

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

-s เรียกใช้สคริปต์ท้องถิ่นจากระยะไกล


การตั้งค่าสถานะที่เป็นประโยชน์:
-gใช้รหัสผ่านส่วนกลางสำหรับโฮสต์ทั้งหมด (พรอมต์รหัสผ่านเดียว)
-nใช้ SSH แทน sshpass มีประโยชน์สำหรับการตรวจสอบสิทธิ์กุญแจสาธารณะ


-24

ก่อนอื่นให้คัดลอกสคริปต์ไปที่ Machine B โดยใช้ scp

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

จากนั้นเพียงเรียกใช้สคริปต์

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

วิธีนี้จะใช้งานได้หากคุณให้สิทธิ์อนุญาตในการเรียกทำงานสคริปต์


สวัสดีฉันแนะนำให้ใช้การแนะนำ แต่ให้ข้อผิดพลาดดังต่อไปนี้ [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: ไม่รู้จักชื่อหรือบริการ [oracle @ node1 ~] $

'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh' บรรทัดคำสั่งไม่ถูกต้องชื่อคำสั่งควรแยกออกจากส่วนผู้ใช้ @ host ด้วยช่องว่างไม่ใช่โคลอน
bortzmeyer

5
ฉันกำลัง downvoting นี้เพราะมันอ้างว่าเป็นหลักว่ามันไม่สามารถทำงานได้
Jason R. Coombs

มันจะมีประโยชน์มากก็คือเจสันเสริมว่าทำไมมันถึงไม่ถูกต้องแทนที่จะบอกความจริง ไม่ช่วยเหลือ
กริช

[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.