ORing ด้วยความจริงในคำสั่งมากกว่า ssh


15

เมื่อฉันพยายามเรียกใช้pkill -fจากระยะไกลผ่าน ssh และพยายามที่จะทิ้งรหัสข้อผิดพลาดที่เป็นไปได้ (เพื่อดำเนินการต่อกับส่วนที่เหลือของสคริปต์ของฉันแม้ว่าจะไม่พบกระบวนการ) || trueไม่ทำงานตามที่ฉันคาดหวัง

$ 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

ฉันคิดว่ามันเป็นsshที่ส่งกลับ 255 ไม่ใช่คำสั่งระหว่างเครื่องหมายคำพูด แต่ทำไม?

คำตอบ:


29

การคาดคะเนของคุณว่าเป็นsshตัวเองที่ส่งคืนสถานะทางออก 255 ถูกต้อง sshหน้าคนกล่าวว่า:

ออกจาก ssh ด้วยสถานะออกของคำสั่งระยะไกลหรือ 255 หากมีข้อผิดพลาดเกิดขึ้น

หากคุณเพียงแค่เรียกใช้ssh pi@10.20.0.10 "pkill -f asdf"คุณน่าจะได้รับสถานะการออก1ซึ่งสอดคล้องกับpkillสถานะของ“ ไม่มีกระบวนการที่ตรงกัน

ส่วนที่ท้าทายคือการเข้าใจว่าทำไมข้อผิดพลาดเกิดขึ้นกับ SSH เมื่อคุณเรียกใช้

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

คำสั่งรีโมต SSH

เซิร์ฟเวอร์ SSH เรียกใช้เชลล์เพื่อเรียกใช้คำสั่งระยะไกล นี่คือตัวอย่างของการกระทำนี้:

$ 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

โปรดทราบว่าเชลล์เริ่มต้นคือbashและว่าคำสั่งระยะไกลไม่ใช่คำสั่งง่าย ๆ แต่เป็นไพพ์ไลน์ “ ลำดับของคำสั่งหนึ่งคำสั่งหรือมากกว่านั้นคั่นด้วยตัวดำเนินการควบคุม|

Bash shell นั้นฉลาดพอที่จะรู้ได้ว่าหากคำสั่งนั้นถูกส่งผ่านโดย-cตัวเลือกนั้นเป็นคำสั่งง่าย ๆมันสามารถปรับให้เหมาะสมโดยไม่ทำการปลอมกระบวนการใหม่เช่นมันexecเป็นคำสั่งง่าย ๆโดยตรงแทนที่จะผ่านขั้นตอนพิเศษ ของforkไอเอ็นจีก่อนที่มันจะexecs นี่คือตัวอย่างของสิ่งที่เกิดขึ้นเมื่อคุณเรียกใช้คำสั่งง่าย ๆ ระยะไกล ( ps -elfในกรณีนี้):

$ 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

ฉันเคยเจอพฤติกรรมนี้มาก่อน แต่ฉันไม่พบการอ้างอิงที่ดีกว่าคำตอบ AskUbuntuนี้

พฤติกรรม pkill

เนื่องจากpkill -f asdf || trueไม่ได้เป็นคำสั่งง่ายๆ (เป็นรายการคำสั่ง ), การเพิ่มประสิทธิภาพดังกล่าวข้างต้นไม่สามารถเกิดขึ้นดังนั้นเมื่อคุณทำงานssh pi@10.20.0.10 "pkill -f asdf || true"ที่ส้อมกระบวนการและการบริหารsshdbash -c "pkill -f asdf || true"

ตามคำตอบของ ctx pkillจะไม่ฆ่ากระบวนการของตัวเอง อย่างไรก็ตามมันจะฆ่ากระบวนการอื่นใดที่บรรทัดคำสั่งตรงกับ-fรูปแบบ bash -cคำสั่งตรงกับรูปแบบนี้จึงฆ่ากระบวนการนี้ - พ่อแม่ของตัวเอง (ที่มันเกิดขึ้น)

จากนั้นเซิร์ฟเวอร์ SSH จะเห็นว่ากระบวนการเชลล์ที่เริ่มต้นขึ้นเพื่อเรียกใช้คำสั่งระยะไกลถูกฆ่าโดยไม่คาดคิดดังนั้นจึงรายงานข้อผิดพลาดไปยังไคลเอ็นต์ SSH


1
ในขณะที่คำตอบที่ถูกต้องระบุแหล่งที่มาของปัญหาที่เกิดขึ้นในขณะที่pkillฆ่ากระบวนการเปลือกแม่เพราะรายการหาเรื่องตรงกับ regexp ผมจะยกคำคัดค้านคำศัพท์: x || yคือไม่คำสั่งผสมมันเป็นรายการคำสั่ง
Stéphane Chazelas

