เป็นวิธีที่สะอาดที่สุดในการ ssh และเรียกใช้หลายคำสั่งใน Bash คืออะไร?


349

ฉันมีการตั้งค่าตัวแทน ssh แล้วและฉันสามารถเรียกใช้คำสั่งบนเซิร์ฟเวอร์ภายนอกใน Bash script ทำสิ่งต่าง ๆ เช่น:

ssh blah_server "ls; pwd;"

ตอนนี้สิ่งที่ฉันอยากทำจริง ๆ ก็คือรันคำสั่งยาวจำนวนมากบนเซิร์ฟเวอร์ภายนอก การรวมสิ่งเหล่านี้ไว้ระหว่างเครื่องหมายอัญประกาศค่อนข้างน่าเกลียดและฉันควรหลีกเลี่ยงหลาย ๆ ครั้งเพื่อหลีกเลี่ยงปัญหานี้

ดังนั้นมีวิธีที่ฉันสามารถทำได้ในครั้งเดียวล้อมรอบด้วยวงเล็บหรืออะไร? ฉันกำลังมองหาบางอย่างตามแนวของ:

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

โดยทั่วไปฉันจะมีความสุขกับการแก้ปัญหาใด ๆ ตราบเท่าที่มันสะอาด

แก้ไข

เพื่ออธิบายให้ชัดเจนฉันกำลังพูดถึงสิ่งนี้เป็นส่วนหนึ่งของสคริปต์ทุบตีที่ใหญ่กว่า คนอื่นอาจต้องจัดการกับสคริปต์ลงบรรทัดดังนั้นฉันต้องการให้มันสะอาด ฉันไม่ต้องการมีสคริปต์ทุบตีที่มีหนึ่งบรรทัดที่มีลักษณะ:

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

เพราะมันน่าเกลียดและยากที่จะอ่าน


2
อืมวิธีการเกี่ยวกับสิ่งที่ใส่ลงในสคริปต์บนเซิร์ฟเวอร์และเพียงแค่เรียกมันด้วยsshการภาวนา?
Nikolai Fetissov

@Nikolai ถ้าคำสั่งที่ขึ้นอยู่บนฝั่งไคลเอ็นต์ที่พวกเขาสามารถเขียนลงในสคริปต์เปลือกแล้วscp, sshและวิ่ง ฉันคิดว่านี่เป็นวิธีที่สะอาดที่สุด
khachik

6
นี่เป็นส่วนหนึ่งของสคริปต์ทุบตีที่ใหญ่กว่าดังนั้นฉันจึงไม่แยกครึ่งชีวิตบนคอมพิวเตอร์ส่วนบุคคลของฉันและอีกครึ่งอยู่บนเซิร์ฟเวอร์และทำงานผ่าน ssh ถ้าเป็นไปได้ทั้งหมดฉันอยากจะให้มันเป็นสคริปต์เดียวที่เรียกใช้จากคอมพิวเตอร์ส่วนตัวของฉัน จริงๆแล้วไม่มีวิธีที่สะอาดในการห่อหุ้มคำสั่งจำนวนมากใน ssh หรือไม่?
Eli

วิธีที่ดีที่สุดไม่ได้ที่จะใช้ทุบตี แต่ Perl, Python, Ruby, ฯลฯ
Salva

1
เหตุใดคุณต้องการหลีกเลี่ยงการวางคำสั่งระยะไกลไว้ในเครื่องหมายคำพูด คุณสามารถมีการขึ้นบรรทัดใหม่ภายในเครื่องหมายคำพูดได้มากเท่าที่คุณต้องการ และการใช้สตริงแทนอินพุตมาตรฐานหมายความว่าอินพุตมาตรฐานพร้อมใช้งานเช่นการอ่านอินพุตไปยังสคริปต์ระยะไกล (แม้ว่าบน Unix, ราคาเดียวมักจะเป็นที่ต้องการมากกว่าคำพูดคู่เฉพาะถ้าคุณต้องการเปลือกท้องถิ่นในการประเมินบางส่วนของสตริง.)
tripleee

คำตอบ:


457

วิธีการเกี่ยวกับ Bash Here Document :

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

