Cách sạch nhất để ssh và chạy nhiều lệnh trong Bash là gì?


349

Tôi đã có một tác nhân ssh được thiết lập và tôi có thể chạy các lệnh trên máy chủ bên ngoài trong tập lệnh Bash làm các công cụ như:

ssh blah_server "ls; pwd;"

Bây giờ, những gì tôi thực sự muốn làm là chạy rất nhiều lệnh dài trên một máy chủ bên ngoài. Bao gồm tất cả những điều này ở giữa các dấu ngoặc kép sẽ khá xấu, và tôi thực sự muốn tránh ssh'ing nhiều lần chỉ để tránh điều này.

Vì vậy, có cách nào tôi có thể làm điều này trong một lần được đặt trong ngoặc đơn hoặc một cái gì đó không? Tôi đang tìm kiếm thứ gì đó dọc theo dòng:

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

Về cơ bản, tôi sẽ hài lòng với bất kỳ giải pháp nào miễn là nó sạch.

Biên tập

Để làm rõ, tôi đang nói về việc đây là một phần của tập lệnh bash lớn hơn. Những người khác có thể cần phải xử lý kịch bản, vì vậy tôi muốn giữ sạch nó. Tôi không muốn có một tập lệnh bash với một dòng giống như:

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

bởi vì nó cực kỳ xấu xí và khó đọc.


2
Hmm, làm thế nào về việc đưa tất cả những điều đó vào một tập lệnh trên máy chủ và chỉ gọi nó bằng một sshlời mời?
Nikolai Fetissov

@Nikolai nếu các lệnh phụ thuộc về phía khách hàng, họ có thể được viết thành một kịch bản shell, sau đó scp, sshvà chạy. Đây sẽ là cách sạch nhất, tôi nghĩ.
khachik

6
Đây là một phần của tập lệnh bash lớn hơn, vì vậy tôi không muốn chia nó ra với một nửa sống trên máy tính cá nhân của tôi và nửa còn lại sống trên máy chủ và chạy qua ssh. Nếu có thể, tôi thực sự muốn giữ nó như một tập lệnh chạy từ máy tính cá nhân của tôi. Có thực sự không có cách sạch để bao gồm một loạt các lệnh trong một ssh?
Eli

Cách tốt nhất là không sử dụng bash mà là Perl, Python, Ruby, v.v.
salva

1
Tại sao bạn muốn tránh đặt các lệnh từ xa trong dấu ngoặc kép? Bạn có thể có dòng mới bên trong dấu ngoặc kép, bao nhiêu tùy thích; và sử dụng một chuỗi thay vì đầu vào tiêu chuẩn có nghĩa là đầu vào tiêu chuẩn có sẵn, ví dụ như đọc đầu vào cho tập lệnh từ xa. (Mặc dù trên Unix, các trích dẫn đơn thường được ưu tiên hơn các trích dẫn kép, trừ khi bạn đặc biệt cần shell cục bộ để đánh giá một số phần của chuỗi.)
tripleee

Câu trả lời:


456

Làm thế nào về một Bash ở đây Tài liệu :

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

Để tránh các vấn đề được đề cập bởi @Globalz trong các nhận xét, bạn có thể (tùy thuộc vào những gì bạn đang làm trên trang web từ xa) bằng cách thay thế dòng đầu tiên bằng

ssh otherhost /bin/bash << EOF

Lưu ý rằng bạn có thể thay thế biến trong tài liệu Tại đây, nhưng bạn có thể phải xử lý các vấn đề trích dẫn. Chẳng hạn, nếu bạn trích dẫn "chuỗi giới hạn" (nghĩa là EOFở phần trên), thì bạn không thể thay thế biến. Nhưng không trích dẫn chuỗi giới hạn, các biến được thay thế. Ví dụ: nếu bạn đã xác định $NAMEở trên trong tập lệnh shell của mình, bạn có thể làm

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

và nó sẽ tạo một tệp ở đích otherhostvới tên của bất cứ thứ gì bạn được gán $NAME. Các quy tắc khác về trích dẫn shell shell cũng được áp dụng, nhưng quá phức tạp để đi vào đây.


6
Điều này trông giống như những gì tôi muốn! Nó hoạt động thế nào Bạn có tình cờ có một liên kết đến một trang giải thích nó không?
Eli

