Ssh $ होस्ट $ FOO और ssh $ होस्ट "sudo su user -c $ FOO" प्रकार के निर्माण में उद्धृत करें


30

मैं अक्सर ssh पर जटिल कमांड जारी करता हूं; इन कमांडों में एक-लाइनों को जगाने या खराब करने के लिए पाइपिंग शामिल है, और परिणामस्वरूप एकल उद्धरण और $ s शामिल हैं। मैं न तो उद्धृत करने के लिए एक कठिन और तेज़ नियम का पता लगा सका हूं, न ही इसके लिए एक अच्छा संदर्भ पाया गया है। उदाहरण के लिए, निम्नलिखित पर विचार करें:

# what I'd run locally:
CMD='pgrep -fl java | grep -i datanode | awk '{print $1}'
# this works with ssh $host "$CMD":
CMD='pgrep -fl java | grep -i datanode | awk '"'"'{print $1}'"'"

(अजीब बयान में अतिरिक्त उद्धरण पर ध्यान दें।)

लेकिन मुझे यह कैसे काम करना है, जैसे ssh $host "sudo su user -c '$CMD'"? क्या ऐसे परिदृश्यों में उद्धरण के प्रबंधन के लिए एक सामान्य नुस्खा है? "

जवाबों:


35

उद्धृत करने के कई स्तरों (वास्तव में, पार्सिंग / व्याख्या के कई स्तरों) से निपटना जटिल हो सकता है। यह कुछ बातों को ध्यान में रखने में मदद करता है:

  • प्रत्येक "उद्धृत करने का स्तर" संभवतः एक अलग भाषा को शामिल कर सकता है।
  • भाषा द्वारा उद्धरण नियम भिन्न होते हैं।
  • जब एक या दो से अधिक नेस्टेड स्तरों के साथ काम करते हैं, तो आमतौर पर "नीचे से ऊपर, ऊपर" (यानी सबसे बाहरी तक) काम करना सबसे आसान होता है।

कोटेशन का स्तर

आइए हम आपके उदाहरण आदेशों को देखें।

pgrep -fl java | grep -i datanode | awk '{print $1}'

आपका पहला उदाहरण कमांड (ऊपर) चार भाषाओं का उपयोग करता है: आपका खोल, pgrep में regex, grep में regex (जो pgrep में regex भाषा से भिन्न हो सकता है ), और awk । इसमें शामिल व्याख्या के दो स्तर हैं: शेल और शामिल कमांड के प्रत्येक के लिए शेल के बाद का एक स्तर। उद्धृत करने का केवल एक ही स्पष्ट स्तर है (शेल को awk में उद्धृत करना )।

ssh host 