เพื่อหลีกเลี่ยงปัญหาที่ระบุโดย @Globalz ในความคิดเห็นคุณอาจจะสามารถ (ขึ้นอยู่กับสิ่งที่คุณทำในไซต์ระยะไกล) ได้ด้วยการแทนที่บรรทัดแรกด้วย

ssh otherhost /bin/bash << EOF

โปรดทราบว่าคุณสามารถทำการทดแทนตัวแปรได้ในเอกสารที่นี่ แต่คุณอาจต้องจัดการกับปัญหาการเสนอราคา ตัวอย่างเช่นหากคุณอ้างถึง "ขีด จำกัด ของสตริง" (เช่นEOFในข้างต้น) คุณจะไม่สามารถทำการแทนที่ตัวแปรได้ แต่ไม่มีการอ้างอิงสตริง จำกัด ตัวแปรจะถูกแทนที่ ตัวอย่างเช่นหากคุณได้กำหนดไว้$NAMEข้างต้นในเชลล์สคริปต์ของคุณคุณสามารถทำได้

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

และมันจะสร้างไฟล์บนปลายทางที่มีชื่อของสิ่งที่คุณต้องการได้รับมอบหมายให้otherhost $NAMEกฎอื่น ๆ เกี่ยวกับการอ้างถึงเชลล์สคริปต์ก็มีผลเช่นกัน แต่ซับซ้อนเกินไปที่จะเข้าไปที่นี่


6
ดูเหมือนว่าสิ่งที่ฉันต้องการ! มันทำงานยังไง? คุณจะมีลิงค์ไปยังหน้าที่อธิบายได้หรือไม่?
Eli

9
+1 เป็นเพียงแค่การคิดว่าตัวเอง - ที่นี่ที่เดียวสำหรับการอ่านเกี่ยวกับมัน: tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
ฉันยังได้รับผลลัพธ์นี้ในพื้นที่ของฉัน: Pseudo-terminal จะไม่ถูกจัดสรรเพราะ stdin ไม่ใช่เทอร์มินัล
Globalz

14
มันอาจเป็นสิ่งสำคัญสำหรับคุณที่จะพูดคำ (เช่นแรก 'EOF') เพื่อป้องกันการขยายตัวของบรรทัดคำสั่ง ดู: man bash | น้อย + / 'เอกสารที่นี่'
assem

6
Paul ฉันแนะนำเพียงเพราะมีเกือบ 200k views คำถามนี้ดูเหมือนว่าผู้คนจำนวนมากมาที่นี่เมื่อสคริปต์ด้วย ssh เป็นเรื่องปกติที่จะต้องฉีดค่าเมื่อเขียนสคริปต์ หากไม่ใช่เพราะเสียงรบกวนในคำถามและความคิดเห็นอื่น ๆ ฉันจะทำเพียงอย่างเดียวและไปในทางของฉัน แต่ก็ไม่น่าจะเห็นได้ในขั้นตอนนี้ เชิงอรรถหนึ่งบรรทัดอาจช่วยให้ผู้คนมีอาการปวดหัวที่สำคัญ
koyae

123

แก้ไขสคริปต์ของคุณแบบโลคัลจากนั้นส่งสคริปต์ไปยัง ssh เช่น

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

commands-to-execute-remotely.shหน้าตาของคุณอยู่ที่ไหน:

ls some_folder
./someaction.sh
pwd;

7
นี่เป็นข้อได้เปรียบที่ยอดเยี่ยมที่คุณรู้ว่าสิ่งที่ถูกดำเนินการโดยสคริปต์ระยะไกล - ไม่มีปัญหาในการอ้างอิง หากคุณต้องการคำสั่งแบบไดนามิกคุณสามารถใช้เชลล์สคริปต์ที่มี subshell, ยังคงเป็น pip ใน ssh, เช่น( echo $mycmd $myvar ; ...) | ssh myhost- เช่นเดียวกับการใช้ cat คุณรู้ว่าสิ่งที่กำลังจะเข้าสู่กระแสคำสั่ง ssh และแน่นอนว่า subshell ในสคริปต์สามารถเป็นแบบหลายบรรทัดสำหรับการอ่าน - ดูlinuxjournal.com/content/bash-sub-shells
RichVel

6
คุณสามารถทำสิ่งนี้ด้วยข้อโต้แย้งในcommands-to-execute-remotely.sh?
elaRosca

