Ssh पर एक आदेश में सच के साथ


15

जब मैं pkill -fssh के माध्यम से दूरस्थ रूप से चलाने की कोशिश करता हूं , और संभव त्रुटि कोड को छोड़ने की कोशिश करता हूं (मेरी स्क्रिप्ट के बाकी हिस्सों पर जाने के लिए, भले ही कोई प्रक्रिया नहीं मिलती है), || 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और यह कि रिमोट कमांड एक साधारण कमांड नहीं है , लेकिन एक पाइपलाइन है , "कंट्रोल ऑपरेटर द्वारा अलग किए गए एक या अधिक कमांड का एक क्रम |"।

बैश शेल यह महसूस करने के लिए पर्याप्त चतुर है कि यदि -cविकल्प द्वारा इसे पारित किया जा रहा है , तो यह एक साधारण कमांड है , यह वास्तव में नई प्रक्रिया के लिए मजबूर न करके अनुकूलन कर सकता है, अर्थात, यह सीधे execअतिरिक्त चरण के माध्यम से जाने के बजाय सरल कमांड का उपयोग करता है। की forking इससे पहले कि यह execहै। जब आप दूरस्थ सरल कमांड चलाते हैं ( 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", sshdप्रक्रिया कांटे और निष्पादित होती है bash -c "pkill -f asdf || true"

जैसा कि ctx का उत्तर बताता है, pkillअपनी प्रक्रिया को नहीं मारेगा। हालांकि, यह किसी भी अन्य प्रक्रिया को मार देगा जिसकी कमांड लाइन -fपैटर्न से मेल खाती है । bash -cअपने स्वयं के माता पिता (के रूप में यह होता है) - तो यह इस प्रक्रिया को मारता आदेश इस पैटर्न से मेल खाता है।

SSH सर्वर तब देखता है कि दूरस्थ कमांड को चलाने के लिए शुरू की गई शेल प्रक्रिया अप्रत्याशित रूप से मार दी गई थी, इसलिए यह SSH क्लाइंट को एक त्रुटि की सूचना देता है।


1
इस सवाल का जवाब सही ढंग से स्रोत के रूप में एक समस्या की पहचान करता है जबकि pkillक्योंकि इसके आर्ग सूची regexp से मेल खाता अपनी मूल खोल प्रक्रिया को मारता है, मैं एक शब्दावली आपत्ति बढ़ा देंगे: x || yहै नहीं एक यौगिक आदेश है, यह एक है आदेश सूची
स्टीफन चेज़लस

@ StéphaneChazelas प्रतिक्रिया के लिए धन्यवाद। मैं बैश को कंपाउंड कमांड के रूप में सशर्त निर्माणों में शामिल करने जा रहा था, लेकिन मैं मानता हूं कि x||yकमांड लिस्ट के रूप में विचार करने के लिए यह तार्किक रूप से सुसंगत है । मैंने अब अपने जवाब को विभिन्न POSIX परिभाषाओं के लिंक शामिल करने के लिए संपादित किया है।
एंथोनी जोगेगन

1
ध्यान दें कि सामान्य मामले में, यह इतना अधिक नहीं है कि यह अनुकूलन नहीं कर सकता क्योंकि यह एक कमांड सूची है कि यह sill चलाने के लिए एक और कमांड है (संभावित रूप से)। में zsh/ ksh93/ FreeBSD sh, false || pkill -f asdfहोता pkillखोल प्रक्रिया में मार डाला। bashकेवल एक साधारण कमांड होने पर ही अनुकूलन करता है। true; pkill -f asdfयह भी एक समस्या होगी।
स्टीफन चेज़लस

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
गौथियर

2
@Gauthier वास्तव में, मुझे लगता है कि इस मामले में, बैश काफी चतुर है यह महसूस करने के लिए कि कमान एक सरल है (एक यौगिक कमांड नहीं) इसलिए यह वास्तव में एक नई प्रक्रिया के लिए मजबूर न करके अनुकूलन करता है। मुझे याद है कि पहले भी इसी तरह का व्यवहार हुआ था (मुझे अपना जवाब अपडेट करना होगा)।
एंथनी जोगेगन 12

3

आप pkill को "asdf" से मेल खाने वाली किसी भी चीज़ को मारने के लिए कहते हैं। आपको इसे [a] sdf से मेल करने के लिए कहना चाहिए, इस तरह यह अभी भी "asdf" नाम की किसी भी चीज़ की तलाश करेगा, लेकिन खुद को नहीं देखेगा (यदि आप asdf को [a] sdf के साथ संरेखित करते हैं, तो ध्यान दें कि 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

यह ट्रिक पुरानी है, और मैंने इसे दशकों पहले यूनिक्स फेक में देखा था (जो अभी भी एक अच्छा पढ़ा है!)

इसे "स्वचालित" करने के लिए, यह आसान नहीं है, लेकिन आमतौर पर हर बार आपको चर स्ट्रिंग regexp = "कुछ" के लिए grep करने की आवश्यकता होती है, आप यह करने की कोशिश कर सकते हैं:

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

ध्यान दें: मुझे पता है कि मेरी 'असफल' जीआरईपी छूट, कोई भी रेगीक्स को अपने पास रख सकता है, क्योंकि यह पहले से ही खुद से मेल नहीं खाएगा (ए, बी या सी कमांडलाइन के '] के साथ मेल नहीं खाएगा) । लेकिन यह रेगीक्स के परीक्षण के साथ आने वाला तुच्छ नहीं है। सामान्य तौर पर, चाल काम करती है। वह जो ऑटोमेट करता है वह ज्यादातर समय काम करेगा। जब नहीं, कुछ चालाक हैक्स (या मैनुअल हस्तक्षेप) की आवश्यकता होगी।
ओलिवियर दुलक

इसके अलावा, (abc)?(def)?होना चाहिए ([a]bc)?([d]ef)?... आप regex के साथ regex पार्स नहीं कर सकते ?! > :-)
wizzwizz4

@ wizzwizz4 मुझे पता है। लेकिन आपकी छूट पहले से ही मेल नहीं खाएगी। यह एक जटिल बात है, मैंने सिर्फ सरल मामलों के लिए एक सरल समाधान प्रदान किया है
ओलिवियर डुलैक

@ wizzwizz4 मैं पहले से ही अपनी पहली टिप्पणी में ऐसा कहता हूं ...
ओलिवियर डुलैक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.