मैं शब्द विभाजन से बचने के लिए स्ट्रिंग के अंदर एक चर विस्तार कैसे उद्धृत कर सकता हूं?


9
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

ऐसे मामले में, मैं $myvarमूल्य के सफेद रिक्त स्थान के कारण शब्द विभाजन से बचने के लिए कैसे उद्धृत कर सकता हूं myvar?


4
कोई फर्क नहीं पड़ता कि आप इसे कैसे हल करते हैं, यह अभी भी बहुत कुछ नहीं करेगा। cdकेवल अंदर प्रभाव पड़ता है bash -cखोल।
Kusalananda

मैं केवल प्रश्न के लिए केवल एक न्यूनतम उदाहरण दे रहा हूं, यह मेरी वास्तविक समस्या का काम करने वाला समाधान नहीं है, जो कि unix.stackexchange.com/a/269080/674 प्लस है जिसे कमांड स्ट्रिंग में दिया गया है, जिसका sudo bash -cएक चर विस्तार है एक पथनाम जिसमें व्हाट्सएप हो सकता है।
टिम

जवाबों:


13

उस कोड में कोई शब्द विभाजन नहीं है (जैसा कि उस कोड में वैरिएबल को विभाजित करता है) उस कोड में $myvarनहीं है जैसा कि अयोग्य नहीं है।

हालांकि एक कमांड इंजेक्शन भेद्यता $myvarहै जिसे पास करने से पहले विस्तारित किया जाता है bash। तो इसकी सामग्री को बैश कोड के रूप में व्याख्यायित किया जाता है!

वहाँ के रिक्त स्थान शब्द विभाजन केcd कारण नहीं , बल्कि कई तर्क दिए जाएंगे , क्योंकि उन्हें शेल सिंटैक्स में कई टोकन के रूप में पार्स किया जाएगा। उस मूल्य के साथ , जो रिबूट करेगा! Thatbye;reboot

यहाँ आप चाहते हैं:

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

(जहां की सामग्री को पारित $myvarकि इनलाइन स्क्रिप्ट के पहले तर्क के रूप में; टिप्पणी कैसे दोनों $myvarऔर $1रोकें आईएफएस-शब्द-बंटवारे (और ग्लोबिंग) करने के लिए अपने-अपने खोल के लिए उद्धृत किया गया)।

या:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'

(जहां आप $myvarएक पर्यावरण चर में सामग्री पास करते हैं )।

बेशक आप केवल cd उस इनलाइन लिपि में चलकर कुछ उपयोगी हासिल नहीं करेंगे (यह जाँचने के अलावा कि वहाँ क्या rootहो सकता cdहै)। संभवतः, आप उस स्क्रिप्ट को cdवहां चाहते हैं और फिर वहां कुछ और करते हैं जैसे:

sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"

यदि इरादा एक निर्देशिका में sudoसक्षम होने के लिए उपयोग करने का था cdजिसे आप अन्यथा उपयोग नहीं करते हैं, तो यह वास्तव में काम नहीं कर सकता है।

sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"

में bashअपनी वर्तमान निर्देशिका के साथ एक इंटरैक्टिव शुरू होगा $myvar। लेकिन वह शेल के रूप में चल रहा होगा root

तुम यह कर सकते थे:

sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"

bashवर्तमान निर्देशिका के साथ एक अनपेक्षित इंटरएक्टिव प्राप्त करने के लिए $myvar, लेकिन अगर आपके पास cdउस निर्देशिका में पहली बार अनुमति नहीं है, तो आप उस निर्देशिका में कुछ भी नहीं कर पाएंगे, भले ही वह आपकी वर्तमान कार्यशील निर्देशिका हो।

$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied

एक अपवाद यह होगा कि यदि आपके पास निर्देशिका की खोज की अनुमति है, लेकिन इसके पथ के निर्देशिका घटकों में से एक के लिए नहीं:

$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied

¹ ( $myvarजैसे $(seq 10)) के मूल्यों के लिए कड़ाई से बोलना, शब्द कमांड प्रतिस्थापन के विस्तार पर निश्चित रूप से शब्द विभाजन होगा जैसे कि bash शेल द्वाराroot


4

जादू को उद्धृत करते हुए नया (बैश 4.4) है जो आपको सीधे वही करने की अनुमति देता है जो आप चाहते हैं। बड़े संदर्भ के आधार पर, अन्य तकनीकों में से एक का उपयोग करना बेहतर हो सकता है, लेकिन यह इस सीमित संदर्भ में काम करता है।

sudo bash -c "cd ${myvar@Q}; pwd"

4

यदि आप सामग्री पर भरोसा नहीं कर सकते हैं $myvar, तो इसका printfबचा हुआ संस्करण बनाने के लिए GNU का उपयोग करना एकमात्र उचित तरीका है :

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

जैसा कि printfमैन पेज कहता है:

%q

ARGUMENT एक प्रारूप में मुद्रित होता है जिसे शेल इनपुट के रूप में पुन: उपयोग किया जा सकता है, प्रस्तावित POSP $''सिंटैक्स के साथ गैर-मुद्रण योग्य वर्णों से बचकर ।


इसके एक पोर्टेबल संस्करण के लिए मेरा जवाब देखें।
आर .. गिटहब स्टॉप हेल्पिंग आईसीई