1
ฉันคิดว่านี่มีประโยชน์มากกว่าสารละลาย paultomblin เนื่องจากสคริปต์สามารถเปลี่ยนเส้นทางไปยังเซิร์ฟเวอร์ ssh ใด ๆ โดยไม่ต้องเปิดไฟล์ นอกจากนี้ยังสามารถอ่านได้มากขึ้น
Patrick Bassut

3
ใช่ แต่ใช้ echo หรือเอกสาร here (ดูคำตอบยอดนิยม): use: $localvarเพื่อแปลตัวแปรที่กำหนดไว้ในเครื่อง\$remotevarเพื่อตีความตัวแปรที่กำหนดจากระยะไกล\$(something with optionnal args)เพื่อรับเอาท์พุทของสิ่งที่ดำเนินการบนเซิร์ฟเวอร์ระยะไกล ตัวอย่างที่คุณสามารถ ssh ผ่าน 1 (หรือเช่นที่แสดงที่นี่คำสั่ง 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
อืม ... สิ่งนี้ต่างจากการใช้การเปลี่ยนเส้นทางอินพุตssh blah_server < commands-to-execute-remotely.shอย่างไร
flow2k

41

เพื่อให้ตรงกับรหัสตัวอย่างของคุณคุณสามารถห่อคำสั่งของคุณภายใน qoutes เดี่ยวหรือสองครั้ง ตัวอย่างเช่น

ssh blah_server "
  ls
  pwd
"

ฉันชอบรูปแบบนี้ แต่น่าเสียดายที่ไม่มีประโยชน์สำหรับการจัดเก็บข้อมูล std ลงในตัวแปร
Signus

1
Signus คุณหมายถึงอะไรโดย "การเก็บข้อมูล std ลงในตัวแปร"
Andrei B

1
@Signus เป็นไปได้อย่างสมบูรณ์ในการทำสิ่งที่คุณอธิบายแม้ว่าคุณอาจจะต้องการใช้อัญประกาศเดี่ยวแทนเครื่องหมายอัญประกาศคู่รอบคำสั่งระยะไกล (หรือยกเว้นตัวดำเนินการที่ต้องหลีกเลี่ยงภายในเครื่องหมายอัญประกาศคู่เพื่อป้องกันเชลล์ภายในเครื่องของคุณจากการสกัดกั้นและ สอดแทรกพวกเขา)
tripleee

ฉันไม่สามารถใส่โค้ดอัญประกาศคู่ในคำสั่งเช่นนี้ fileToRemove = $ (find. -type f -name 'xxx.war') fileToRemove ควรมีชื่อไฟล์อยู่ภายใน แต่จะมีสตริงว่างเปล่าแทน จำเป็นต้องหลบหนีสิ่งใด
jkonst

37

ฉันเห็นสองวิธี:

ก่อนอื่นคุณสร้างซ็อกเก็ตควบคุมแบบนี้:

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

และเรียกใช้คำสั่งของคุณ

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

วิธีนี้คุณสามารถเขียนคำสั่ง ssh โดยไม่ต้องเชื่อมต่อกับเซิร์ฟเวอร์อีกครั้ง

ประการที่สองจะสร้างสคริปต์แบบไดนามิกscpไอเอ็นจีมันและทำงาน


20

ซึ่งสามารถทำได้ดังนี้ ใส่คำสั่งของคุณในสคริปต์ลองตั้งชื่อมัน command-inc.sh

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

บันทึกไฟล์

ตอนนี้เรียกใช้บนเซิร์ฟเวอร์ระยะไกล

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

ไม่เคยล้มเหลวสำหรับฉัน


คล้ายกับสิ่งที่ฉันคิดในตอนแรก! แต่ทำไมถึงbash -sจำเป็น?
flow2k

ยัง#!/bin/bashใช้งานจริงเหรอ?
flow2k

2
-s มีไว้สำหรับความเข้ากันได้ จาก man bash -s หากมีอ็อพชัน -s อยู่หรือหากไม่มีอาร์กิวเมนต์อยู่หลังจากประมวลผลตัวเลือกคำสั่งจะถูกอ่านจากอินพุตมาตรฐาน ตัวเลือกนี้ช่วยให้สามารถตั้งค่าพารามิเตอร์ตำแหน่งเมื่อเรียกใช้เชลล์แบบโต้ตอบ
RJ

1
RJ ขอบคุณ! ด้วยความคิดเห็นแรกของฉันดูเหมือนว่าเราเพิ่งจะทำssh user@remote < /path/to/commands-inc.sh(ดูเหมือนจะทำงานให้ฉัน) ฉันเดาว่าเวอร์ชั่นของคุณรับรองว่าเราใช้bashเชลล์และไม่ใช่เชลล์อื่น ๆ - นั่นคือจุดประสงค์ไม่ใช่เหรอ
flow2k

2
ขอบคุณ ใช่พวกเราบางคนมีโน๊ตของเราและนิสัยจากรุ่นเก่าของทุบตีและกระสุนอื่น ๆ :-)
RJ

