একটি ভেরিয়েবলের অক্ষর প্রতিস্থাপনের সবচেয়ে সংক্ষিপ্ততম উপায়


18

একটি ভেরিয়েবলের অক্ষর প্রতিস্থাপনের বিভিন্ন উপায় রয়েছে।

আমি খুঁজে পাওয়া সবচেয়ে সংক্ষিপ্ততম উপায় tr:

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

একটি দ্রুত উপায় আছে কি? এবং এই কোটিং-এর মতো ', "এবং নিজেই es


আমি মনে করি আপনি ব্যবহার চালিয়ে যেতে পারেন tr। BASH এর PE ভাল তবে tr এক্ষেত্রে অনেক দ্রুত। উদাহরণস্বরূপ echo "$OUTPUT" | tr -dc '[[:alpha:]]' যেহেতু আপনি কেবলমাত্র
বর্ণমালা

2
যেহেতু আপনি বুদ্ধিমান-বুদ্ধিমান হতে আগ্রহী: সর্বদা আপনার ভেরিয়েবলগুলি উদ্ধৃত করুন! echo "$OUTPUT"। বা ভাল: printf "%s\n" "$OUTPUT"। (কখন কী ঘটে OUTPUT="-n"?)
musifhil

আপনি ব্রাউজিং কোডগল্ফ , বিশেষত বাশ টিপস বিবেচনা করতে পারেন ।
hoosierEE

উত্তর:


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সমাধানগুলির জন্য অ্যাকাউন্ট করতে হবে
ইরুবার

@ ১_সিআর হ্যাঁ তবে যেহেতু তিনি যে কোনও পদ্ধতি ব্যবহার করেন তা আমি বুঝতে পারি এটি অপ্রাসঙ্গিক।
টেরডন

1
বেশ নয়, OUTPUT="${OUTPUT//[`\"\']/}" কমান্ড প্রতিস্থাপন জড়িত নয়
ইরুবার

@ 1_সিআর আহ, আমি দেখছি, হ্যাঁ, আপনি বেশ ঠিক আছেন এবং ফলাফলটি পরিবর্তন করে। ধন্যবাদ, উত্তর সম্পাদিত।
টেরডন

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 - আপনার মন্তব্যের অর্থ এখানে কী তা আমি বুঝতে পারি না?
slm

12

বাশ বা জ্যাশে এটি হ'ল:

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