GNU printfको केवल GNU सिस्टम पर ही लागू किया जाएगा और यदि $(printf '%q' "$myvar")इसे शेल में चलाया जाता है जहाँ printfबिल्ट नहीं है। ज्यादातर गोले printfआजकल बन चुके हैं। %qहालांकि सभी समर्थन नहीं करते हैं और जब वे करते हैं, तो वे संबंधित शेल द्वारा समर्थित एक प्रारूप में बात करते हैं, जो जरूरी नहीं कि उसी के अनुसार हो सकता है bash। वास्तव में, bashके printfखुद भी के कुछ मूल्यों के लिए के लिए ठीक से बातें बोली में विफल रहता है $myvarकुछ स्थानों में।
स्टीफन चेजलस

साथ bash-4.3कम से कम, कि अभी भी साथ एक कमांड इंजेक्शन भेद्यता है myvar=$'\xa3``reboot`\xa3`' में एक zh_HK.big5hkscs उदाहरण के लिए स्थान में।
स्टीफन चेज़लस

3

सबसे पहले, जैसा कि दूसरों ने उल्लेख किया है कि आपकी cdकमान बेकार है क्योंकि यह एक शेल के संदर्भ में होता है जो तुरंत बाहर निकल जाता है। लेकिन सामान्य तौर पर सवाल समझ में आता है। आपको जिस चीज की आवश्यकता होती है वह एक मनमाने ढंग से स्ट्रिंग को कोट करने का एक तरीका है , ताकि इसे एक संदर्भ में इस्तेमाल किया जा सके जहां इसे शेल-उद्धृत स्ट्रिंग के रूप में व्याख्या किया जाएगा। मेरे पास मेरे sh ट्रिक्स पेज में इसका एक उदाहरण है :

quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }

इस फ़ंक्शन के साथ आप तब कर सकते हैं:

myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"

3

स्टीफन चेज़लस जो सुझाव देते हैं वह निश्चित रूप से ऐसा करने का सबसे अच्छा तरीका है। मैं बस एक sedउपप्रकार का उपयोग करने के लिए विरोध के रूप में पैरामीटर विस्तार वाक्यविन्यास के साथ सुरक्षित रूप से बोली करने के बारे में एक उत्तर प्रदान करूंगा , जैसे कि आर .. का उत्तर।

myvar='foo \'\''"bar'

quote() {
  printf "'%s'" "${1//\'/\'\\\'\'}"
}

printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"

उस स्क्रिप्ट का आउटपुट है:

value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar

1

हाँ, शब्द विभाजन है।
अच्छी तरह से, तकनीकी रूप से, कमांड लाइन बश पार्सिंग लाइन को विभाजित करती है।

आइए सबसे पहले सही उद्धृत करते हैं:

काम करने का सबसे सरल (और असुरक्षित) आदेश प्राप्त करने के लिए काम करता है जैसा कि मुझे विश्वास है कि आप इसे चाहते हैं:

bash -c "cd \"$myvar\""

आइए पुष्टि करें कि क्या होता है (मानकर myvar="/path to/my directory"):

$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>

जैसा कि आप देख सकते हैं, पथ स्ट्रिंग रिक्त स्थान पर विभाजित किया गया था। तथा:

$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>

विभाजित नहीं है।

हालाँकि, कमांड (जैसा कि लिखा गया) असुरक्षित है। बेहतर उपयोग:

$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory

यह एक डैश (-) या निम्न लिंक के साथ शुरू होने वाली फ़ाइलों के खिलाफ सीडी की रक्षा करेगा जो समस्याओं का कारण बन सकता है।

यह काम करेगा यदि आप भरोसा कर सकते हैं कि स्ट्रिंग $myvarएक पथ है।
हालाँकि, "कोड इंजेक्शन" अभी भी संभव है क्योंकि आप "एक स्ट्रिंग निष्पादित कर रहे हैं":

$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May  8 12:25:51 UTC 2018

आपको स्ट्रिंग के एक मजबूत उद्धरण की आवश्यकता है, जैसे:

$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>

जैसा कि आप ऊपर देख सकते हैं, मूल्य की व्याख्या नहीं की गई थी, लेकिन बस के रूप में इस्तेमाल किया गया था "$1'

अब हम (सुरक्षित रूप से) sudo का उपयोग कर सकते हैं:

$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory

यदि कई तर्क हैं, तो आप उपयोग कर सकते हैं:

$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"

-2

यहां एकल उद्धरण काम नहीं करेंगे, तब से आपके चर का विस्तार नहीं किया जाएगा। यदि आप आश्वस्त हैं कि आपके मार्ग के लिए परिवर्तनशील है, तो सरलतम उपाय यह है कि परिणामी विस्तार के चारों ओर केवल उद्धरण जोड़ें, एक बार यह नए बैश शेल में है:

sudo bash -c "cd \"$myvar\""

2
अभी भी एक कमांड इंजेक्शन भेद्यता, जैसे की मूल्यों के $myvarलिए $(reboot)या"; reboot; #
स्टीफन चेज़लस

1
उपयोग sudo bash -c "cd -P -- '$myvar'"करने से समस्या को केवल उन मूल्यों तक सीमित कर दिया जाएगा $myvar , जिनमें एकल उद्धरण वर्ण होते हैं, जो सैनिटाइज़ करना आसान होगा।
स्टीफन चेज़लस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.