@ StéphaneChazelasขอบคุณสำหรับความคิดเห็น ฉันไปโดยการสร้างเงื่อนไขของ Bash เป็นคำสั่งผสมแต่ฉันยอมรับว่ามันสอดคล้องกันมากขึ้นในเชิงตรรกะเพื่อพิจารณาx||yเป็นรายการคำสั่ง ตอนนี้ฉันได้แก้ไขคำตอบของฉันเพื่อรวมลิงก์ไปยังข้อกำหนด POSIX ต่างๆ
Anthony G - ความยุติธรรมสำหรับโมนิก้า

1
โปรดทราบว่าในกรณีทั่วไปมันไม่มากจนไม่สามารถปรับให้เหมาะสมเนื่องจากเป็นรายการคำสั่งที่ sill มีคำสั่งอื่นให้เรียกใช้ (อาจ) ในzsh/ ksh93/ FreeBSD sh, false || pkill -f asdfจะได้pkillดำเนินการในกระบวนการเปลือก bashการปรับให้เหมาะสมเท่านั้นเมื่อมีเพียงคำสั่งเดียวเท่านั้น true; pkill -f asdfก็จะเป็นปัญหา
Stéphane Chazelas

9

คำสั่งระยะไกลของคุณฆ่าตัวเอง:

$ 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 และ pkill จะไม่สนใจกระบวนการของตนเอง แต่ด้วยแฟล็ก -f พวกเขาจะพบพาเรนต์เชลล์:

$ 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

นั่นทำให้รู้สึก! bash -c 'pgrep -af asdf'(ไม่มี|| true) ไม่พบตัวเอง ทำไมจะไม่ล่ะ? -fมันมี
Gauthier

2
@Gauthier จริงๆแล้วฉันคิดว่าในกรณีนี้ Bash ฉลาดพอที่จะรู้ว่าคำสั่งนั้นง่ายมาก (ไม่ใช่คำสั่งแบบผสม) ดังนั้นมันจึงปรับให้เหมาะสมโดยไม่ได้ปลอมกระบวนการใหม่ ฉันจำได้ว่าเคยเจอพฤติกรรมที่คล้ายกันมาก่อน (ฉันต้องอัปเดตคำตอบของฉัน)
Anthony G - ความยุติธรรมสำหรับ Monica

3

คุณขอให้ pkill ฆ่าทุกอย่างที่ตรงกับ "asdf" คุณควรบอกให้ตรงกับ [a] sdf วิธีนั้นจะยังคงมองหาชื่อ "asdf" แต่จะไม่เห็นตัวเอง (หากคุณจัดตำแหน่ง asdf กับ [a] sdf สังเกตว่า s อยู่ในแนวเดียวกันกับ] และ ไม่ใช่ s.)

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

มันเป็นเคล็ดลับทั่วไปที่ใช้กับ 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

เคล็ดลับนี้เก่าและฉันเห็นมันหลายสิบปีที่ผ่านมาในคำถามที่พบบ่อย Unix (ซึ่งยังคงอ่านดี!)

หากต้องการ "ทำให้เป็นอัตโนมัติ" มันไม่ใช่เรื่องง่าย แต่โดยปกติทุกครั้งที่คุณต้อง grep สำหรับตัวแปรสตริง regexp = "บางอย่าง" คุณสามารถลองทำสิ่งต่อไปนี้

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" ...

หมายเหตุ: ฉันทราบว่า grep 'ล้มเหลว' ของฉันเป็นตัวอย่างใครสามารถรักษา regexp ตามที่มันจะไม่ตรงกับตัวเองแล้ว (a, b หรือ c จะไม่ตรงกับ ']' ของ commandline) . แต่มันก็ไม่สำคัญที่จะเกิดขึ้นกับการทดสอบของ regexp โดยทั่วไปเคล็ดลับใช้งานได้ คนที่ทำงานอัตโนมัติจะใช้เวลาส่วนใหญ่ เมื่อไม่เช่นนั้นจะต้องมีการแฮ็กที่ชาญฉลาด
Olivier Dulac

นอกจากนี้(abc)?(def)?จะต้องเป็น([a]bc)?([d]ef)?... คุณไม่สามารถแยก regex ด้วย regex ได้หรือไม่! > :-)
wizzwizz4

@ wizzwizz4 ฉันรู้ แต่ตัวอย่างของคุณจะไม่ตรงกัน นี้เป็นสิ่งที่ซับซ้อนฉันเพียงแค่ให้วิธีง่ายๆสำหรับกรณีที่ง่าย
โอลิเวีย Dulac

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