9
+1 Chỉ nghĩ rằng bản thân tôi - đây là một nơi để đọc về nó: tldp.org/LDP/abs/html/here-docs.html
bosmacs

14
Tôi cũng nhận được đầu ra này cho địa phương của mình: Thiết bị đầu cuối giả sẽ không được phân bổ vì stdin không phải là thiết bị đầu cuối.
Globalz

14
Điều quan trọng đối với bạn là trích dẫn từ (nghĩa là 'EOF' đầu tiên) để ngăn chặn việc mở rộng các dòng lệnh. Xem: người đàn ông bash | ít + / 'Đây Documents'
Assem

6
Paul, tôi chỉ đề xuất nó vì với gần 200 nghìn lượt xem cho câu hỏi này, có vẻ như rất nhiều người đang đến đây khi viết kịch bản với ssh. Đó là thông thường để tiêm giá trị khi kịch bản. Nếu không phải vì sự ồn ào trong các câu hỏi và bình luận khác, tôi sẽ chỉ đưa ra một ý kiến ​​và theo cách của tôi, nhưng không chắc là sẽ được nhìn thấy ở giai đoạn này. Một chú thích một dòng có thể cứu người ta một số vấn đề đau đầu.
koyae

122

Chỉnh sửa tập lệnh của bạn cục bộ, sau đó chuyển nó thành ssh, vd

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

nơi commands-to-execute-remotely.shtrông giống như danh sách của bạn ở trên:

ls some_folder
./someaction.sh
pwd;

7
Điều này có lợi thế lớn là bạn biết chính xác những gì đang được thực thi bởi tập lệnh từ xa - không có vấn đề gì với trích dẫn. Nếu bạn cần các lệnh động, bạn có thể sử dụng tập lệnh shell với lớp con, vẫn chuyển sang ssh, tức là ( echo $mycmd $myvar ; ...) | ssh myhost- như với cách sử dụng con mèo, bạn biết chính xác những gì đang đi vào luồng lệnh ssh. Và tất nhiên, phần con trong tập lệnh có thể là nhiều dòng để dễ đọc - xem linuxjournal.com/content/bash-sub-shells
RichVel

6
Bạn có thể làm điều này với các đối số trong commands-to-execute-remotely.sh?
elaRosca

1
Tôi nghĩ rằng điều này hữu ích hơn nhiều so với giải pháp paultomblin. Vì tập lệnh có thể được chuyển hướng đến bất kỳ máy chủ ssh nào mà không cần phải mở tệp. Ngoài ra nó dễ đọc hơn nhiều.
Patrick Bassut