নোট করুন যে ${VAR//PATTERN/}প্যাটার্নের সমস্ত উদাহরণ সরিয়ে দেয়। আরও তথ্যের জন্য বাশ প্যারামিটার সম্প্রসারণ

এই সমাধানটি ছোট স্ট্রিংয়ের জন্য দ্রুত হওয়া উচিত কারণ এতে কোনও বাহ্যিক প্রোগ্রাম চালানো জড়িত না। তবে খুব দীর্ঘ স্ট্রিংয়ের বিপরীতটি সত্য - পাঠ্য ক্রিয়াকলাপগুলির জন্য উত্সর্গীকৃত সরঞ্জাম ব্যবহার করা ভাল is উদাহরণস্বরূপ:

$ 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
টেরডন

এটি ইনপুট ডেটা এবং রিজেক্সপ বাস্তবায়নের উপর নির্ভর করে। আপনার উত্তরে আপনি কিছু নির্দিষ্ট বড় ডেটা সেট নিয়েছিলেন - তবে ডেটা সেটটি ছোট হতে পারে। বা ভিন্ন। এছাড়াও, আপনি রেজিএক্সপেক্সের সময় নয় তবে প্রতিধ্বনের সময় পরিমাপ করেন, সুতরাং আপনার তুলনাটি সত্যিই সঠিক কিনা তা আমি নিশ্চিত হতে পারি না।
gena2x

ভাল দিক. তবে, আপনি পরীক্ষা না করে গতি সম্পর্কে দাবি করতে পারবেন না। প্রকৃতপক্ষে, কোনও ভেরিয়েবলের দায়িত্ব দেওয়ার সময় এটি দ্রুত মনে হয় তবে যখন স্ক্রিনে মুদ্রণ করা হয় তখন tr(আমার উত্তর দেখুন)। আমি সম্মত হই যে এটি অনেকগুলি বিষয়ের উপর নির্ভর করবে তবে ঠিক এ কারণেই আপনি এটি পরীক্ষা করতে পারবেন না যে কোনটি জিতবে।
টেরডন

6

যদি অফ-সুযোগে আপনি শেলটি পুনরায় ব্যবহারের জন্য কোটগুলি হ্যান্ডেল করার চেষ্টা করছেন তবে আপনি এটি ছাড়া এটি করতে পারেন এগুলি অপসারণ না করে , এবং এটি খুব মৃত:

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

এই ফাংশন শেলটি আপনার দেওয়া যে কোনও আরগ অ্যারেটিকে উদ্ধৃত করে এবং পুনরাবৃত্তিযোগ্য যুক্তি অনুসারে এর আউটপুট বৃদ্ধি করে।

এখানে এটি কয়েকটি আরগ সহ রয়েছে:

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করতে হবে '\''

একক, নন-হোয়াইটস্পেস, নন-নাল বাইটের অন্য একটি একক বাইটের একটি নির্বাচন প্রতিস্থাপন সম্ভবত $IFSএবং এর সাথে কোনও পসিক্স শেলের মধ্যে দ্রুত কাজ করা যেতে পারে $*

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

আউটপুট

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

সেখানে আমি কেবল printfএটি করেছি যাতে আপনি এটি দেখতে পারেন তবে অবশ্যই যদি আমি এটি করতাম:

var="$*"

... বরং printf কমান্ডের$var আপনি সেখানে আউটপুটে যা দেখতে পাচ্ছেন তা হ'ল।

যখন আমি set -fশেলটি গ্লোব না করার নির্দেশ দিই - যদি স্ট্রিংয়ে এমন অক্ষর থাকে যা গ্লোব নিদর্শন হিসাবে চিহ্নিত করা যায়। আমি এটি করছি কারণ শেল পার্সার ভেরিয়েবলের ক্ষেত্রে ক্ষেত্র বিভাজন সম্পাদন করার পরে গ্লোব নিদর্শনগুলি প্রসারিত করে। গ্লোববিংয়ের মতো পুনরায় সক্ষম করা যায় 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। যখন তুমি" একজনকে উদ্ধৃত করেন আপনি সেই অ্যারে সম্পত্তিটিকে ওভাররাইড করে এবং এটি একটি একক স্ট্রিং হিসাবে মূল্যায়ন করেন।

তাই যখন আমি ...

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 - এটি শেলের উপর নির্ভর করে। এটা প্রায় স্পষ্টভাবে তুলনায় দ্রুততর trকোনো শেল, কিন্তু পার্থক্য মধ্যে iffy হয় bashজন্য ${var//$c/$newc/}কেস। আমি আশা করি এমনকি এক্ষেত্রে এটি কিছুটা মার্জিনের চেয়ে দ্রুত হবে তবে আমি সাধারণত এটি নিয়ে উদ্বিগ্ন নই কারণ এই জিনিসগুলির জন্য আমি সর্বদা ব্যবহার করি dash- যা সাধারণত প্রতিটি ক্ষেত্রেই আকারের ক্রম দিয়ে দ্রুত। এবং তাই এটি তুলনা করা কঠিন।
মাইকসার্ভ

@terdon - আমি চেষ্টা করেছি কিন্তু - এমনকি bash- করছেন time (IFS=\"\'`; set -- $var; printf %s "$*")এবং time (var=${var//\'`/\"/})উভয় ফলাফলের 0.0000sসব ক্ষেত্রের জন্য ফলাফল নেই। আমি কি কিছু ভুল করছি, আপনার কি মনে হয়? সেখানে ব্যাককোয়টের আগে একটি ব্যাকস্ল্যাশ হওয়ার কথা আছে তবে আমি কীভাবে একটি মন্তব্য কোড ক্ষেত্রে ব্যাককোট রাখতে পারি তা আমি জানি না।
মাইকসার্ভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.