नियमित अभिव्यक्तियों का उपयोग करके बैश में खोजें और बदलें


161

मैंने यह उदाहरण देखा है:

hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//[0-9]/}

जो इस सिंटैक्स का अनुसरण करता है: ${variable//pattern/replacement}

दुर्भाग्य से patternक्षेत्र पूर्ण रेगेक्स सिंटैक्स का समर्थन नहीं करता है (यदि मैं उपयोग करता हूं .या \s, उदाहरण के लिए, यह शाब्दिक वर्णों से मेल खाने की कोशिश करता है)।

मैं पूर्ण रेगेक्स सिंटैक्स का उपयोग करके स्ट्रिंग को कैसे खोज / बदल सकता हूं?


यहां एक संबंधित प्रश्न मिला: stackoverflow.com/questions/5658085/…
jheddings

2
FYI करें, \sमानक POSIX परिभाषित नियमित अभिव्यक्ति सिंटैक्स का हिस्सा नहीं है (न ही BRE या ERE); यह एक पीसीआर एक्सटेंशन है, और ज्यादातर शेल से उपलब्ध नहीं है। [[:space:]]अधिक सार्वभौमिक समकक्ष है।
चार्ल्स डफी

1
\sद्वारा प्रतिस्थापित किया जा सकता है [[:space:]], वैसे, .द्वारा ?, और आधारभूत खोल पैटर्न भाषा को extglob एक्सटेंशन वैकल्पिक उपसमूहों, बार-बार समूहों जैसी चीजों के लिए इस्तेमाल किया जा सकता है, और पसंद है।
चार्ल्स डफी


मैं सोलारिस पर बाश संस्करण 4.1.11 में इसका उपयोग करता हूं ... गूंज $ {हैलो // [0-9]} अंतिम स्लैश की कमी को नोटिस करें।
डैनियल लिस्टन

जवाबों:


175

Sed का उपयोग करें :

MYVAR=ho02123ware38384you443d34o3434ingtod38384day
echo "$MYVAR" | sed -e 's/[a-zA-Z]/X/g' -e 's/[0-9]/N/g'
# prints XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

ध्यान दें कि बाद -eके क्रम में संसाधित किया जाता है। साथ ही, gअभिव्यक्ति के लिए ध्वज इनपुट में सभी घटनाओं से मेल खाएगा।

आप इस पद्धति का उपयोग करके अपने पसंदीदा टूल को भी चुन सकते हैं, जैसे कि perl, awk, उदा:

echo "$MYVAR" | perl -pe 's/[a-zA-Z]/X/g and s/[0-9]/N/g'

यह आपको अधिक रचनात्मक मैच करने की अनुमति दे सकता है ... उदाहरण के लिए, ऊपर दिए गए स्निप में, संख्यात्मक प्रतिस्थापन का उपयोग नहीं किया जाएगा जब तक कि पहले अभिव्यक्ति (आलसी andमूल्यांकन के कारण ) पर एक मैच न हो । और निश्चित रूप से, आपके पास अपनी बोली लगाने के लिए पर्ल का पूर्ण भाषा समर्थन है ...


यह केवल एक ही जगह देता है जहाँ तक मैं बता सकता हूँ। वहाँ एक तरीका है यह पैटर्न के सभी घटनाओं को प्रतिस्थापित करने के लिए है जैसे कि मैंने जो कोड पोस्ट किया है?
लानारू

मैंने अपने जवाब को कई प्रतिस्थापनों के साथ-साथ वैश्विक पैटर्न मिलान के लिए अद्यतन किया है। यदि इससे सहायता मिलती है तो मुझे बताएं।
झिंग्स

बहुत बहुत धन्यवाद! जिज्ञासा से बाहर, आपने एक लाइन संस्करण (अपने मूल उत्तर में) से दो-लाइनर पर स्विच क्यों किया?
लानारू

9
sedप्रक्रिया आरंभीकरण समय के कारण अन्य बाहरी उपकरणों का उपयोग करना महंगा है। मैंने विशेष रूप से ऑल-बैश समाधान की खोज की, क्योंकि मैंने sedअपने लूप में प्रत्येक आइटम के लिए कॉल करने की तुलना में 3 डी से अधिक तेज होने के लिए बैश प्रतिस्थापन का उपयोग किया ।
rr-

6
@CiroSantilli 六四 事件 ill 威 视 granted, दी, यह सामान्य ज्ञान है, लेकिन यह बुद्धिमान नहीं है। हां, बैश धीमा है, चाहे जो भी हो - लेकिन अच्छी तरह से लिखा हुआ बैश जो उप-प्रकारों से बचता है, शाब्दिक रूप से बैश की तुलना में तेजी से आदेश है जो हर छोटे से छोटे काम के लिए बाहरी उपकरण कहता है। इसके अलावा, अच्छी तरह से लिखी गई शेल स्क्रिप्ट तेजी से दुभाषियों (जैसे ksh93, जो awk के साथ सममूल्य पर प्रदर्शन करती है) से लाभान्वित होंगी, जबकि खराब-लिखित लोगों के लिए ऐसा कुछ नहीं है।
चार्ल्स डफी

133

यह वास्तव में शुद्ध बैश में किया जा सकता है:

hello=ho02123ware38384you443d34o3434ingtod38384day
re='(.*)[0-9]+(.*)'
while [[ $hello =~ $re ]]; do
  hello=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
done
echo "$hello"

... पैदावार ...

howareyoudoingtodday

2
कुछ मुझे बताता है कि आप इन से प्यार करेंगे: stackoverflow.com/questions/5624969/… =)
निकल-

=~क्या चाबी है। लेकिन थोड़ा क्लंकी, लूप में पुनर्मूल्यांकन दिया। @ झेजिंग समाधान 2 साल पहले एक और अच्छा विकल्प है - कॉलिंग सेड या पर्ल)।
ब्रेंट फ़ॉस्ट