इसके बाद आपने शीर्ष पर ssh का स्तर जोड़ा । यह प्रभावी रूप से एक और शेल स्तर है: ssh स्वयं कमांड की व्याख्या नहीं करता है, यह इसे दूरस्थ छोर (जैसे (जैसे) पर एक शेल को सौंपता है sh -c …और यह शेल स्ट्रिंग की व्याख्या करता है।

ssh host "sudo su user -c …"

फिर आपने सु ( सूडो के माध्यम से , जो इसके कमांड तर्कों की व्याख्या नहीं करता है, का उपयोग करके बीच में एक और शेल स्तर जोड़ने के बारे में पूछा , इसलिए हम इसे अनदेखा कर सकते हैं)। इस बिंदु पर, आपके पास नेस्टिंग के तीन स्तर हैं ( awk → shell, shell → shell ( ssh ), shell → shell ( su user -c ), इसलिए मैं "नीचे, ऊपर" दृष्टिकोण का उपयोग करने की सलाह देता हूं। मैं यह मान लूंगा। अपने गोले बॉर्न संगत (जैसे हैं , राख , पानी का छींटा , ksh , बैश , zsh , आदि)। खोल (के कुछ अन्य प्रकार मछली , आर सीआदि) अलग सिंटैक्स की आवश्यकता हो सकती है, लेकिन विधि अभी भी लागू होती है।

नीचे से ऊपर

  1. उस स्ट्रिंग को तैयार करें जिसे आप अंतरतम स्तर पर प्रस्तुत करना चाहते हैं।
  2. अगली-उच्चतम भाषा के उद्धरण प्रदर्शनों के एक उद्धरण तंत्र का चयन करें।
  3. अपने चयनित उद्धृत तंत्र के अनुसार वांछित स्ट्रिंग को कोट करें।
    • अक्सर कई भिन्नताएं होती हैं कि किस तरह के तंत्र को लागू करना है। इसे हाथ से करना आमतौर पर अभ्यास और अनुभव का विषय है। जब इसे प्रोग्रामिक रूप से किया जाता है, तो आमतौर पर सही पाने के लिए सबसे आसान चुनना सबसे अच्छा होता है (आमतौर पर "सबसे शाब्दिक" (कम से कम पलायन))।
  4. वैकल्पिक रूप से, अतिरिक्त कोड के साथ परिणामी स्ट्रिंग का उपयोग करें।
  5. यदि आप अभी तक उद्धृत / व्याख्या के अपने वांछित स्तर तक नहीं पहुंचे हैं, तो परिणामी उद्धृत स्ट्रिंग (प्लस कोई भी जोड़ा गया कोड) लें और इसे चरण 2 में शुरुआती स्ट्रिंग के रूप में उपयोग करें।

शब्दार्थ वारि को उद्धृत करना

यहां ध्यान रखने वाली बात यह है कि प्रत्येक भाषा (कोटिंग लेवल) एक ही कोटिंग कैरेक्टर को थोड़ा अलग शब्दार्थ (या यहां तक ​​कि बहुत अलग शब्दार्थ) दे सकती है।

अधिकांश भाषाओं में एक "शाब्दिक" उद्धृत तंत्र है, लेकिन वे वास्तव में कितने शाब्दिक हैं। बॉर्न जैसे गोले का एकल उद्धरण वास्तव में शाब्दिक है (जिसका अर्थ है कि आप इसका उपयोग एकल उद्धरण चरित्र को उद्धृत करने के लिए नहीं कर सकते हैं)। अन्य भाषाओं (पर्ल, रूबी) कि वे व्याख्या में कम शाब्दिक हैं कुछ भी उद्धृत क्षेत्रों के अंदर बैकस्लैश दृश्यों गैर शाब्दिक (विशेष रूप से, \\और \'में परिणाम \और 'है, लेकिन अन्य बैकस्लैश दृश्यों वास्तव में शाब्दिक हैं)।

आपको इसके उद्धरण नियमों और समग्र वाक्यविन्यास को समझने के लिए अपनी प्रत्येक भाषा के लिए दस्तावेज़ीकरण पढ़ना होगा।

आपका उदाहरण

आपके उदाहरण का अंतरतम स्तर एक अजीब कार्यक्रम है।

{print $1}

आप इसे शेल कमांड लाइन में एम्बेड करने जा रहे हैं:

pgrep -fl java | grep -i datanode | awk 

हमें कम से कम (कम से कम) अंतरिक्ष और जाग कार्यक्रम $में रक्षा करने की आवश्यकता है । स्पष्ट विकल्प पूरे कार्यक्रम के आसपास शेल में एकल उद्धरण का उपयोग करना है।

  • '{print $1}'

हालांकि अन्य विकल्प भी हैं:

  • {print\ \$1} सीधे अंतरिक्ष से बचो और $
  • {print' $'1} एकल उद्धरण केवल स्थान और $
  • "{print \$1}" डबल पूरी बोली और बच निकले $
  • {print" $"1}डबल कोट्स केवल स्पेस है और $
    यह नियमों को थोड़ा झुका सकता है ( $डबल कोटेड स्ट्रिंग के अंत में अनसैप्ड) शाब्दिक है, लेकिन यह अधिकांश गोले में काम करने लगता है।

यदि प्रोग्राम खुले और बंद घुंघराले ब्रेसिज़ के बीच एक अल्पविराम का उपयोग करता है तो हमें कुछ गोले में "ब्रेस विस्तार" से बचने के लिए कॉमा या घुंघराले ब्रेस को भी उद्धृत या बचाना होगा।

हम '{print $1}'इसे बाकी "खोल" कोड में एम्बेड करते हैं:

pgrep -fl java | grep -i datanode | awk '{print $1}'

इसके बाद, आप इसे su और sudo के माध्यम से चलाना चाहते थे ।

sudo su user -c 

su user -c …ऐसा ही है some-shell -c …(कुछ अन्य यूआईडी के तहत चलने के अलावा), इसलिए सु सिर्फ एक और शेल स्तर जोड़ता है। सुडो अपने तर्कों की व्याख्या नहीं करता है, इसलिए यह किसी भी उद्धरण स्तर को नहीं जोड़ता है।

हमें अपने कमांड स्ट्रिंग के लिए एक और शेल स्तर की आवश्यकता है। हम फिर से एकल उद्धरण चुन सकते हैं, लेकिन हमें मौजूदा एकल उद्धरणों को विशेष संभालना होगा। सामान्य तरीका इस तरह दिखता है:

'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'

यहां चार तार हैं जो शेल व्याख्या करेगा और संक्षिप्त करेगा: पहला एकल उद्धृत स्ट्रिंग ( pgrep … awk), एक बची हुई एकल बोली, एकल-उद्धृत awk प्रोग्राम, एक और बची हुई एकल उद्धरण।

बेशक, कई विकल्प हैं:

  • pgrep\ -fl\ java\ \|\ grep\ -i\ datanode\ \|\ awk\ \'{print\ \$1} सब कुछ महत्वपूर्ण है
  • pgrep\ -fl\ java\|grep\ -i\ datanode\|awk\ \'{print\$1}वही, लेकिन शानदार व्हाट्सएप के बिना (यहां तक ​​कि awk प्रोग्राम में भी!)
  • "pgrep -fl java | grep -i datanode | awk '{print \$1}'" डबल पूरी बात बोली, बचो $
  • 'pgrep -fl java | grep -i datanode | awk '"'"'{print \$1}'"'"आपकी भिन्नता; बच निकलने के बजाय दोहरे उद्धरण चिह्नों (दो वर्णों) का उपयोग करने के कारण सामान्य तरीके से थोड़ा लंबा

पहले स्तर में अलग-अलग उद्धरण का उपयोग करना इस स्तर पर अन्य विविधताओं के लिए अनुमति देता है:

  • 'pgrep -fl java | grep -i datanode | awk "{print \$1}"'
  • 'pgrep -fl java | grep -i datanode | awk {print\ \$1}'

Sudo / * su * कमांड लाइन में पहला वेरिएशन एंबेड करते हुए यह दें:

sudo su user -c 'pgrep -fl java | grep -i datanode | awk '\''{print $1}'\'

आप किसी अन्य एकल शेल स्तर संदर्भों (उदाहरण ssh host …) में उसी स्ट्रिंग का उपयोग कर सकते हैं ।

इसके बाद, आपने शीर्ष पर ssh का स्तर जोड़ा । यह प्रभावी रूप से एक और शेल स्तर है: ssh स्वयं कमांड की व्याख्या नहीं करता है, लेकिन यह इसे दूरस्थ छोर (थ्रू) (जैसे) पर एक शेल को सौंपता है sh -c …और यह शेल स्ट्रिंग की व्याख्या करता है।

ssh host 

प्रक्रिया समान है: स्ट्रिंग लें, एक उद्धरण विधि चुनें, इसका उपयोग करें, इसे एम्बेड करें।

एकल उद्धरणों का फिर से उपयोग करना:

'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'

अब ग्यारह तार हैं जिनकी व्याख्या की गई है और संक्षिप्त किया गया है: 'sudo su user -c 'एकल उद्धरण से 'pgrep … awk 'बच गए, एकल उद्धरण से बच गए, बैकस्लैश से बच गए, दो से बच गए एकल उद्धरण, एकल उद्धृत ऑक प्रोग्राम, एक बच गए एकल उद्धरण, एक बच गए बैकलैश, और एक अंतिम एकल उद्धरण से बच गए। ।

अंतिम रूप इस तरह दिखता है:

ssh host 'sudo su user -c '\''pgrep -fl java | grep -i datanode | awk '\'\\\'\''{print $1}'\'\\\'

यह हाथ से टाइप करने के लिए थोड़ा सा बोझिल है, लेकिन शेल के एकल उद्धरण के शाब्दिक स्वरूप से थोड़ी भिन्नता को स्वचालित करना आसान हो जाता है:

#!/bin/sh

sq() { # single quote for Bourne shell evaluation
    # Change ' to '\'' and wrap in single quotes.
    # If original starts/ends with a single quote, creates useless
    # (but harmless) '' at beginning/end of result.
    printf '%s\n' "$*" | sed -e "s/'/'\\\\''/g" -e 1s/^/\'/ -e \$s/\$/\'/
}

# Some shells (ksh, bash, zsh) can do something similar with %q, but
# the result may not be compatible with other shells (ksh uses $'...',
# but dash does not recognize it).
#
# sq() { printf %q "$*"; }

ap='{print $1}'
s1="pgrep -fl java | grep -i datanode | awk $(sq "$ap")"
s2="sudo su user -c $(sq "$s1")"

ssh host "$(sq "$s2")"


7

एक सामान्य समाधान के साथ स्पष्ट, गहराई से स्पष्टीकरण के लिए क्रिस जॉन्सन का उत्तर देखें । मैं कुछ अतिरिक्त सुझाव देने जा रहा हूं जो कुछ सामान्य परिस्थितियों में मदद करते हैं।

एकल उद्धरण सब कुछ से बचते हैं लेकिन एक एकल उद्धरण। इसलिए यदि आप जानते हैं कि किसी वैरिएबल के मूल्य में कोई एकल उद्धरण शामिल नहीं है, तो आप इसे शेल स्क्रिप्ट में एकल उद्धरणों के बीच सुरक्षित रूप से प्रक्षेपित कर सकते हैं।

su -c "grep '$pattern' /root/file"  # assuming there is no ' in $pattern

यदि आपका स्थानीय शेल ksh93 या zsh है, तो आप चर में एकल उद्धरणों के साथ उन्हें फिर से लिखकर सामना कर सकते हैं '\''। (हालांकि बैश में ${foo//pattern/replacement}कंस्ट्रक्शन भी है , लेकिन सिंगल कोट्स की हैंडलिंग मेरे लिए मायने नहीं रखती है।)

su -c "grep '${pattern//'/'\''}' /root/file"  # if the outer shell is zsh
su -c "grep '${pattern//\'/\'\\\'\'}' /root/file"  # if the outer shell is ksh93

नेस्टेड कोटिंग से निपटने के लिए एक और टिप जितना संभव हो उतना पर्यावरण चर के माध्यम से तार पास करना है। Ssh और sudo अधिकांश पर्यावरण चर को छोड़ने की प्रवृत्ति रखते हैं, लेकिन वे अक्सर इसके LC_*माध्यम से जाने के लिए कॉन्फ़िगर किए जाते हैं , क्योंकि ये सामान्य रूप से प्रयोज्य के लिए बहुत महत्वपूर्ण होते हैं (इनमें स्थानीय जानकारी होती है) और शायद ही कभी सुरक्षा के प्रति संवेदनशील माना जाता है।

LC_CMD='what you would use locally' ssh $host 'sudo su user -c "$LC_CMD"'

यहाँ, चूंकि LC_CMDएक शेल स्निपेट होता है, इसलिए इसे शाब्दिक रूप से अंतरतम शेल प्रदान किया जाना चाहिए। इसलिए चर का विस्तार शेल द्वारा तुरंत ऊपर किया गया है। अंतरतम-लेकिन-एक शैल देखता है "$LC_CMD", और अंतरतम शेल कमांड देखता है।

पाठ प्रसंस्करण उपयोगिता के लिए डेटा पास करने के लिए एक समान विधि उपयोगी है। यदि आप शेल इंटरपोलेशन का उपयोग करते हैं, तो उपयोगिता चर के मान को एक कमांड के रूप में मानेगी, उदाहरण के sed "s/$pattern/$replacement/"लिए यदि चर होते हैं तो काम नहीं करेगा /। इसलिए awk (sed नहीं) का उपयोग करें, और या तो इसका -vविकल्प या ENVIRONशेल से डेटा पास करने के लिए सरणी (यदि आप गुजरते हैं ENVIRON, तो चर निर्यात करना याद रखें)।

awk -vpattern="$pattern" replacement="$replacement" '{gsub(pattern,replacement); print}'

2

जैसा कि क्रिस जॉनसन बहुत अच्छी तरह से वर्णन करते हैं, आपके पास यहां उद्धृत अप्रत्यक्ष स्तर के कुछ स्तर हैं; आप अपने स्थानीय हिदायत shellदूरदराज के निर्देश देने shellके माध्यम से sshहै कि यह निर्देश देना चाहिए sudoनिर्देश देने के लिए suदूरदराज के निर्देश देने के लिए shellअपने पाइप लाइन को चलाने के लिए pgrep -fl java | grep -i datanode | awk '{print $1}'के रूप में user। इस तरह के आदेश के लिए बहुत थकाऊ आवश्यकता होती है \'"quote quoting"\'

यदि आप मेरी सलाह लेंगे, तो आप सब बकवास करेंगे और करेंगे:

% ssh $host <<REM=LOC_EXPANSION <<'REMOTE_CMD' |
> localhost_data='$(commands run on localhost at runtime)' #quotes don't affect expansion
> more_localhost_data="$(values set at heredoc expansion)" #remote shell will receive m_h_d="result"
> REM=LOC_EXPANSION
> commands typed exactly as if located at 
> the remote terminal including variable 
> "${{more_,}localhost_data}" operations
> 'quotes and' \all possibly even 
> a\wk <<'REMOTELY_INVOKED_HEREDOC' |
> {as is often useful with $awk
> so long as the terminator for}
> REMOTELY_INVOKED_HEREDOC
> differs from that of REM=LOC_EXPANSION and
> REMOTE_CMD
> and here you can | pipeline operate on |\
> any output | received from | ssh as |\
> run above | in your local | terminal |\
> however | tee > ./you_wish.result
<desired output>

अधिक जानकारी के लिए:

स्लेश प्रतिस्थापन के लिए विभिन्न प्रकार के उद्धरणों के साथ पाइपिंग पथ के लिए मेरे (शायद बहुत लंबे-घुमावदार) उत्तर की जांच करें जिसमें मैं उस काम के पीछे के कुछ सिद्धांत पर चर्चा करता हूं।

-माइक


यह दिलचस्प लग रहा है, लेकिन मैं इसे काम नहीं कर सकता। क्या आप एक न्यूनतम कार्य उदाहरण पोस्ट कर सकते हैं?
जॉन लॉरेंस एस्पेन

मेरा मानना ​​है कि इस उदाहरण को zsh की आवश्यकता है क्योंकि यह स्टड के लिए कई पुनर्निर्देशन का उपयोग करता है। अन्य बॉर्न-जैसे गोले में, दूसरा <<पहले वाले को बदल देता है। क्या इसे कहीं "ज़िश" कहना चाहिए, या मुझे कुछ याद आया? (चालाक चाल हालांकि, एक वंशानुगत है कि आंशिक रूप से स्थानीय विस्तार के अधीन है)

यहाँ एक बैश संगत संस्करण है: unix.stackexchange.com/questions/422489/…
dabest1

0

अधिक दोहरे उद्धरण चिह्नों का उपयोग कैसे करें?

तो आपके ssh $host $CMDइस एक के साथ ठीक काम करना चाहिए:

CMD="pgrep -fl java | grep -i datanode | awk '{print $1}'"

अब और अधिक जटिल एक है, के लिए ssh $host "sudo su user -c \"$CMD\""। मैं तुम सब में संवेदनशील वर्ण से बचें करना है लगता है CMD: $, \और "। तो मैं कोशिश करता हूं और देखता हूं कि क्या यह काम करता है echo $CMD | sed -e 's/[$\\"]/\\\1/g':।

अगर यह ठीक लग रहा है, तो शेल फ़ंक्शन में इको + सेड लपेटें, और आप के साथ जाने के लिए अच्छा है ssh $host "sudo su user -c \"$(escape_my_var $CMD)\""

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