10

ใส่คำสั่งทั้งหมดลงในสคริปต์และสามารถเรียกใช้เช่น

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

ค่อนข้างแปลก แต่ก็ใช้งานได้ดี และ args สามารถส่งผ่านไปยังสคริปต์ (ก่อนหรือหลังการเปลี่ยนเส้นทาง) เช่น 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2' เช่น 'ssh <remote-user> @ <remote-host> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
gaoithe

ฉันมีคำถามที่คล้ายกันกับคำตอบอื่นข้างต้น แต่จุดประสงค์ของการรวมbash -sคืออะไร
flow2k

1
@ flow2k -s เป็นการระบุ bash เพื่ออ่านคำสั่งจากอินพุตมาตรฐาน นี่คือคำอธิบายจากmanหน้าIf 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.
อึ๊ง Prakash

@JaiPrakash ขอบคุณสำหรับสิ่งนี้ แต่ฉันคิดว่าถ้าเราสามารถทำตามbashคำสั่งทั้งหมดได้เช่นssh remote-user@remote-host <./remote-commands.shกัน ฉันลองใช้วิธีของฉันและดูเหมือนว่าจะใช้งานได้ แต่ฉันไม่แน่ใจว่ามันขาดอะไรบ้าง
flow2k

@ flow2k จากความเข้าใจของฉันในหน้าคนที่เคยมีจะไม่มีข้อบกพร่องใด ๆ โดยไม่มีตัวเลือกที่ มันจำเป็นถ้าคุณพยายามที่จะส่งผ่านข้อโต้แย้งใด ๆ - ขอบคุณ
Jai Prakash

9

SSH และเรียกใช้คำสั่งหลายรายการใน Bash

คั่นคำสั่งด้วยเครื่องหมายอัฒภาคภายในสตริงส่งผ่านไปยัง echo ซึ่งไพพ์ทั้งหมดลงในคำสั่ง ssh ตัวอย่างเช่น:

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

ในอนาคตโปรดอธิบายว่ารหัสของคุณทำอะไรสั้น ๆ จากนั้นพูดคุยเกี่ยวกับวิธีการทำงานแล้วใส่รหัส จากนั้นใส่รหัสในบล็อกรหัสเพื่อให้ง่ายต่อการอ่าน
Eric Leschinski

จากคำถามที่คุณนำเสนอว่าสิ่งที่ OP ต้องการที่จะหลีกเลี่ยง: "ผมไม่ต้องการที่จะมีสคริปต์ทุบตีด้วยเส้นหนึ่งที่ดูเหมือนว่า ..."
jww

6

สำหรับทุกคนสะดุดที่นี่เช่นฉัน - ฉันประสบความสำเร็จกับการหนีเครื่องหมายอัฒภาคและบรรทัดใหม่:

ขั้นตอนแรก: อัฒภาค ด้วยวิธีนี้เราจะไม่ทำลายคำสั่ง ssh:

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

แสดงรายการรีโมตโฮสต์ / โฮมไดเร็กทอรี (ล็อกอินเป็น root) โดยที่

ssh <host> echo test;ls
                    ^ NO backslash

รายการไดเรกทอรีการทำงานปัจจุบัน

ขั้นตอนถัดไป: การแยกแถว:

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

นี่แสดงรายการไดเรกทอรีการทำงานระยะไกลอีกครั้ง - ปรับปรุงการจัดรูปแบบ:

ssh <host>\
  echo test\;\
  ls

ถ้าจริง ๆ แล้วดีกว่าที่นี่เอกสารหรือคำพูดรอบ ๆ เส้นที่แตก - ใช่ฉันไม่ต้องตัดสินใจ ...