3
कॉलिंग sedया perlसमझदार है, यदि इनपुट के एक से अधिक लाइन प्रोसेस करने के लिए प्रत्येक इनवोकेशन का उपयोग किया जाता है। लूप के अंदर पर इस तरह के एक उपकरण को आमंत्रित करना, जैसा कि इसके आउटपुट स्ट्रीम को संसाधित करने के लिए लूप का उपयोग करने के विपरीत है, मूर्ख है।
चार्ल्स डफी

2
FYI करें, zsh में, यह $matchइसके बजाय है $BASH_REMATCH। (आप इसके साथ बैश की तरह व्यवहार कर सकते हैं setopt bash_rematch।)
मैरिएन

यह अजीब है - अशुभ, क्योंकि zsh एक POSIX शेल होने की कोशिश नहीं कर रहा है, यह यकीनन POSIX मार्गदर्शन पत्र के बारे में है जो POSIX- निर्दिष्ट (शेल या सिस्टम-प्रासंगिक) उद्देश्यों के लिए इस्तेमाल किया जा रहा है और लोअरकेस वेरिएबल के लिए आरक्षित किया जा रहा है अनुप्रयोग का उपयोग करें। लेकिन zsh के रूप में inasmuch एक ऐसी चीज है जो अनुप्रयोगों को चलाता है, केवल एक एप्लिकेशन के बजाय, सिस्टम नेमस्पेस के बजाय एप्लिकेशन वेरिएबल नेमस्पेस का उपयोग करने का यह निर्णय भयानक रूप से विकृत लगता है।
चार्ल्स डफी

95