3
có, nhưng sử dụng echo hoặc tài liệu ở đây (xem câu trả lời trên cùng): use: $localvarđể diễn giải một biến được xác định cục bộ, \$remotevarđể giải thích từ xa một biến được xác định từ xa, \$(something with optionnal args)để có được đầu ra của một cái gì đó được thực thi trên máy chủ từ xa. Một ví dụ mà bạn có thể ssh qua 1 (hoặc, như được hiển thị ở đây, nhiều lệnh 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 ... điều này khác với việc sử dụng chuyển hướng đầu vào như ssh blah_server < commands-to-execute-remotely.shthế nào?
Flow2k

41

Để khớp với mã mẫu của bạn, bạn có thể gói các lệnh của mình bên trong các lệnh đơn hoặc đôi. Ví dụ

ssh blah_server "
  ls
  pwd
"

Tôi thích định dạng này, tuy nhiên thật đáng buồn là nó không hữu ích cho việc lưu trữ dữ liệu std vào một biến.
Signus

1
Signus, ý bạn là gì khi "lưu trữ dữ liệu std vào một biến"?
Andrei B

1
@Signus Hoàn toàn có thể làm những gì bạn mô tả, mặc dù bạn có thể sẽ muốn sử dụng dấu ngoặc đơn thay vì dấu ngoặc kép xung quanh các lệnh từ xa (hoặc thoát các toán tử cần thoát trong dấu ngoặc kép để ngăn chặn vỏ cục bộ của bạn khỏi bị chặn và nội suy chúng).
tripleee

Tôi không thể đặt bên trong mã trích dẫn kép một lệnh như tệp nàyToRemove = $ (find. -Apepe f -name 'xxx.war'). fileToRemove nên có một tên tệp bên trong nhưng thay vào đó nó có một chuỗi rỗng. Có một cái gì đó cần phải được trốn thoát?
jkonst

37

Tôi thấy hai cách:

Đầu tiên bạn tạo một ổ cắm điều khiển như thế này:

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

và chạy các lệnh của bạn

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

Bằng cách này bạn có thể viết một lệnh ssh mà không thực sự kết nối lại với máy chủ.

Thứ hai sẽ là tự động tạo tập lệnh, nhập scpnó và chạy.


20

Điều này cũng có thể được thực hiện như sau. Đặt các lệnh của bạn trong một tập lệnh, hãy đặt tên cho nó là Command-inc.sh

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

Lưu các tập tin

Bây giờ chạy nó trên máy chủ từ xa.

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

Không bao giờ thất bại đối với tôi.


Tương tự như những gì tôi đã nghĩ ban đầu! Nhưng tại sao lại bash -scần thiết?
Flow2k

Ngoài ra, có #!/bin/bashthực sự được sử dụng?
Flow2k

2
Các -s là có để tương thích. Từ man bash -s Nếu có tùy chọn -s hoặc nếu không còn đối số sau khi xử lý tùy chọn, thì các lệnh được đọc từ đầu vào tiêu chuẩn. Tùy chọn này cho phép các tham số vị trí được đặt khi gọi trình bao tương tác.
RJ

1
RJ, cảm ơn! Với nhận xét đầu tiên của tôi, có vẻ như chúng tôi chỉ làm ssh user@remote < /path/to/commands-inc.sh(có vẻ như nó hoạt động với tôi). Chà, tôi đoán phiên bản của bạn đảm bảo chúng tôi sử dụng bashshell chứ không phải một số shell khác - đó là mục đích, phải không?
Flow2k

2
Cảm ơn. Đúng, một số người trong chúng ta có ghi chú và thói quen từ các phiên bản cũ hơn của bash và các loại vỏ khác. :-)
RJ

10

Đặt tất cả các lệnh vào một kịch bản và nó có thể được chạy như

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

Hơi kỳ quặc nhưng nó hoạt động tốt. Và args có thể được chuyển đến tập lệnh (trước hoặc sau khi chuyển hướng). ví dụ: 'ssh <remote-user> @ <remote-host> "bash -s" arg1 <./ remote-commands.sh arg2' eg 'ssh <remote-user> @ <remote-host> "bash -s" - - -arg1 <./ remote-commands.sh arg2 '
gaoithe

Tôi đã có một câu hỏi tương tự với một câu trả lời khác ở trên, nhưng mục đích bao gồm là bash -sgì?
Flow2k

1
@ Flow2k -s là để chỉ ra bash để đọc các lệnh từ đầu vào tiêu chuẩn. Dưới đây là mô tả từ các mantrangIf 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 Cảm ơn vì điều này, nhưng tôi thực sự đã suy nghĩ liệu chúng ta có thể bashhoàn toàn loại bỏ lệnh này hay không ssh remote-user@remote-host <./remote-commands.sh. Tôi đã thử theo cách của mình và nó dường như hoạt động, mặc dù tôi không chắc liệu nó có bị thiếu theo một cách nào đó không.
Flow2k

@ Flow2k từ sự hiểu biết của tôi về các trang người đàn ông sẽ không có bất kỳ thiếu sót nào nếu không có tùy chọn đó. Nó được yêu cầu nếu bạn đang cố gắng vượt qua bất kỳ đối số. - cảm ơn
Jai Prakash

9

SSH và chạy nhiều lệnh trong Bash.

Các lệnh riêng biệt với dấu chấm phẩy trong một chuỗi, được truyền cho echo, tất cả được dẫn vào lệnh ssh. Ví dụ:

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, trong tương lai, vui lòng mô tả ngắn gọn mã của bạn làm gì. Sau đó nói về cách nó hoạt động, sau đó đặt mã. Sau đó đặt mã vào một khối mã để dễ đọc.
Eric Leschinski

Từ câu hỏi, bạn đã đưa ra chính xác những gì OP muốn tránh: "Tôi không muốn có một tập lệnh bash với một dòng trông giống như ..."
jww

6

Đối với bất kỳ ai vấp ngã ở đây như tôi - tôi đã thành công với việc thoát khỏi dấu chấm phẩy và dòng mới:

Bước đầu tiên: dấu chấm phẩy. Bằng cách này, chúng tôi không phá vỡ lệnh ssh:

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

Liệt kê các thư mục host / home từ xa (đăng nhập với quyền root), trong khi

ssh <host> echo test;ls
                    ^ NO backslash

liệt kê các thư mục làm việc hiện tại.

Bước tiếp theo: chia dòng:

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

Điều này một lần nữa liệt kê thư mục làm việc từ xa - định dạng được cải thiện:

ssh <host>\
  echo test\;\
  ls

Nếu thực sự đẹp hơn tài liệu ở đây hoặc trích dẫn xung quanh các dòng bị hỏng - tốt, không phải tôi quyết định ...

(Sử dụng bash, Ubuntu 14.04 LTS.)


1
Còn gì tuyệt vời hơn khi bạn có thể sử dụng && và || cách này cũng vậy - kiểm tra tiếng vang \ & \ & ls
Miro Kropacek

@MiroKropacek Điều đó cũng tốt. Tốt hơn? Phụ thuộc vào những gì bạn muốn; chạy lệnh thứ hai một cách có điều kiện , sau đó có, chạy nó vô điều kiện (không có vấn đề nếu lần đầu tiên thành công hay thất bại), sau đó không ...
Aconcagua

@MiroKropacek, tôi đã không phải thoát khỏi && khi đặt dấu ngoặc kép xung quanh các lệnh:ssh host "echo test && ls"
not2savvy

5

Các câu trả lời được đăng bằng cách sử dụng chuỗi nhiều dòng và nhiều tập lệnh bash không hoạt động đối với tôi.

  • Chuỗi multiline dài là khó để duy trì.
  • Các tập lệnh bash riêng biệt không duy trì các biến cục bộ.

Đây là một cách chức năng để ssh và chạy nhiều lệnh trong khi vẫn giữ bối cảnh cục bộ.

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
Bạn đang nghĩ về điều này như thể lý do duy nhất ai đó muốn làm điều này là nếu họ đang ngồi ở một dòng lệnh. Cũng có những lý do phổ biến khác cho câu hỏi này, chẳng hạn như thực thi các lệnh trên các môi trường phân tán bằng các công cụ như rundeck, jenkins, v.v.
Charles Addis

điều này hoạt động nhưng tôi nhận được thông báo lỗi không mong muốn
Annahri

5

Không chắc chắn nếu sạch nhất cho các lệnh dài nhưng chắc chắn là dễ nhất:

ssh user@host "cmd1; cmd2; cmd3"

Tốt nhất trong sự đơn giản!
not2savvy

4

Điều này hoạt động tốt để tạo tập lệnh, vì bạn không phải bao gồm các tệp khác:

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

Phần "$ (bash) -s" cung cấp cho bạn vị trí bash trên máy cục bộ, thay vì máy từ xa. Tôi nghĩ rằng bạn muốn '$ (bash) -s' thay vào đó (dấu ngoặc đơn để triệt tiêu thay thế tham số cục bộ).
jsears

1
Paul Tomblin đã cung cấp câu trả lời Bash Here Document 6 năm trước. Giá trị nào được thêm vào bởi câu trả lời này?
jww

-scờ, tôi trích dẫn anh ta rằng bạn có thể thoát khỏi việc thay thế dòng đầu tiên bằng ... , và tôi đã không thể thoát khỏi chỉ bằng cách gọi bash tôi lờ mờ nhớ.
sjas

4

Cách dễ nhất để định cấu hình hệ thống của bạn để sử dụng các phiên ssh duy nhất theo mặc định với ghép kênh.

Điều này có thể được thực hiện bằng cách tạo một thư mục cho các socket:

mkdir ~/.ssh/controlmasters

Và sau đó thêm phần sau vào cấu hình .ssh của bạn:

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

Bây giờ, bạn không cần sửa đổi bất kỳ mã nào của mình. Điều này cho phép nhiều cuộc gọi đến ssh và scp mà không tạo nhiều phiên, điều này rất hữu ích khi cần có nhiều tương tác hơn giữa các máy cục bộ và máy từ xa.

Cảm ơn câu trả lời của @ terminus, http://www.cyberciti.biz/faq/linux-unix-osx-bsd-ssh-multiplexing-to-speed-up-ssh-connections/https://en.wikibooks.org / wiki / OpenSSH / Cookbook / Ghép kênh .

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.