बैश का उपयोग करते समय किन पात्रों से बचना चाहिए?


206

क्या उन पात्रों की कोई व्यापक सूची है, जिन्हें बैश में भागने की आवश्यकता है? क्या इसके साथ ही जाँच की जा सकती है sed?

विशेष रूप से, मैं जाँच कर रहा था कि %भागने की आवश्यकता है या नहीं। मैंने कोशिश की

echo "h%h" | sed 's/%/i/g'

और भागने के बिना, ठीक काम किया %। क्या इसका मतलब यह है कि %बच निकलने की जरूरत नहीं है? क्या यह आवश्यकता की जांच करने का एक अच्छा तरीका था?

और अधिक सामान्य: वे में से बचने के लिए एक ही अक्षर हैं shellऔर bash?


4
सामान्य तौर पर, यदि आप परवाह करते हैं, तो आप इसे गलत कर रहे हैं। डेटा को संभालना कभी भी कोडिंग के लिए उपयोग की जाने वाली पार्सिंग और मूल्यांकन प्रक्रिया के माध्यम से इसे चलाने से नहीं बचना चाहिए, जिससे भागने से बचने में मदद मिलती है। यह एसक्यूएल के लिए सर्वोत्तम प्रथाओं के समानांतर है - जहां राइट थिंकिंग बाइ वैरिएबल का उपयोग करना है और गलत थिंकिंग स्ट्रिंग प्रतिस्थापन के माध्यम से इंजेक्ट किए गए डेटा को "सैनिटाइज" करने की कोशिश है।
चार्ल्स डफी

साथ संबंधित stackoverflow.com/questions/2854655/...
skywinder

8
@CharlesDuffy हाँ, लेकिन कभी-कभी तैयार स्टेटमेंट इंजन बैकएंड पर क्या कर रहा है, बस चीजों से बच रहा है। क्या SO "गलत कर रहा है" क्योंकि वे उपयोगकर्ता-प्रस्तुत टिप्पणियों को ब्राउज़र में प्रदर्शित करने से पहले बच जाते हैं? नहीं, वे XSS को रोक रहे हैं। बिल्कुल भी लापरवाही करना गलत नहीं है।
पार्थियन शॉट

@ParthianShot, यदि तैयार स्टेटमेंट इंजन कोड से डेटा को पूरी तरह से बाहर नहीं रख रहा है, तो इसे लिखने वाले लोगों को गोली मार देनी चाहिए। हां, मुझे पता है कि MySQL के वायर प्रोटोकॉल को उसी तरह से लागू किया गया है; मेरा कथन खड़ा है।
चार्ल्स डफी

@CharlesDuffy और मेरा कहना है कि कभी-कभी आपके विकल्प टूलचिन का उपयोग करके किसी काम को सुरक्षित रूप से करने के लिए होते हैं, जो एक प्यूरिस्ट क्रैंग बना देगा, या इसे सुंदर बनाने के लिए आठ बार समय और प्रयास को सिंक करता है- अभी भी खड़ा है।
पार्थियन शॉट

जवाबों:


282

दो आसान और सुरक्षित नियम हैं जो न केवल में काम करते हैं shबल्कि यह भी है bash

1. पूरे स्ट्रिंग को सिंगल कोट्स में रखें

यह एकल उद्धरण को छोड़कर सभी वर्णों के लिए काम करता है। एकल उद्धरण से बचने के लिए, उद्धरण को बंद करने से पहले, एकल उद्धरण डालें, और उद्धरण को फिर से खोलें।

'I'\''m a s@fe $tring which ends in newline
'

sed कमांड: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. एक बैकस्लैश के साथ हर चार से बच

यह न्यूलाइन को छोड़कर सभी वर्णों के लिए काम करता है। नईलाइन वर्णों के लिए एकल या दोहरे उद्धरण चिह्नों का उपयोग करें। खाली तार अभी भी संभाला जाना चाहिए - के साथ बदलें""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sed कमांड sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/':।

2 बी। 2 का अधिक पठनीय संस्करण

पात्रों का एक आसान सुरक्षित सेट है, जैसे [a-zA-Z0-9,._+:@%/-], जो इसे और अधिक पठनीय रखने के लिए बिना छोड़े छोड़ा जा सकता है

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sed कमांड LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/':।


ध्यान दें कि एक sed प्रोग्राम में, कोई यह नहीं जान सकता है कि इनपुट की अंतिम पंक्ति एक नईलाइन बाइट के साथ समाप्त होती है (सिवाय इसके कि खाली होने पर)। यही कारण है कि दोनों सेड कमांड के ऊपर यह नहीं माना जाता है। आप मैन्युअल रूप से एक उद्धृत न्यूलाइन जोड़ सकते हैं।