इन उदाहरणों में भी बेहोश करने की क्रिया का उपयोग करने की आवश्यकता नहीं है:

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[a-zA-Z]/X} 
echo ${MYVAR//[0-9]/N}

आप वर्ण वर्ग ब्रैकेट अभिव्यक्तियों का भी उपयोग कर सकते हैं

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day
MYVAR=${MYVAR//[[:alpha:]]/X} 
echo ${MYVAR//[[:digit:]]/N}

उत्पादन

XXNNNNNXXXXNNNNNXXXNNNXNNXNNNNXXXXXXNNNNNXXX

हालाँकि @Lanaru जानना चाहता था, अगर मैं इस प्रश्न को सही ढंग से समझता हूं, तो "पूर्ण" या PCRE एक्सटेंशन \s\S\w\W\d\Dआदि php रूबी पाइथन आदि के रूप में काम क्यों नहीं करते। ये एक्सटेंशन पर्ल-संगत रेग्युलर एक्सप्रेशंस (PCRE) से हैं। शेल के नियमित रूप के अन्य रूपों के साथ संगत नहीं हो सकता है।

ये काम नहीं करते:

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo ${hello//\d/}


#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | sed 's/\d//g'

सभी शाब्दिक "डी" वर्णों के साथ आउटपुट हटा दिया गया

ho02123ware38384you44334o3434ingto38384ay

लेकिन निम्नलिखित उम्मीद के मुताबिक काम करता है

#!/bin/bash
hello=ho02123ware38384you443d34o3434ingtod38384day
echo $hello | perl -pe 's/\d//g'

उत्पादन

howareyoudoingtodday

आशा है कि चीजों को थोड़ा और स्पष्ट करता है, लेकिन अगर आप अभी तक भ्रमित नहीं हैं, तो आप मैक ओएस एक्स पर यह कोशिश क्यों नहीं करते हैं जिसमें REG_ENHANCED ध्वज सक्षम है:

#!/bin/bash
MYVAR=ho02123ware38384you443d34o3434ingtod38384day;
echo $MYVAR | grep -o -E '\d'

* Nix के अधिकांश स्वादों पर आपको केवल निम्न आउटपुट दिखाई देंगे:

d
d
d

एन ज्वॉय!


6
क्षमा करें? ${foo//$bar/$baz}है यह fnmatch () - - शैली पैटर्न मिलान POSIX.2 BRE या ERE वाक्य-विन्यास।
चार्ल्स डफी

8
... इसलिए, जबकि ${hello//[[:digit:]]/}काम करता है, अगर हम को फ़िल्टर केवल पत्र से पहले अंक करना चाहता था o, ${hello//o[[:digit:]]*}उम्मीद (fnmatch पैटर्न में के बाद से एक की तुलना में एक पूरी तरह से अलग व्यवहार होता है, *सब से मेल खाता है, बल्कि तुरंत पहले आइटम संशोधित होने के लिए की तुलना में 0 या अधिक)।
चार्ल्स डफी

1
देखें pubs.opengroup.org/onlinepubs/9699919799/utilities/... (और सभी कि यह संदर्भ द्वारा शामिल किया गया है) fnmatch पर पूर्ण कल्पना के लिए।
चार्ल्स डफी

1
आदमी बैश: एक अतिरिक्त बाइनरी ऑपरेटर, = ~, उपलब्ध है, जैसा कि == और =! जब इसका उपयोग किया जाता है, तो ऑपरेटर के दाईं ओर स्ट्रिंग को एक विस्तारित नियमित अभिव्यक्ति माना जाता है और तदनुसार मिलान किया जाता है (जैसा कि रेगेक्स (3) में)।
निकल- ०

1
@aderchox आप अंक के लिए आप उपयोग कर सकते हैं सही हैं, [0-9]या[[:digit:]]
nickl-

13

यदि आप बार-बार कॉल कर रहे हैं और प्रदर्शन से चिंतित हैं, तो यह परीक्षण बताता है कि BASH विधि ~ 15x तेज है और यह किसी भी अन्य बाहरी प्रक्रिया के साथ छेड़खानी करने की संभावना है।

hello=123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X123456789X

P1=$(date +%s)

for i in {1..10000}
do
   echo $hello | sed s/X//g > /dev/null
done

P2=$(date +%s)
echo $[$P2-$P1]

for i in {1..10000}
do
   echo ${hello//X/} > /dev/null
done

P3=$(date +%s)
echo $[$P3-$P2]

1
यदि आप कांटे कम करने के लिए रास्ते में रुचि रखते हैं, तो इस उत्तर में newConnector शब्द की खोज करें कि कैसे Bash में कमांड के आउटपुट में एक चर सेट करें?
एफ। होरी

8

[[:digit:]]पैटर्न के रूप में उपयोग (डबल ब्रैकेट्स पर ध्यान दें):

$ hello=ho02123ware38384you443d34o3434ingtod38384day
$ echo ${hello//[[:digit:]]/}
howareyoudoingtodday

बस जवाबों को संक्षेप में प्रस्तुत करना चाहता था (विशेषकर @ nicl -ss https://stackoverflow.com/a/22261334/2916086 )।

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