Tại sao lệnh `cd` không hoạt động thông qua SSH?


41

Tôi đã cố gắng sao lưu một số tệp qua SSH nhưng thay vì tar'những tệp tôi muốn tôi đã có thư mục nhà của mình. Tôi đã làm một số thử nghiệm thêm và nó hiểu rõ điều này:

ssh root@server /bin/sh -c "cd /boot && ls -l"

Mà bất ngờ của tôi liệt kê các tập tin trong /rootkhông /boot. Nhưng nếu tôi chạy toàn bộ /bin/shlệnh từ một thiết bị đầu cuối thì nó sẽ cds và in các /boottệp.

Chuyện gì đang xảy ra ở đây vậy?


1
Trừ khi đây là một ví dụ, tại sao bạn không sử dụng ssh root@server /bin/sh -c "ls -l /boot"?
demure

Thật là buồn cười bạn hỏi. Tôi đã cố gắng làm điều đó trước khi bình luận và vẫn không nhận được danh sách thư mục.
unxnut

2
@demure bởi vì tôi thực sự muốn tar và tar / boot sẽ bao gồm đường dẫn khởi động trong kết quả mà tôi không muốn
Ambroz Bizjak

Câu trả lời:


35

ssh không cho phép bạn chỉ định một lệnh chính xác, như bạn đã làm, như một chuỗi các đối số được truyền cho execvp trên máy chủ từ xa. Thay vào đó, nó nối tất cả các đối số thành một chuỗi và chạy chúng thông qua một vỏ từ xa. Theo tôi, đây là một lỗ hổng thiết kế lớn trong ssh ... đó là một công cụ unix hoạt động tốt trong hầu hết các cách, nhưng khi đến lúc chỉ định một lệnh, nó đã chọn sử dụng một chuỗi nguyên khối duy nhất thay vì một argv, như nó được thiết kế cho MSDOS hoặc một cái gì đó!

Vì ssh sẽ truyền lệnh của bạn dưới dạng một chuỗi sh -c, nên bạn không cần phải cung cấp lệnh của riêng mình sh -c. Khi bạn làm, kết quả là

sh -c '/bin/sh -c cd /boot && ls -l'

với trích dẫn ban đầu bị mất. Vì vậy, các lệnh được phân tách bằng &&:

`/bin/sh -c cd /boot`
`ls -l`

Đầu tiên trong số đó chạy shell với văn bản lệnh "cd" và $0= "boot". Lệnh "cd" hoàn thành thành công, $0không liên quan và /bin/sh -cchỉ ra thành công, sau đó ls -lxảy ra.


2
Mặc dù tôi đồng ý rằng thật đáng tiếc khi sshhành xử như vậy, nếu không, nó sẽ phải được gọi sexec, không phải ssh. sshlà phiên bản bảo mật của rsh(hoạt động tương tự trong vấn đề đó) và rlogin( rshđược gọi rloginkhi không được thông qua một dòng lệnh). Thật không may là nó cũng không thực hiện rexecđược.
Stéphane Chazelas

@StephaneChazelas đã bao giờ có lệnh rexec chưa? Nó chỉ là một chức năng thư viện C theo như tôi biết. Điểm tốt mặc dù, về rsh. Trở thành một thay thế thả xuống cho rsh là chìa khóa để áp dụng nhanh chóng trong những ngày đầu của ssh, khi mọi người đã có hàng tấn tập lệnh đã sử dụng rsh.

Tôi chắc chắn đã sử dụng nó trong quá khứ. Nhìn bây giờ, nó phải có trên HPUX vì dường như không có nhiều Unice khác có nó, tôi phải thừa nhận mặc dù tôi có ấn tượng rằng nó phổ biến hơn.
Stéphane Chazelas

Cảm ơn. Điều này thực sự có ích, đặc biệt. kể từ khi tôi đào hầm với ssh. Tôi đã phải thực hiện ssh -t machine1 'ssh -t machine2 "echo \" 1 && 2 \ ""' để lấy '1 && 2' làm đầu ra. Điều đó đã giết chết vài giờ.
ghayes

