ORing với true trong một lệnh trên ssh


15

Khi tôi cố chạy pkill -ftừ xa qua ssh và cố gắng loại bỏ mã lỗi có thể xảy ra (tiếp tục với phần còn lại của tập lệnh của tôi ngay cả khi không tìm thấy quy trình nào), || truekhông hoạt động như tôi mong đợi.

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255

Tôi cho rằng đó là ssh trả về 255, không phải lệnh giữa các dấu ngoặc kép, nhưng tại sao?

Câu trả lời:


29

Giả sử của bạn rằng sshchính nó trả về trạng thái thoát 255 là chính xác. Các sshtrang người đàn ông khẳng định rằng:

ssh thoát với trạng thái thoát của lệnh từ xa hoặc với 255 nếu xảy ra lỗi.

Nếu bạn chỉ đơn giản là chạy ssh pi@10.20.0.10 "pkill -f asdf", rất có thể bạn sẽ nhận được trạng thái thoát 1, tương ứng với pkilltrạng thái cho Không có quy trình nào khớp với .

Phần thử thách là để hiểu tại sao xảy ra lỗi với SSH khi bạn chạy

ssh pi@10.20.0.10 "pkill -f asdf || true"

Các lệnh từ xa SSH

Máy chủ SSH khởi chạy shell để chạy (các) lệnh từ xa. Đây là một ví dụ về điều này trong hành động:

$ ssh server "ps -elf | tail -5"
4 S root     35323  1024 12  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony [priv]
5 S anthony  35329 35323  0  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony@notty
0 S anthony  35330 35329  0  80   0 - 28283 do_wai 12:01 ?        00:00:00 bash -c ps -elf | tail -5
0 R anthony  35341 35330  0  80   0 - 40340 -      12:01 ?        00:00:00 ps -elf
0 S anthony  35342 35330  0  80   0 - 26985 pipe_w 12:01 ?        00:00:00 tail -5

Lưu ý rằng shell mặc định là bashvà lệnh từ xa không phải là một lệnh đơn giản mà là một đường ống , một chuỗi gồm một hoặc nhiều lệnh được phân tách bởi toán tử điều khiển |.

Shell Bash đủ thông minh để nhận ra rằng nếu lệnh được truyền cho nó bởi -ctùy chọn là một lệnh đơn giản , nó có thể tối ưu hóa bằng cách không thực sự tạo ra một quy trình mới, tức là, nó trực tiếp execlà lệnh đơn giản thay vì đi qua bước bổ sung của forking trước khi nó execs. Đây là một ví dụ về những gì xảy ra khi bạn chạy một lệnh đơn giản từ xa ( ps -elftrong trường hợp này):

$ ssh server "ps -elf" | tail -5
1 S root     34740     2  0  80   0 -     0 worker 11:49 ?        00:00:00 [kworker/0:1]
1 S root     34762     2  0  80   0 -     0 worker 11:50 ?        00:00:00 [kworker/0:3]
4 S root     34824  1024 31  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony [priv]
5 S anthony  34829 34824  0  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony@notty
0 R anthony  34830 34829  0  80   0 - 40340 -      11:51 ?        00:00:00 ps -elf

Tôi đã gặp phải hành vi này trước đây nhưng tôi không thể tìm thấy một tài liệu tham khảo nào tốt hơn câu trả lời AskUb Ubuntu này .

hành vi của pkill

pkill -f asdf || truekhông phải là một lệnh đơn giản (nó là một danh sách lệnh ), nên việc tối ưu hóa ở trên không thể xảy ra nên khi bạn chạy ssh pi@10.20.0.10 "pkill -f asdf || true", sshdtiến trình sẽ chuyển đổi và thực thi bash -c "pkill -f asdf || true".

Như câu trả lời của ctx chỉ ra, pkillsẽ không giết quá trình của chính nó. Tuy nhiên, nó sẽ giết bất kỳ tiến trình nào khác có dòng lệnh khớp với -fmẫu. Các bash -clệnh phù hợp với mô hình này để nó giết chết quá trình này - cha mẹ của mình (vì nó xảy ra).

Sau đó, máy chủ SSH thấy rằng tiến trình shell mà nó khởi động để chạy các lệnh từ xa đã bị giết một cách bất ngờ nên nó báo lỗi cho máy khách SSH.