(โดยใช้ bash, Ubuntu 14.04 LTS.)


1
ยิ่งไปกว่านั้นคุณสามารถใช้ && และ || ด้วยวิธีนี้ - echo test \ & \ & ls
Miro Kropacek

@MiroKropacek ก็ดีเหมือนกัน ดีขึ้นหรือไม่ ขึ้นอยู่กับสิ่งที่คุณต้องการ; เรียกใช้คำสั่งที่สองแบบมีเงื่อนไขแล้วใช่เรียกใช้โดยไม่มีเงื่อนไข (ไม่ว่าคำสั่งแรกจะสำเร็จหรือล้มเหลว) ก็จะไม่ ...
Aconcagua

@MiroKropacek ฉันไม่จำเป็นต้องหลบหนี && เมื่อใส่เครื่องหมายอัญประกาศคู่ล้อมรอบคำสั่ง:ssh host "echo test && ls"
not2savvy

5

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

  • สตริงหลายบรรทัดยาวยากที่จะรักษา
  • สคริปต์ทุบตีที่แยกต่างหากจะไม่รักษาตัวแปรในเครื่องไว้

นี่คือวิธีการทำงานเพื่อ ssh และเรียกใช้หลายคำสั่งในขณะที่รักษาบริบทท้องถิ่น

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
คุณกำลังคิดเกี่ยวกับสิ่งนี้ราวกับว่าเหตุผลเดียวที่ใครบางคนต้องการทำเช่นนี้คือหากพวกเขากำลังนั่งอยู่ที่บรรทัดคำสั่ง มีสาเหตุทั่วไปอื่น ๆ สำหรับคำถามนี้เช่นการดำเนินการคำสั่งในสภาพแวดล้อมแบบกระจายด้วยเครื่องมือต่าง ๆ เช่น rundeck, jenkins เป็นต้น
Charles Addis

5

ไม่แน่ใจว่าสะอาดที่สุดสำหรับคำสั่งยาว แต่แน่นอนที่สุด:

ssh user@host "cmd1; cmd2; cmd3"

ดีที่สุดในความเรียบง่าย!
not2savvy

4

วิธีนี้ใช้งานได้ดีสำหรับการสร้างสคริปต์เนื่องจากคุณไม่จำเป็นต้องรวมไฟล์อื่น ๆ :

#!/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

ส่วน "$ (ซึ่งทุบตี) -s" ให้ตำแหน่งของทุบตีบนเครื่องโลคัลมากกว่าเครื่องรีโมต ฉันคิดว่าคุณต้องการ '$ (ซึ่งทุบตี) -s' แทน (อัญประกาศเดียวเพื่อระงับการทดแทนพารามิเตอร์ท้องถิ่น)
jsears

1
Paul Tomblin ให้คำตอบBash Here Document เมื่อ 6 ปีก่อน คำตอบนี้เพิ่มมูลค่าอะไร?
jww

-sธงฉันอ้างอิงเขาคุณอาจจะสามารถหนีไปได้ด้วยการแทนที่บรรทัดแรกด้วย ...และฉันไม่สามารถหนีไปได้ด้วยการเรียกทุบตีที่ฉันจำได้
sjas

4

วิธีที่ง่ายที่สุดในการกำหนดค่าระบบของคุณให้ใช้เซสชัน ssh เดี่ยวโดยค่าเริ่มต้นด้วยมัลติเพล็กซ์

สิ่งนี้สามารถทำได้โดยการสร้างโฟลเดอร์สำหรับซ็อกเก็ต:

mkdir ~/.ssh/controlmasters

จากนั้นเพิ่มสิ่งต่อไปนี้ในการกำหนดค่า. ssh ของคุณ:

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

ตอนนี้คุณไม่จำเป็นต้องแก้ไขรหัสใด ๆ ของคุณ การทำเช่นนี้ช่วยให้สามารถเรียก ssh และ scp ได้หลายครั้งโดยไม่ต้องสร้างหลายเซสชันซึ่งมีประโยชน์เมื่อจำเป็นต้องมีการโต้ตอบระหว่างเครื่องโลคอลและรีโมท

ขอบคุณคำตอบของ @ terminus http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/และhttps://en.wikibooks.org /

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.