ध्यान दें कि शेल चर केवल POSIX अर्थ में पाठ के लिए परिभाषित किए गए हैं। बाइनरी डेटा को संसाधित करना परिभाषित नहीं है। कार्यान्वयन के लिए जो मायने रखता है, बाइनरी एनयूएल बाइट्स के अपवाद के साथ काम करता है (क्योंकि चर सी स्ट्रिंग्स के साथ कार्यान्वित किए जाते हैं, और इसका मतलब सी स्ट्रिंग्स के रूप में उपयोग किया जाता है, अर्थात् प्रोग्राम तर्क), लेकिन आपको "बाइनरी" लोकेल जैसे कि लेट 1 से स्विच करना चाहिए ।


(आप आसानी से POSIX युक्ति पढ़कर नियमों को मान्य कर सकते हैं sh। bash के लिए, @AustinPhillips द्वारा लिंक किए गए संदर्भ मैनुअल की जाँच करें)


नोट: # 1 मधुमक्खी पर एक अच्छा बदलाव यहाँ देखा जा सकता है: github.com/scop/bash-completion/blo// । इसे चलाने की आवश्यकता नहीं है sed, लेकिन आवश्यकता होती है bash
18

4
किसी और के लिए ध्यान दें (मेरे जैसे!) जो इन कामों को करने के लिए संघर्ष करता है .... ऐसा लगता है कि ओएसएक्स पर आपको मिलने वाले सेड का स्वाद इन सेड कमांड को ठीक से नहीं चलाता है। वे हालांकि लिनक्स पर ठीक काम करते हैं!
दलनेन

@ बाल्डेलन: यहाँ परीक्षण नहीं कर सकते। कृपया संपादित करें जब आपके पास एक संस्करण है जो दोनों पर काम करता है।
जो तो

लगता है कि आप चूक गए हैं, स्ट्रिंग को '-' (माइनस) के साथ शुरू करना चाहिए, या क्या यह केवल फ़ाइल नाम पर लागू होता है? - बाद के मामले में सामने '' / 'की जरूरत है।
स्लैशमाईस

मुझे नहीं पता तुम्हारा क्या मतलब है। उन सेड कमांड के साथ इनपुट स्ट्रिंग को स्टड से लिया जाता है।
जो

59

प्रारूप जिसे शेल इनपुट के रूप में पुन: उपयोग किया जा सकता है

इस तरह के अनुरोध के लिए एक विशेष printf प्रारूप निर्देश ( %q) बनाया गया है:

Printf [-v var] प्रारूप [तर्क]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

कुछ नमूने:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

इसका उपयोग चर के माध्यम से भी किया जा सकता है:

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

सभी के साथ त्वरित जांच (128) ascii बाइट्स:

ध्यान दें कि 128 से 255 तक के सभी बाइट्स से बचना होगा।

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

यह कुछ इस तरह प्रस्तुत करना चाहिए:

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

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

क्यों ,?