1
Trong khi câu trả lời xác định một cách chính xác nguồn một vấn đề như trong pkillgiết chết quá trình shell mẹ của nó bởi vì danh sách arg của nó phù hợp với regexp, tôi sẽ nâng cao một sự phản đối thuật ngữ: x || ykhông một lệnh phức hợp , đó là một danh sách lệnh .
Stéphane Chazelas

@ StéphaneChazelas Cảm ơn bạn đã phản hồi. Tôi đã từng sử dụng các cấu trúc có điều kiện của Bash như là các lệnh ghép nhưng tôi đồng ý rằng nó hợp lý hơn khi xem xét x||ynhư một danh sách lệnh. Bây giờ tôi đã chỉnh sửa câu trả lời của mình để bao gồm các liên kết đến các định nghĩa POSIX khác nhau.
Anthony G - công lý cho Monica

1
Lưu ý rằng trong trường hợp chung, nó không nhiều đến mức không thể tối ưu hóa bởi vì đó là danh sách lệnh mà ngưỡng này có một lệnh khác để chạy (có khả năng). Trong zsh/ ksh93/ FreeBSD sh, false || pkill -f asdfsẽ pkillthực hiện trong quy trình shell. bashchỉ thực hiện tối ưu hóa khi chỉ có một lệnh đơn giản. true; pkill -f asdfcũng sẽ là một vấn đề
Stéphane Chazelas

9

Lệnh từ xa của bạn sẽ tự giết:

$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true

pgrep và pkill sẽ bỏ qua quy trình riêng của họ, nhưng với cờ -f, họ sẽ tìm thấy trình bao cha mẹ:

$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true

Điều đó có ý nghĩa! bash -c 'pgrep -af asdf'(không có || true) không tìm thấy chính nó. Tại sao không? Nó có -f.
Gauthier

2
@Gauthier Thật ra, tôi nghĩ trong trường hợp này, Bash đủ thông minh để nhận ra rằng lệnh này là một lệnh đơn giản (không phải là lệnh ghép) nên nó tối ưu hóa bằng cách không thực sự tạo ra một quy trình mới. Tôi nhớ đã gặp phải hành vi tương tự trước đây (tôi phải cập nhật câu trả lời của mình).
Anthony G - công lý cho Monica

3

Bạn yêu cầu pkill giết bất cứ thứ gì khớp với "asdf". Bạn nên bảo nó khớp với [a] sdf, theo cách đó nó vẫn sẽ tìm bất cứ thứ gì có tên "asdf", nhưng sẽ không nhìn thấy chính nó (nếu bạn căn chỉnh asdf với [a] sdf, lưu ý rằng s được căn chỉnh với] và không S.)

ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'

Đây là một mẹo phổ biến cũng được sử dụng với grep / egrep / awk / etc:

ps -ef | grep "something"  # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself

# why it works:
# the commandline contains:     ps -ef | grep [s]omething
# and grep tries to find:                      something

Thủ thuật này đã cũ và tôi đã thấy nó cách đây hàng thập kỷ trong hệ điều hành Unix (vẫn là một bài đọc tốt!)

Để "tự động hóa" nó, điều đó không dễ, nhưng thông thường mỗi lần bạn cần grep cho một chuỗi biến regrec = "cái gì đó", bạn có thể thử làm:

grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')" 
#  if regexp="something",  it does: grep "[s]omething"
#  if regexp="otherthing", it does: grep "[o]therthing"
#  if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...

lưu ý: Tôi biết rằng grep 'fail' của tôi, người ta có thể giữ nguyên biểu thức chính quy, vì nó sẽ không khớp với chính nó (a, b hoặc c sẽ không khớp với ']' của dòng lệnh) . Nhưng nó không phải là tầm thường khi đưa ra một thử nghiệm của regrec. Nói chung, thủ thuật hoạt động. một trong đó tự động hóa sẽ làm việc hầu hết thời gian. Khi không, một số hack thông minh (hoặc can thiệp thủ công) sẽ cần thiết.
Olivier Dulac

Ngoài ra, (abc)?(def)?sẽ phải ([a]bc)?([d]ef)?... Bạn không thể phân tích regex bằng regex?! > :-)
wizzwizz4

@ wizzwizz4 Tôi biết. nhưng mẫu mực của bạn đã không phù hợp với chính nó. Đây là một điều phức tạp, tôi chỉ cung cấp một giải pháp đơn giản cho các trường hợp đơn giản hơn
Olivier Dulac

@ wizzwizz4 Tôi đã nói như vậy trong bình luận đầu tiên của mình ...
Olivier Dulac
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.