एक चर में वर्णों को बदलने का सबसे छोटा तरीका


18

एक चर में वर्णों को बदलने के कई तरीके हैं।

trअब तक का सबसे छोटा तरीका मुझे पता चला है :

OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT

क्या कोई तेज़ तरीका है? और क्या यह उद्धरण-जैसे उद्धरणों के लिए सुरक्षित है ', "और `स्वयं?


मुझे लगता है कि आप उपयोग कर सकते हैं tr। BASH का PE अच्छा है लेकिन इस मामले में tr ज्यादा तेज है। उदाहरण के लिए echo "$OUTPUT" | tr -dc '[[:alpha:]]' जब से आप केवल अल्फ़ान्यूमेरिक्स करना चाहते हैं
वैलेंटाइन बजरमी

2
चूंकि आप उद्धरण-प्रेमी होने में रुचि रखते हैं: हमेशा अपने चर को उद्धृत करें! echo "$OUTPUT"। या बेहतर printf "%s\n" "$OUTPUT":। (क्या होता है जब OUTPUT="-n"?)
मुसिल

आप ब्राउज़िंग कोडगुल्फ़ पर भी विचार कर सकते हैं , विशेष रूप से बैश टिप्स।
होसिएरेई

जवाबों:


22

चलो देखते हैं। सबसे छोटा जो मैं आपके साथ आ सकता हूं वह आपके trसमाधान का एक मोड़ है :

OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"

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

OUTPUT="${OUTPUT//[\'\"\`]}"

और sedहां, हालांकि यह पात्रों के संदर्भ में लंबा है:

OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"

मुझे यकीन नहीं है कि आप लंबाई में कम या समय के संदर्भ में मतलब है। लंबाई के लिहाज से ये दोनों उतने ही छोटे हैं (जितने वैसे भी मिल सकते हैं) जब यह उन विशिष्ट पात्रों को हटाने की बात आती है। तो, जो सबसे तेज है? मैंने OUTPUTआपके उदाहरण में चर को सेट करके परीक्षण किया लेकिन कई दर्जन बार दोहराया:

$ echo ${#OUTPUT} 
4900

$ time tr -d "\"\`'" <<<$OUTPUT
real    0m0.002s
user    0m0.004s
sys     0m0.000s
$ time sed s/[\'\"\`]//g <<<$OUTPUT
real    0m0.005s
user    0m0.000s
sys     0m0.000s
$ time echo ${OUTPUT//[\'\"\`]}
real    0m0.027s
user    0m0.028s
sys     0m0.000s

जैसा कि आप देख सकते हैं, trस्पष्ट रूप से सबसे तेज़ है, इसके बाद निकटता से sed। इसके अलावा, ऐसा लगता है कि उपयोग echoकरना वास्तव में उपयोग करने की तुलना में थोड़ा तेज है <<<:

$ for i in {1..10}; do 
    ( time echo $OUTPUT | tr -d "\"\`'" > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0025
$ for i in {1..10}; do 
    ( time tr -d "\"\`'" <<<$OUTPUT > /dev/null ) 2>&1 
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0029

चूंकि अंतर छोटा है, मैंने दोनों में से प्रत्येक के लिए 10 बार उपरोक्त परीक्षण चलाए और यह पता चला कि सबसे तेज़ वास्तव में वह है जिसे आपको शुरू करना था:

echo $OUTPUT | tr -d "\"\`'" 

हालाँकि, यह परिवर्तन जब आप एक चर को निर्दिष्ट करने के ओवरहेड को ध्यान में रखते हैं, तो यहां, trसाधारण प्रतिस्थापन की तुलना में थोड़ा धीमा है:

$ for i in {1..10}; do
    ( time OUTPUT=${OUTPUT//[\'\"\`]} ) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0032

$ for i in {1..10}; do
    ( time OUTPUT=$(echo $OUTPUT | tr -d "\"\`'")) 2>&1
  done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}'; 
0.0044

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


4
चूंकि ओपी संशोधित मूल्य को वापस स्थापित करने में रुचि रखता है OUTPUT, इसलिए आपको कमांड प्रतिस्थापन उप-शेल ओवरहेड में शामिल होना होगा trऔर sedसमाधान
iruvar

@ 1_CR हाँ, लेकिन जब से वह जिस विधि का उपयोग करता है, मैं उसे अप्रासंगिक मानता हूँ।
terdon

1
काफी नहीं, OUTPUT="${OUTPUT//[`\"\']/}" कमांड प्रतिस्थापन शामिल नहीं है
iruvar

@ 1_CR आह, मैं देखता हूं, हां, आप काफी सही हैं और इससे परिणाम बदल जाता है। धन्यवाद, उत्तर संपादित
terdon

2
जिन विधियों में कमांड प्रतिस्थापन शामिल होता है, उनमें कुछ हद तक स्ट्रिंग का प्रबंधन होता है। (आप इसे टाल सकते हैं लेकिन कमांड को अधिक जटिल बनाने की कीमत पर।) विशेष रूप से, कमांड प्रतिस्थापन नए सिरे से पीछे हट जाता है।
गिल्स एसओ- बुराई को रोकना '

15

आप चर प्रतिस्थापन का उपयोग कर सकते हैं :

$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d

उस सिंटैक्स का उपयोग करें: ${parameter//pattern/string}स्ट्रिंग के साथ पैटर्न की सभी घटनाओं को बदलने के लिए।

$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd

@ रूबो77 echo ${OUTPUT//[`\"\']/x}देता हैaxbxcxa
अराजकता

विस्तार "चर विस्तार" नाम देना गलत है। इसे "पैरामीटर विस्तार" कहा जाता है।
gena2x

@ gena2x - मुझे समझ नहीं आ रहा है कि आपकी टिप्पणी का यहाँ क्या अर्थ है?
SLM

12

बाश या zsh में यह है:

OUTPUT="${OUTPUT//[\`\"\']/}"

ध्यान दें कि ${VAR//PATTERN/}पैटर्न के सभी उदाहरणों को हटा देता है। अधिक जानकारी के लिए बैश पैरामीटर विस्तार

यह समाधान छोटे तारों के लिए सबसे तेज़ होना चाहिए क्योंकि इसमें किसी भी बाहरी प्रोग्राम को चलाना शामिल नहीं है। हालांकि बहुत लंबे तार के विपरीत विपरीत सच है - पाठ के संचालन के लिए समर्पित उपकरण का उपयोग करना बेहतर है, उदाहरण के लिए:

$ OUTPUT="$(cat /usr/src/linux/.config)"

$ time (echo $OUTPUT | OUTPUT="${OUTPUT//set/abc}")
real    0m1.766s
user    0m1.681s
sys     0m0.002s

$ time (echo $OUTPUT | sed s/set/abc/g >/dev/null)
real    0m0.094s
user    0m0.078s
sys     0m0.006s

1
वास्तव में, trतेज है। रेग्जेस और ग्लब्स महंगे हैं, और जबकि यहां कोई बाहरी कार्यक्रम नहीं है, बैश हमेशा की तरह कुछ धीमा होगा tr
terdon

यह अत्यधिक इनपुट डेटा पर और रेगेक्सपी कार्यान्वयन पर निर्भर करता है। आपके उत्तर में आपने कुछ विशिष्ट बड़े डेटा सेट लिए - लेकिन डेटा सेट छोटा हो सकता है। या अलग। इसके अलावा, आप regexp का समय नहीं बल्कि प्रतिध्वनि का समय मापते हैं, इसलिए यदि आपकी तुलना वास्तव में उचित है, तो मुझे यकीन नहीं हो सकता है।
gena2x

अच्छे अंक। हालाँकि, आप परीक्षण के बिना गति के बारे में दावे नहीं कर सकते। वास्तव में, जब किसी वैरिएबल को असाइन किया जाता है तो यह तेज लगता है लेकिन स्क्रीन पर प्रिंट करते समय trजीत होती है (मेरा उत्तर देखें)। मैं मानता हूं कि यह कई कारकों पर निर्भर करेगा लेकिन वास्तव में यही कारण है कि आप यह नहीं बता सकते हैं कि वास्तव में परीक्षण किए बिना कौन जीतता है।
terdon

6

यदि, ऑफ-मौके पर, आप केवल शेल के पुन: उपयोग के लिए उद्धरणों को संभालने की कोशिश कर रहे हैं, तो आप उन्हें हटाए बिना भी ऐसा कर सकते हैं , और यह आसान भी है:

aq() { sh -c 'for a do
       alias "$((i=$i+1))=$a"
       done; alias' -- "$@"
}

यह फ़ंक्शन शेल आपके द्वारा हाथ किए गए किसी भी आर्ग सरणी को उद्धृत करता है और इसके आउटपुट को पुनरावृत्त तर्क में बढ़ाता है।

यहाँ यह कुछ args के साथ है:

aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'

आउटपुट

1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'

वह आउटपुट है dashजिसमें से आमतौर पर सुरक्षित-उद्धरण एकल-उद्धृत आउटपुट जैसे होते हैं '"'"'bashकरेगा '\''

एकल, गैर-व्हाट्सएप, गैर-नल बाइट्स के चयन को किसी अन्य एकल बाइट के साथ बदलने से संभवतः किसी भी POSIX शेल में $IFSऔर उसके साथ सबसे तेज किया जा सकता है $*

set -f; IFS=\"\'\`; set -- $var; printf %s "$*"

आउटपुट

"some ""crazy """"""""string ""here

वहाँ मैं सिर्फ printfइतना है कि आप इसे देख सकते हैं, लेकिन निश्चित रूप से, अगर मैंने किया था:

var="$*"

... इसके बजाय printfकमांड $varका मूल्य वही होगा जो आप वहां के आउटपुट में देखते हैं।

जब मैं set -fशेल को ग्लोब नहीं करने का निर्देश देता हूं - यदि स्ट्रिंग में ऐसे अक्षर हैं जो ग्लोब पैटर्न के रूप में रखे जा सकते हैं। मैं ऐसा करते हैं क्योंकि गोले पार्सर फैलता पैटर्न glob के बाद यह चर पर क्षेत्र बंटवारे प्रदर्शन करती है। ग्लोबिंग को फिर से सक्षम किया जा सकता है set +f। सामान्य तौर पर - लिपियों में - मुझे अपने बैंग को सेट करने के लिए उपयोगी लगता है जैसे:

#!/usr/bin/sh -f

और फिर स्पष्ट रूप से सक्षम करने के लिएset +f जो कुछ भी मैं यह चाहता हूँ पर लाइन के साथ ।

फ़ील्ड विभाजन विभाजन वर्णों के आधार पर होता है $IFS

$IFSमूल्य दो प्रकार के होते हैं- $IFSव्हॉट्सएप और $IFSनॉन-व्हाट्सएप। $IFSव्हाट्सएप (स्पेस, टैब, न्यूलाइन) सीमांकित क्षेत्रों को एक ही क्षेत्र के अनुक्रम के साथ निर्दिष्ट करने के लिए निर्दिष्ट किया जाता है (या यदि वे पहले से कुछ और नहीं करते हैं) - तो ...

IFS=\ ; var='      '; printf '<%s>' $var
<>

लेकिन सभी अन्य लोग प्रति घटना के लिए एक ही क्षेत्र का मूल्यांकन करने के लिए निर्दिष्ट होते हैं - वे काटे नहीं जाते हैं।

IFS=/; var='/////'; printf '<%s>' $var
<><><><><>

सभी चर विस्तार डिफ़ॉल्ट रूप से, $IFSडेटा सीमांकित हैं - वे अलग-अलग फ़ील्ड के अनुसार विभाजित होते हैं $IFS। जब आप "-quote करते हैं, तो आप उस एरे प्रॉपर्टी को ओवरराइड करते हैं और इसे सिंगल स्ट्रिंग के रूप में मूल्यांकित करते हैं।

तो जब मैं करता हूँ ...

IFS=\"\'\`; set -- $var

मैं विस्तार से $IFSउत्पन्न कई सीमांकित क्षेत्रों में शेल के तर्क सरणी को सेट कर रहा हूं $var। जब इसे विस्तारित किया जाता है तो इसमें निहित पात्रों के लिए अपने मूल्यों $IFSको खो दिया जाता है - वे अब केवल क्षेत्र विभाजक हैं - वे हैं \0NUL

"$*"- अन्य दोहरे-अवतरण चर-विस्तार की तरह - के क्षेत्र-विभाजन गुणों को भी ओवरराइड करता है $IFS। लेकिन, इसके अलावा , यह $IFS प्रत्येक सीमांकित क्षेत्र के लिए पहली बाइट को प्रतिस्थापित करता है "$@"। तो क्योंकि "था पहले मूल्य में $IFS बाद के सभी सीमांकक बन "में "$*"और "जरूरत नहीं है $IFSजब आप इसे या तो विभाजित करते हैं। आप पूरी तरह से एक और मूल्य के $IFS बाद बदल सकते हैं set -- $argsऔर इसकी नई पहली बाइट तब क्षेत्र में सीमांकक के लिए दिखाई देगी "$*"। क्या अधिक है, आप उनमें से सभी निशान पूरी तरह से हटा सकते हैं:

set -- $var; IFS=; printf %s "$*"

आउटपुट

some crazy string here

बहुत अच्छा, +1। मुझे आश्चर्य है कि अगर यह वास्तव में तेज है। क्या आप मेरे जवाब के दृष्टिकोण से इसकी तुलना करते हुए कुछ समय परीक्षण जोड़ सकते हैं? मैं उम्मीद करता हूं कि आपका काम और तेज होगा, लेकिन देखना चाहेंगे।
terdon

@terdon - जो शेल पर निर्भर करता है। यह किसी भी शेल की तुलना में लगभग निश्चित रूप से तेज है tr, लेकिन मामले के bashलिए अंतर iffy है ${var//$c/$newc/}। मुझे उम्मीद है कि इस मामले में भी यह कुछ मार्जिन से तेज होगा, लेकिन मैं आमतौर पर इस बारे में चिंता नहीं करता हूं क्योंकि इस सामान के लिए मैं हमेशा उपयोग करता हूं dash- जो आम तौर पर हर सम्मान में परिमाण के आदेशों से तेज होता है। और इसलिए तुलना करना कठिन है।
mikeserv

@terdon - मैंने कोशिश की। लेकिन - यहां तक कि में bash- कर रही time (IFS=\"\'`; set -- $var; printf %s "$*")है और time (var=${var//\'`/\"/})दोनों में परिणाम 0.0000sसभी क्षेत्रों के लिए परिणाम। क्या मैं कुछ गलत कर रहा हूं, क्या आपको लगता है? वहाँ बैकलॉग से पहले एक बैकस्लैश माना जाता है, लेकिन मुझे पता नहीं है कि कैसे एक टिप्पणी क्षेत्र के क्षेत्र में एक बैकक्वॉट डाल दिया जाए।
19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.