Nếu bạn muốn truyền một lệnh chính xác, ví dụ từ "$ @", bạn có thể sử dụng lệnh này: "`printf "%q " "$@"`"với bash's printfđể trích dẫn một chuỗi hoặc chuỗi có thoát vỏ.
Sam Watkins

36

Đó là một vấn đề trích dẫn. Ssh đã chạy lệnh bạn truyền nó trong shell. Khi bạn truyền nhiều tham số, chúng được nối với một khoảng trắng ở giữa để tạo chuỗi. Vì vậy, lệnh từ xa mà bạn đang chạy từ xa là /bin/sh -c cd /boot && ls -l(không có dấu ngoặc kép, vì dấu ngoặc kép trong lệnh của bạn đã được giải thích bởi trình bao cục bộ).

/bin/sh -c cd /bootchạy /bin/shvà nói với nó để chạy các lệnh cdvà cũng để thiết lập $0để /boot. Một khi điều này được thực hiện, shell cha (cái được khởi chạy bởi sshd) chạy ls -l.

Trong trường hợp của bạn, chỉ cần loại bỏ sh -ccái hoàn toàn vô dụng trừ khi trình điều khiển từ xa của bạn (như được chỉ định trong /etc/passwdhoặc cơ sở dữ liệu mật khẩu khác) không hiểu lệnh này.

ssh root@server "cd /boot && ls -l"

Nếu bạn cần gọi một shell khác, bạn phải trích dẫn lệnh từ xa để bảo vệ nó khỏi sự mở rộng bởi shell từ xa được gọi bởi sshd. Ví dụ: nếu shell đăng nhập của bạn là dấu gạch ngang và bạn muốn chạy lệnh bash:

ssh root@server 'bash -c "cd ~bob && ls -l"'

8

Tôi nghĩ nó có liên quan nhiều hơn đến cách các tùy chọn được phân tích cú pháp bởi trình bao. Ví dụ, điều này hoạt động:

$ ssh root@server /bin/sh -c '"cd /boot && ls -l"'

Điều này có cùng vấn đề với lệnh của bạn:

$ ssh root@server /bin/sh -c 'cd /boot && ls -l'

Nếu bạn bật công -vtắc, sshbạn có thể thấy những gì đang diễn ra:

Lệnh 1:

debug1: Lệnh gửi: / bin / sh -c "cd / boot && ls -l"

Lệnh thứ 2:

debug1: Lệnh gửi: / bin / sh -c cd / boot && ls -l

Thông thường khi gửi lệnh thông qua sshbạn phải đặc biệt chú ý đến trích dẫn và bọc dấu ngoặc kép trong dấu ngoặc kép khi các lớp khác nhau loại bỏ chúng. Cũng đừng bận tâm gửi /bin/sh.

Bạn có thể làm điều rất hữu ích một khi bạn hiểu được trích dẫn sshnhư sau. Điều này sẽ chạy lệnh trên máy chủ từ xa nhưng thu thập kết quả trong một tệp cục bộ trên hệ thống nơi bạn đã chạy sshlệnh:

$ ssh root@server 'free -m' > /tmp/memory.status

hoặc cái này, nơi bạn tar một thư mục trên một máy chủ từ xa và tạo nó trên hệ thống cục bộ:

$ ssh remotehost 'tar zcvf - SOURCEDIR' | cat > DESTFILE.tar.gz

Tài liệu tham khảo


4

Thông thường, bạn không phải chỉ định sử dụng shell nào. Những công việc này:

ssh user@host "cd /boot && pwd"

Nhưng nếu shell mặc định của bạn có vấn đề, thì chỉ cần đảm bảo rằng bạn trích dẫn toàn bộ lệnh, bao gồm cả lời gọi shell. Một trong những công việc sau đây

ssh user@host '/bin/sh -c "cd /boot && pwd"'
ssh user@host "/bin/sh -c \"cd /boot && pwd\""

Lưu ý rằng mặc dù cd /boot && pwdhoạt động trong tất cả các hệ vỏ của các gia đình lớn (Bourne, csh, RC), /bin/sh -c "cd /boot && pwd"sẽ không hoạt động nếu vỏ từ xa là của rcgia đình "không có gì đặc biệt.
Stéphane Chazelas
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.