आप कुछ ऐसे पात्र देख सकते हैं जिन्हें हमेशा भागने की जरूरत नहीं है , जैसे ,, }और {

तो हमेशा नहीं लेकिन कभी-कभी :

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

या

echo test { 1, 2, 3 }
test { 1, 2, 3 }

लेकिन देखभाल:

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 

यह समस्या है कि, bash / sh के माध्यम से pritnf को कॉल करने पर, स्ट्रिंग को पहले bash / sh के लिए बच जाना चाहिए
ThorSummoner

1
@ थोरसुमोनर, यदि आप स्ट्रिंग को एक अलग भाषा से शेल के शाब्दिक तर्क के रूप में पास करते हैं (जहां आप संभवतः पहले से ही जानते हैं कि कैसे बोली जाए)। पायथन में: subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()आपको एक उचित रूप से शेल-उद्धृत संस्करण देगा arbitrary_string
चार्ल्स डफी

1
FYI bash %qलंबे समय से टूट गया था - अगर मेरा दिमाग मुझे अच्छी तरह से काम करता है, तो 2013 में ~ 10 साल तक टूटने के बाद एक त्रुटि (लेकिन अभी भी टूट सकती है) हो गई थी। तो इस पर भरोसा मत करो।
जो तो

@CharlesDuffy, एक बार जब आप पायथन भूमि में होते हैं, shlex.quote()(> = 3.3, pipes.quote()- अनडॉस्मेंटेड - पुराने संस्करणों के लिए) भी काम करेंगे और अधिकांश स्ट्रिंग्स को अधिक मानव-पठनीय संस्करण (उद्धरण जोड़ना और बचाना, आवश्यक रूप से जोड़ना) का उत्पादन करेंगे, एक खोल को फैलाने की आवश्यकता के बिना।
थॉमस पर्ल

1
के बारे में विशेष नोट्स जोड़ने के लिए धन्यवाद ,। मुझे यह जानकर आश्चर्य हुआ कि बिल्ट-इन बैश printf -- %q ','देता है \,, लेकिन /usr/bin/printf -- %q ','देता है ,(अन-एस्कैप्ड)। अन्य वर्ण के लिए एक ही: {, |, }, ~
केविनरपे

34

RTFM के लिए होने ... में से किसी और को बचाने के लिए बैश :

दोहरे उद्धरण चिह्नों में पात्रों संलग्न उद्धरण के भीतर सभी पात्रों का शाब्दिक मूल्य को बरकरार रखता है, के अपवाद के साथ $, `, \, और, जब इतिहास विस्तार सक्षम किया गया है, !

... इसलिए यदि आप बचते हैं (और खुद ही, निश्चित रूप से) तो आप शायद ठीक हैं।

यदि आप संदेह में होने पर अधिक रूढ़िवादी लेते हैं, तो इसे 'एप्रोच' से बाहर निकालें, यह संभव है कि पहचानकर्ता पात्रों (जैसे एएससीआईआई पत्र, संख्या, या '_') से बचकर विशेष अर्थ के साथ वर्ण प्राप्त करने से बचें। यह बहुत संभावना नहीं है कि ये कभी भी (यानी कुछ अजीब POSIX-ish शेल में) का विशेष अर्थ है और इस तरह से बच निकलने की आवश्यकता है।


1
यहाँ ऊपर उद्धृत मैनुअल है: gnu.org/software/bash/manual/html_node/Double-Quotes.html
code_monk

यह एक छोटा, मीठा और अधिकांशतः सही उत्तर है (इसके लिए +1), लेकिन शायद एकल उद्धरण का उपयोग करना और भी बेहतर है - मेरा लंबा उत्तर देखें।
जो तो

26

print '%q' तकनीक का उपयोग करके , हम यह पता लगाने के लिए एक लूप चला सकते हैं कि कौन से अक्षर विशेष हैं:

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

यह यह आउटपुट देता है:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

कुछ परिणाम, जैसे ,थोड़े संदिग्ध दिखते हैं। इस पर @ चार्ल्सडफी के इनपुट प्राप्त करना दिलचस्प होगा।


2
आप मेरे उत्तर के अंतिम पैराग्राफ पर ,थोड़ा संदिग्ध दिखने के लिए उत्तर पढ़ सकते हैं
एफ। होरी

2
ध्यान रखें कि %qपता नहीं है कि शेल के भीतर आप वर्ण का उपयोग करने की योजना कहां बना रहे हैं, इसलिए यह उन सभी पात्रों से बच जाएगा जो किसी भी संभावित शेल संदर्भ में एक विशेष अर्थ रख सकते हैं। ,खुद उसके शेल का कोई विशेष अर्थ नहीं है, लेकिन जैसा कि @ F.Hauri ने अपने उत्तर में बताया है, इसका {...}ब्रेस विस्तार के भीतर एक विशेष अर्थ है : gnu.org/savannah-checkouts/gnu/bash/manual/… यह ऐसा है! जिसे केवल विशिष्ट स्थितियों में विस्तार की आवश्यकता होती है, सामान्य रूप से नहीं: echo Hello World!बस ठीक काम करता है, फिर echo test!testभी असफल हो जाएगा।
मेकी

18

जिन पात्रों को भागने की आवश्यकता होती है वे बॉश की तुलना में बॉर्न या पोसिक्स शेल में भिन्न होते हैं। आम तौर पर (बहुत) बैश उन गोले का एक सुपरसेट है, इसलिए आप जो कुछ भी shellबचते हैं उसे बैश में बच जाना चाहिए।

एक अच्छा सामान्य नियम होगा "यदि संदेह है, तो उससे बचो"। लेकिन कुछ पात्रों से बचना उन्हें एक विशेष अर्थ देता है, जैसे \n। के man bashतहत Quotingऔर इन पृष्ठों में सूचीबद्ध हैं echo

इसके अलावा, किसी भी वर्ण से बचो जो अल्फ़ान्यूमेरिक नहीं है, यह अधिक सुरक्षित है। मुझे एक भी निश्चित सूची का पता नहीं है।

आदमी पृष्ठों को उन सभी को कहीं न कहीं सूचीबद्ध करता है, लेकिन एक जगह नहीं। भाषा सीखें, यह सुनिश्चित करने का तरीका है।

एक जो मुझे पकड़ा है वह है !। यह बैश (और csh) में एक विशेष चरित्र (इतिहास का विस्तार) है लेकिन कोर्न शेल में नहीं है। यहां तक echo "Hello world!"कि समस्याएं भी देता है। एकल-उद्धरणों का उपयोग करना, हमेशा की तरह, विशेष अर्थ को हटा देता है।


1
मुझे विशेष रूप से एक अच्छा सामान्य नियम पसंद आएगा "यदि संदेह में है, तो उससे बचो" सलाह। अभी भी संदेह है कि क्या जाँच के साथ sedयह देखने के लिए पर्याप्त है कि क्या इसे बचाना है। आपके उत्तर के लिए धन्यवाद!
फेडोरक्वि 'एसओ

2
@fedorqui: के साथ जाँच sedआवश्यक नहीं है, आप लगभग किसी भी चीज़ से जाँच कर सकते हैं। sedमुद्दा नहीं है, bashहै। एकल उद्धरणों के अंदर कोई विशेष वर्ण नहीं हैं (एकल उद्धरणों को छोड़कर), आप वहां वर्णों से बच भी नहीं सकते। एक sedकमांड को आमतौर पर सिंगल कोट्स के अंदर होना चाहिए क्योंकि आरई मेटाकाचर्स के पास शेल मेटाचैकर्स के बहुत अधिक ओवरलैप्स होते हैं जो सुरक्षित रहें। अपवाद तब होता है जब शेल वेरिएबल्स को एम्बेड किया जाता है, जिसे सावधानीपूर्वक करना होता है।
cdarke

5
के साथ जाँच करें echo। यदि आप बाहर निकलते हैं तो आप क्या डालते हैं, इससे बचने की आवश्यकता नहीं है। :)
मार्क रीड ने

6

मुझे लगता है कि आप मार के तार के बारे में बात कर रहे हैं। विभिन्न प्रकार के स्ट्रिंग्स हैं जो भागने के लिए आवश्यकताओं का एक अलग सेट है। जैसे। सिंगल कोट्स स्ट्रिंग्स डबल कोटेड स्ट्रिंग्स से अलग हैं।

सबसे अच्छा संदर्भ बैश मैनुअल का उद्धरण अनुभाग है।

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


3
तो यह पुष्टि करता है कि बचना एक आसान समाधान के बिना ऐसा जंगल है , प्रत्येक मामले की जांच करनी होगी। धन्यवाद!
फेडोरक्वी 'एसओ

@fedorqui किसी भी भाषा के साथ, नियमों का एक सेट पालन किया जाना है। बैश स्ट्रिंग भागने के लिए, नियमों का सेट मैनुअल में वर्णित के अनुसार काफी छोटा है। उपयोग करने के लिए सबसे आसान स्ट्रिंग एकल उद्धरण है क्योंकि कुछ भी बचने की जरूरत नहीं है। हालाँकि, एकल उद्धरण स्ट्रिंग में एकल उद्धरण शामिल करने का कोई तरीका नहीं है।
ऑस्टिन फिलिप्स

@fedorqui। यह जंगल नहीं है। भागने काफी उल्लेखनीय है। मेरी नई पोस्ट देखें
जो तो

@fedorqui आप एकल-उद्धृत स्ट्रिंग के अंदर एक भी उद्धरण का उपयोग नहीं कर सकते हैं, लेकिन आप इसे कुछ इस तरह से "बच" सकते हैं: 'पाठ' "" अधिक पाठ '
CR।

4

मैंने देखा कि ऑटो-पूर्ण का उपयोग करते समय बैश स्वचालित रूप से कुछ पात्रों से बच जाता है।

उदाहरण के लिए, यदि आपके पास नाम की निर्देशिका है dir:A, तो bash स्वतः पूर्ण हो जाएगाdir\:A

इसका उपयोग करते हुए, मैंने ASCII तालिका के पात्रों का उपयोग करते हुए कुछ प्रयोग किए और निम्नलिखित सूचियाँ प्राप्त कीं:

स्वत: पूर्ण होने पर भागने वाले वर्ण : (स्थान शामिल हैं)

 !"$&'()*,:;<=>?@[\]^`{|}

चरित्र जो बैश नहीं होते हैं :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

(मुझे बाहर रखा गया /, क्योंकि इसका उपयोग निर्देशिका नामों में नहीं किया जा सकता है)


2
यदि आप वास्तव में एक व्यापक सूची बनाना चाहते थे, तो मैं सुझाव दूंगा कि कौन-से वर्णों printf %qको देखता है और संशोधित नहीं करता है यदि तर्क के रूप में पारित किया जाता है - आदर्श रूप से, पूरे वर्णमाला के माध्यम से जा रहा है।
चार्ल्स डफी

ऐसे उदाहरण हैं जहां एपोस्ट्रोफ स्ट्रिंग के साथ भी, आप विशेष वर्णों का निर्माण करने के लिए अक्षरों और संख्याओं से बचने की इच्छा कर सकते हैं। उदाहरण के लिए: tr '\ n' '\ t' जो न्यूलाइन वर्णों का टैब वर्णों में अनुवाद करता है।
डिक गुएर्टिन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.