বাশের একটি অ্যারেতে স্ট্রিং বিভক্ত করুন


640

বাশ স্ক্রিপ্টে আমি একটি লাইনকে টুকরো টুকরো করে ভাগ করতে চাই এবং সেগুলি অ্যারেতে সঞ্চয় করতে চাই।

লাইন:

Paris, France, Europe

আমি তাদের এইরকম একটি অ্যারেতে রাখতে চাই:

array[0] = Paris
array[1] = France
array[2] = Europe

আমি সাধারণ কোডটি ব্যবহার করতে চাই, কমান্ডের গতি কোনও ব্যাপার নয়। আমি এটা কিভাবে করবো?


22
এটি # 1 গুগল হিট তবে উত্তরে বিতর্ক রয়েছে কারণ দুর্ভাগ্যক্রমে প্রশ্নটি , কমা-মহাকাশ হিসাবে ডিলিট করার বিষয়ে জিজ্ঞাসা করে এবং কমা হিসাবে কোনও একক চরিত্র নয় । আপনি শুধুমাত্র আধুনিক করতে আগ্রহী হন তবে, উত্তর এখানে অনুসরণ করা সহজ হয়: stackoverflow.com/questions/918886/...
antak

আপনি যদি একটি স্ট্রিংকে মঞ্জ করতে চান এবং এটি অ্যারে হিসাবে রাখার বিষয়ে চিন্তা না করেন তবে এটিও মনে রাখার cutজন্য একটি দরকারী বাশ কমান্ড। বিভাজক নির্দিষ্ট.ইন.উইকিবুকস.আর / উইকি / কিউট আপনি একটি নির্দিষ্ট প্রস্থ রেকর্ড কাঠামো থেকেও ডেটা বের করতে পারবেন।
en.wikedia.org/wiki/Cut_(Unix

উত্তর:


1088
IFS=', ' read -r -a array <<< "$string"

নোট করুন যে অক্ষরগুলিকে $IFSপৃথকভাবে পৃথক হিসাবে বিবেচনা করা হয় যাতে এই ক্ষেত্রে ক্ষেত্র দুটি অক্ষরের ক্রমের পরিবর্তে কমা বা একটি স্থান দ্বারা পৃথক করা যায় । আকর্ষণীয় হলেও, খালি ক্ষেত্রগুলি যখন ইনপুটটিতে কমা-স্থানটি প্রদর্শিত হয় না কারণ স্থানটি বিশেষভাবে বিবেচনা করা হয়।

একটি পৃথক উপাদান অ্যাক্সেস করতে:

echo "${array[0]}"

উপাদানগুলির উপর পুনরাবৃত্তি করতে:

for element in "${array[@]}"
do
    echo "$element"
done

সূচক এবং মান উভয়ই পেতে:

for index in "${!array[@]}"
do
    echo "$index ${array[index]}"
done

শেষ উদাহরণটি দরকারী কারণ বাশ অ্যারেগুলি বিরল। অন্য কথায়, আপনি কোনও উপাদান মুছতে পারেন বা একটি উপাদান যুক্ত করতে পারেন এবং তারপরে সূচকগুলি স্বচ্ছ নয়।

unset "array[1]"
array[42]=Earth

একটি অ্যারেতে উপাদানের সংখ্যা পেতে:

echo "${#array[@]}"

উপরে উল্লিখিত হিসাবে, অ্যারেগুলি বিচ্ছিন্ন হতে পারে তাই আপনার শেষ উপাদানটি পেতে দৈর্ঘ্যটি ব্যবহার করা উচিত নয়। আপনি এখানে 4.2 বাশ এবং পরবর্তীকালে কীভাবে পারেন:

echo "${array[-1]}"

বাশের যে কোনও সংস্করণে (2.05b এর পরে কোথাও থেকে):

echo "${array[@]: -1:1}"

বড় নেতিবাচক অফসেটগুলি অ্যারের শেষ থেকে আরও দূরে নির্বাচন করে। পুরানো আকারে বিয়োগ চিহ্নের আগে স্থানটি নোট করুন। এটা দরকার.


15
কেবল ব্যবহার করুন IFS=', ', তারপরে আপনাকে আলাদা করে স্থান সরিয়ে ফেলতে হবে না। পরীক্ষা:IFS=', ' read -a array <<< "Paris, France, Europe"; echo "${array[@]}"
l0b0

4
@ l0b0: ধন্যবাদ আমি কী ভাবছিলাম জানি না। আমি declare -p arrayউপায় আউট টেস্ট আউটপুট জন্য ব্যবহার করতে চাই ।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

1
এটি উদ্ধৃতিগুলির সম্মান করে বলে মনে হচ্ছে না। উদাহরণস্বরূপ, France, Europe, "Congo, The Democratic Republic of the"কঙ্গোর পরে এটি বিভক্ত হবে।
ইয়েসরায়েল ডভ

2
@ ইস্রায়েলডভ: বাশের সিএসভি মোকাবেলা করার কোনও উপায় নেই। এটি কোটের ভিতরে থাকা এবং তাদের বাইরে থাকা কমাগুলির মধ্যে পার্থক্য বলতে পারে না। আপনি একটি সরঞ্জাম যা করা CSV বুঝতে পারে এই ধরনের একটি উচ্চ স্তরের ভাষায় liberal এর সংক্ষিপ্ত রূপ হিসেবে, উদাহরণস্বরূপ ব্যবহার করতে হবে CSV পাইথন মধ্যে মডিউল।
পরবর্তী বিজ্ঞপ্তি না দেওয়া পর্যন্ত বিরতি দেওয়া হয়েছে।

4
str="Paris, France, Europe, Los Angeles"; IFS=', ' read -r -a array <<< "$str"array=([0]="Paris" [1]="France" [2]="Europe" [3]="Los" [4]="Angeles")একটি নোট হিসাবে বিভক্ত হবে । সুতরাং এটি কেবল ফাঁকা ফাঁকা ক্ষেত্রের সাথে কাজ করে যেহেতু IFS=', 'স্বতন্ত্র অক্ষরের একটি সেট - স্ট্রিং ডিলিমিটার নয়।
ডগ

332

এই প্রশ্নের উত্তরগুলির সবগুলিই কোনও না কোনওভাবে ভুল।


ভুল উত্তর # 1

IFS=', ' read -r -a array <<< "$string"

1: এটি একটি অপব্যবহার $IFS। মান $IFSপরিবর্তনশীল হয় না একটি হিসাবে গ্রহণ একক পরিবর্তনশীল দৈর্ঘ্যের , স্ট্রিং বিভাজক বরং এটি একটি হিসাবে নেওয়া হয় সেট এর একক-অক্ষর স্ট্রিং বিভাজক, যেখানে প্রতিটি ক্ষেত্রের যে readইনপুট লাইন থেকে বন্ধ splits দ্বারা সমাপ্ত করা যেতে পারে কোনো সেট চরিত্র ( এই উদাহরণে কমা বা স্থান)।

প্রকৃতপক্ষে, প্রকৃত স্টিকারদের জন্য সেখানে, পুরো অর্থটি $IFSকিছুটা বেশি জড়িত। থেকে ব্যাশ ম্যানুয়াল :

শেল একইরূপে এর প্রতিটি অক্ষর IFS একটি বিভেদক, এবং ক্ষেত্র terminators এই অক্ষর ব্যবহার করে শব্দের মধ্যে টুকরা অন্যান্য প্রসারণও এর ফলাফল নেই। যদি আইএফএসটি সেট না করা থাকে, বা এর মানটি হ'ল <space><tab> <নিউলাইন> , ডিফল্ট, তারপরে পূর্ববর্তী বিস্তারের ফলাফলের শুরু এবং শেষে <স্পেস> , <ট্যাব> এবং <নিউলাইন> এর ক্রমগুলি উপেক্ষা করা হয় এবং আইএফএস অক্ষরের কোনও অনুক্রম শুরুর বা শেষের দিকে নয়, শব্দগুলি সীমিত করে দেয়। যদি IFS ডিফল্ট ছাড়া অন্য কোনো মূল্য আছে, তারপর হোয়াইটস্পেস অক্ষরের ক্রমের সাথে <স্পেস> , <ট্যাব> এবং <শব্দের শুরুতে ও শেষে উপেক্ষা করা হয়, যতক্ষণ না হোয়াইটস্পেসের অক্ষর আইএফএসের মান হয় (একটি আইএফএস হোয়াইটস্পেস অক্ষর)। কোন চরিত্র IFS নয় Ifs হোয়াইটস্পেস কোনো সন্নিহিত সহ IFS হোয়াইটস্পেস অক্ষর, একটি ক্ষেত্র delimits। আইএফএস সাদা স্থানের অক্ষরের একটি অনুক্রমকেও একটি ডিলিমিটার হিসাবে বিবেচনা করা হয়। যদি আইএফএসের মান নাল হয় তবে কোনও শব্দ বিভাজন ঘটে না।

মূলত, অ-ডিফল্ট নন-নাল মানগুলির জন্য $IFS, ক্ষেত্রগুলিকে (1) এক বা একাধিক অক্ষরের ক্রম দিয়ে পৃথক করা যায় যা সমস্ত "আইএফএস হোয়াইটস্পেস অক্ষর" এর সেট থেকে থাকে (যা <স্পেস>> এর যে কোনওটি , <tab> , এবং <নিউলাইন> ("নিউলাইন" অর্থ লাইন ফিড (এলএফ) ) যে কোনও জায়গায় উপস্থিত রয়েছে$IFS ), বা (২) $IFS"আইএফএস হোয়াইটস্পেস অক্ষর" এর চারপাশে থাকা যে কোনও অ-"আইএফএস হোয়াইটস্পেস অক্ষর" উপস্থিত রয়েছে ইনপুট লাইনে।

ওপি'র পক্ষে এটি সম্ভবত সম্ভব যে পূর্বের অনুচ্ছেদে আমি বর্ণিত দ্বিতীয় বিচ্ছেদ মোডটি তার ইনপুট স্ট্রিংয়ের জন্য ঠিক যা চায়, তবে আমরা যথেষ্ট আত্মবিশ্বাসের সাথে বলতে পারি যে আমি বর্ণিত প্রথম বিচ্ছেদ মোডটি মোটেই সঠিক নয়। উদাহরণস্বরূপ, যদি তার ইনপুট স্ট্রিংটি ছিল 'Los Angeles, United States, North America'?

IFS=', ' read -ra a <<<'Los Angeles, United States, North America'; declare -p a;
## declare -a a=([0]="Los" [1]="Angeles" [2]="United" [3]="States" [4]="North" [5]="America")

2: এমনকি যদি আপনি এই সমাধানটি একটি একক-অক্ষর বিভাজক (যেমন নিজেই একটি কমা হিসাবে, যে কোনও নিম্নলিখিত স্থান বা অন্যান্য ব্যাগেজ সহ) ব্যবহার করেন না, তবে যদি $stringভেরিয়েবলের মানটি কোনও এলএফ থাকে তবে তা readহবে একবার প্রথম এলএফ-এর মুখোমুখি হওয়ার পরে প্রক্রিয়াজাতকরণ বন্ধ করুন। readBuiltin শুধুমাত্র আবাহন প্রতি এক লাইন প্রক্রিয়া করে। এই সত্য এমনকি যদি আপনি বংশীধ্বনিতুল্য হয় বা ইনপুট পুনঃনির্দেশিত শুধুমাত্র করতে readবিবৃতি, যেমন আমরা এই উদাহরণে করছ এখানে স্ট্রিং প্রক্রিয়া, এবং এইভাবে অপ্রক্রিয়াজাত ইনপুট হারিয়ে যেতে নিশ্চিত করা হয়। কোড যে শক্তিreadবিল্টিনকে কোডটিতে এর সাথে থাকা কমান্ড কাঠামোর মধ্যে ডেটা প্রবাহ সম্পর্কে কোনও জ্ঞান নেই।

আপনি তর্ক করতে পারেন যে এটি কোনও সমস্যার কারণ হতে পারে না তবে তবুও এটি একটি সূক্ষ্ম ঝুঁকি যা সম্ভব হলে এড়ানো উচিত। এটা সত্য যে দ্বারা ঘটিত হয় readতারপর ক্ষেত্রগুলির মধ্যে লাইন প্রথম: builtin আসলে ইনপুট বিভাজন দুই মাত্রা আছে। যেহেতু ওপি কেবল একটি মাত্রার বিভাজন চায়, তাই readবিল্টিনের এই ব্যবহারটি যথাযথ নয় এবং আমাদের এটি এড়ানো উচিত।

3: এই সমাধান সহ একটি অ-স্পষ্ট সম্ভাব্য সমস্যা হ'ল readএটি খালি থাকলে সর্বদা অনুসরণের ক্ষেত্রটি ফেলে দেয়, যদিও এটি অন্যথায় খালি ক্ষেত্রগুলি সংরক্ষণ করে। এখানে একটি ডেমো রয়েছে:

string=', , a, , b, c, , , '; IFS=', ' read -ra a <<<"$string"; declare -p a;
## declare -a a=([0]="" [1]="" [2]="a" [3]="" [4]="b" [5]="c" [6]="" [7]="")

সম্ভবত ওপি এই বিষয়ে চিন্তা করবে না, তবে এটি সম্পর্কে জানার একটি সীমাবদ্ধতা এখনও রয়েছে। এটি সমাধানের দৃ rob়তা এবং সাধারণতা হ্রাস করে।

এই সমস্যাটি ডামি ট্রেইলিং ডিলিমিটারটিকে খাওয়ানোর ঠিক আগে ইনপুট স্ট্রিংয়ে যুক্ত করে সমাধান করা যেতে পারে read, কারণ আমি পরে প্রদর্শন করব।


ভুল উত্তর # 2

string="1:2:3:4:5"
set -f                     # avoid globbing (expansion of *).
array=(${string//:/ })

অনুরূপ ধারণা:

t="one,two,three"
a=($(echo $t | tr ',' "\n"))

(দ্রষ্টব্য: কমান্ড প্রতিস্থাপনের আশেপাশে আমি অনুপস্থিত প্রথম বন্ধনী যুক্ত করেছি যা উত্তরদাতাকে বাদ দেওয়া হয়েছে বলে মনে হয়))

অনুরূপ ধারণা:

string="1,2,3,4"
array=(`echo $string | sed 's/,/\n/g'`)

এই সমাধানগুলি ক্ষেত্রগুলিতে স্ট্রিংকে বিভক্ত করার জন্য একটি অ্যারের অ্যাসাইনমেন্টে শব্দ বিভাজনকে উত্তোলন করে। মজাদারভাবে যথেষ্ট, ঠিক যেমন read, সাধারণ শব্দ বিভাজনও $IFSবিশেষ পরিবর্তনশীল ব্যবহার করে , যদিও এই ক্ষেত্রে এটি সূচিত হয় যে এটি <space><tab> <নিউলাইন> এর ডিফল্ট মানতে সেট করা আছে এবং সুতরাং এক বা একাধিক আইএফএসের যে কোনও অনুক্রম রয়েছে to অক্ষর (যা এখন সমস্ত শ্বেতক্ষেত্রের অক্ষর) ফিল্ড ডিলিমিটার হিসাবে বিবেচিত হয়।

এটি দ্বারা বিভক্ত দুটি স্তরের বিভক্তির সমস্যা সমাধান করে read, যেহেতু শব্দ বিভাজন নিজেই বিভাজনের মাত্র একটি স্তরকে গঠন করে। তবে ঠিক আগের মতোই এখানে সমস্যাটি হ'ল ইনপুট স্ট্রিংয়ের পৃথক ক্ষেত্রগুলিতে ইতিমধ্যে $IFSঅক্ষর থাকতে পারে এবং এভাবে শব্দ বিভাজন ক্রিয়াকলাপের সময় তারা ভুলভাবে বিভক্ত হবে। এই উত্তরদাতারা প্রদত্ত কোনও নমুনা ইনপুট স্ট্রিংয়ের ক্ষেত্রে এটি না হওয়ার ঘটনা ঘটে (কতটা সুবিধাজনক ...) তবে অবশ্যই এই সত্যটি পরিবর্তিত হয় না যে এই আইডিয়ামটি ব্যবহার করে এমন কোনও কোড বেস এর ঝুঁকিটি চালিয়ে দেবে যদি এই ধারনাটি কখনও লাইন থেকে কিছুটা নীচে লঙ্ঘিত হয় তবে উড়িয়ে দেওয়া। আবার, 'Los Angeles, United States, North America'(বা।) এর জবাবদিহি বিবেচনা করুন'Los Angeles:United States:North America' ) ।

এছাড়াও, শব্দ বিভাজন স্বাভাবিকভাবে দ্বারা অনুসরণ করা হয় ফাইলের নাম সম্প্রসারণ ( ওরফে পথনাম সম্প্রসারণ ওরফে , যা, যদি সম্পন্ন হলে, অক্ষর ধারণকারী সম্ভাব্য দুর্নীতিগ্রস্ত শব্দ হবে globbing) *, ?অথবা [দ্বারা অনুসরণ ](এবং, যদি extglobসেট করা থাকে, আলাদা টুকরা পূর্বে ?, *, +, @, বা !) ফাইল সিস্টেমের সামগ্রীর সাথে তাদের মিলিয়ে এবং সেই অনুযায়ী শব্দগুলি ("গ্লোবস") প্রসারিত করে। এই তিন জন উত্তরদাতার মধ্যে প্রথমটি চূড়ান্তভাবে set -fগ্লোব্বিং নিষ্ক্রিয় করতে আগে থেকে চালিয়ে এই সমস্যাটি স্বীকার করেছে। প্রযুক্তিগতভাবে এটি কাজ করে (যদিও আপনার সম্ভবত যোগ করা উচিতset +f পরবর্তীকালে পরবর্তী কোডগুলির জন্য এটি পুনরায় সক্ষম করা সম্ভব হবে যা এটির উপর নির্ভর করে) তবে স্থানীয় কোডে একটি মৌলিক স্ট্রিং-টু-অ্যারে পার্সিং অপারেশন হ্যাক করতে গ্লোবাল শেল সেটিংসের সাথে জগাখিচুড়ি করা অনাকাঙ্ক্ষিত।

এই উত্তর সহ অন্য একটি সমস্যা হ'ল সমস্ত খালি ক্ষেত্র নষ্ট হবে। এটি প্রয়োগের উপর নির্ভর করে সমস্যা হতে পারে বা নাও হতে পারে।

দ্রষ্টব্য: আপনি যদি এই সমাধানটি ব্যবহার করতে চলেছেন তবে একটি পাইপলাইন শুরু করে, কমান্ড প্রতিস্থাপন (যা শেলটি কাঁটাচ্ছে) আহ্বান করার সমস্যায় না গিয়ে প্যারামিটার বিস্তারের${string//:/ } "প্যাটার্ন প্রতিস্থাপন" ফর্মটি ব্যবহার করা ভাল and বাহ্যিক এক্সিকিউটেবল ( বা ) চালানো, যেহেতু প্যারামিটারের বিস্তৃতি নিখুঁতভাবে একটি শেল-অভ্যন্তরীণ অপারেশন। (এছাড়াও, এবং সমাধানগুলির জন্য, ইনপুট ভেরিয়েবলটি কমান্ড প্রতিস্থাপনের ভিতরে ডাবল-কোট করা উচিত; অন্যথায় শব্দ বিভাজন কমান্ডে কার্যকর হবে এবং ক্ষেত্রের মানগুলির সাথে সম্ভবত গণ্ডগোল হবে Also এছাড়াও, কমান্ড প্রতিস্থাপনের রূপটি পুরানোের চেয়ে পছন্দনীয়trsedtrsedecho$(...)`...` এটি কমান্ড বিকল্পের নেস্টিং সহজতর করে এবং পাঠ্য সম্পাদকদের দ্বারা আরও ভাল সিনট্যাক্স হাইলাইট করার অনুমতি দেয় form


ভুল উত্তর # 3

str="a, b, c, d"  # assuming there is a space after ',' as in Q
arr=(${str//,/})  # delete all occurrences of ','

এই উত্তরটি প্রায় # 2 এর মতো । পার্থক্যটি হ'ল উত্তরদাতা এই ধারণাটি তৈরি করেছেন যে ক্ষেত্রগুলি দুটি অক্ষর দ্বারা সীমিত করা হয়েছে, যার মধ্যে একটি ডিফল্টরূপে প্রতিনিধিত্ব করা হচ্ছে $IFSএবং অন্যটি নয়। তিনি এই আইটিএস-প্রতিনিধিত্বমূলক অক্ষরটিকে প্যাটার্ন প্রতিস্থাপনের প্রসারণ ব্যবহার করে এবং তারপরে বেঁচে থাকা আইএফএস-প্রতিনিধিত্বকারী ডিলিমিটার চরিত্রের ক্ষেত্রগুলিকে বিভক্ত করার জন্য শব্দ বিভাজন ব্যবহার করে বরং এই নির্দিষ্ট ক্ষেত্রে সমাধান করেছেন।

এটি খুব জেনেরিক সমাধান নয়। তদ্ব্যতীত, এটি যুক্তিযুক্ত হতে পারে যে কমাটি এখানে প্রকৃতপক্ষে "প্রাথমিক" ডিলিমিটার চরিত্র এবং এটি কেড়ে ফেলা এবং তারপরে ক্ষেত্র বিভাজনের জন্য স্থানের অক্ষরের উপর নির্ভর করে কেবল ভুল। আবার, আমার counterexample বিবেচনা করুন: 'Los Angeles, United States, North America'

এছাড়াও, আবার ফাইল নাম প্রসারিত প্রসারিত শব্দগুলিকে দূষিত করতে পারে তবে অ্যাসাইনমেন্টের জন্য অস্থায়ীভাবে গ্লোব্বিং অক্ষম করে set -fএবং এরপরে এটি প্রতিরোধ করা যেতে পারে set +f

এছাড়াও, আবার, সমস্ত খালি ক্ষেত্রগুলি হারিয়ে যাবে, যা অ্যাপ্লিকেশনটির উপর নির্ভর করে সমস্যা হতে পারে বা নাও পারে।


ভুল উত্তর # 4

string='first line
second line
third line'

oldIFS="$IFS"
IFS='
'
IFS=${IFS:0:1} # this is useful to format your code with tabs
lines=( $string )
IFS="$oldIFS"

এটি # 2 এবং এর মতো # 3 এর যে এটি কাজটি করতে শব্দ বিভাজন ব্যবহার করে, কেবল এখন কোডটি স্পষ্টভাবে $IFSইনপুট স্ট্রিংয়ে উপস্থিত কেবল একক-অক্ষর ক্ষেত্রের ডিলিমেটার ধারণ করে। এটি পুনরাবৃত্তি করা উচিত যে এটি ওপি'র কমা-স্পেস ডিলিমিটারের মতো মাল্টিচার্যাক্টর ফিল্ড ডিলিমিটারগুলির পক্ষে কাজ করতে পারে না। তবে এই উদাহরণে ব্যবহৃত এলএফ এর মতো একক-চরিত্রের ডিলিমিটারের জন্য এটি আসলে নিখুঁত হওয়ার কাছাকাছি আসে। পূর্বের ভুল উত্তরের সাহায্যে ক্ষেত্রগুলি অজান্তেই বিভক্ত করা যায় না এবং প্রয়োজন অনুসারে বিভক্তির মাত্র এক স্তর রয়েছে।

একটি সমস্যা হ'ল ফাইলের নাম সম্প্রসারণ পূর্বের বর্ণিত হিসাবে ক্ষতিগ্রস্থ শব্দগুলিকে দূষিত করবে, যদিও এর পরে আবারও সমালোচনামূলক বিবৃতিটি গুটিয়ে রেখে সমাধান করা যেতে পারে set -f এবং set +f

আর একটি সম্ভাব্য সমস্যা হ'ল, এলএফ যেহেতু পূর্বনির্ধারিত হিসাবে "আইএফএস হোয়াইটস্পেস চরিত্র" হিসাবে যোগ্যতা অর্জন করেছে, সমস্ত খালি ক্ষেত্রগুলি হারিয়ে যাবে, ঠিক যেমন # 2 এবং # 3 । এটি অবশ্যই কোনও সমস্যা হবে না যদি ডিলিমিটারটি একটি "আইএফএস হোয়াইটস্পেসের অক্ষরবিহীন অক্ষর" হয়ে থাকে এবং প্রয়োগের উপর নির্ভর করে এটি কোনওভাবেই গুরুত্বপূর্ণ না, তবে এটি সমাধানের সাধারণতাটিকে বিকৃত করে তোলে।

সুতরাং, সংক্ষেপে, আপনার এক-চরিত্রের ডিলিমিটার রয়েছে বলে ধরে নেওয়া এবং এটি হয় একটি "অ-আইএফএস হোয়াইটস্পেস অক্ষর" বা আপনি খালি ক্ষেত্রের যত্ন নেই, এবং আপনি সমালোচনামূলক বিবৃতিটি মুড়ে দিন set -fএবংset +f তারপরে এই সমাধানটি কার্যকর হয় , কিন্তু অন্যথায় না।

(এছাড়াও তথ্যের স্বার্থে, ব্যাশে ভেরিয়েবলের জন্য একটি এলএফ বরাদ্দকরণ $'...'বাক্য বাক্সের সাহায্যে আরও সহজে করা যায় , যেমন eg IFS=$'\n';)


ভুল উত্তর # 5

countries='Paris, France, Europe'
OIFS="$IFS"
IFS=', ' array=($countries)
IFS="$OIFS"

অনুরূপ ধারণা:

IFS=', ' eval 'array=($string)'

এই সমাধানটি কার্যকরভাবে # 1 এর মধ্যে ক্রস (যাতে এটি $IFSকমা-স্পেসে সেট হয়) এবং # 2-4 (এর মধ্যে এটি স্ট্রিংকে ক্ষেত্রগুলিতে বিভক্ত করতে শব্দ বিভাজন ব্যবহার করে)। এর কারণে, এটি উপরের সমস্ত ভুল উত্তরকে যে বেশিরভাগ সমস্যায় ভোগায় তা বেশিরভাগ সমস্যায় ভুগছে, বাছাই করা সমস্ত বিশ্বের সবচেয়ে খারাপের মতো like

এছাড়াও, দ্বিতীয় বৈকল্পিক সম্পর্কিত, evalকলটি সম্পূর্ণ অপ্রয়োজনীয় বলে মনে হতে পারে , যেহেতু এর যুক্তিটি একটি একক-উদ্ধৃত স্ট্রিং আক্ষরিক, এবং তাই এটি স্থিতিশীলভাবে পরিচিত। তবে আসলে evalএইভাবে ব্যবহার করার জন্য একটি খুব অ-সুস্পষ্ট সুবিধা রয়েছে । সাধারণত, আপনি যখন একটি সাধারণ কমান্ড চালান যা কেবলমাত্র একটি চলক অ্যাসাইনমেন্ট ধারণ করে , যার অর্থ আসল কমান্ড শব্দটি অনুসরণ না করে, শেল পরিবেশে অ্যাসাইনমেন্টটি কার্যকর হয়:

IFS=', '; ## changes $IFS in the shell environment

সরল কমান্ডে একাধিক ভেরিয়েবল অ্যাসাইনমেন্ট যুক্ত থাকলেও এটি সত্য ; আবার, যতক্ষণ না কোনও কমান্ড শব্দ নেই, সমস্ত পরিবর্তনশীল অ্যাসাইনমেন্ট শেল পরিবেশকে প্রভাবিত করে:

IFS=', ' array=($countries); ## changes both $IFS and $array in the shell environment

তবে, যদি ভেরিয়েবল অ্যাসাইনমেন্টটি কমান্ড নামের সাথে সংযুক্ত থাকে (আমি এটিকে "উপসর্গ অ্যাসাইনমেন্ট" বলতে চাই) তবে এটি শেল পরিবেশের উপর প্রভাব ফেলবে না এবং পরিবর্তে কেবল নির্বাহী কমান্ডের পরিবেশকে প্রভাবিত করবে, নির্বিশেষে এটি বিল্টিনই হোক না কেন বা বাহ্যিক:

IFS=', ' :; ## : is a builtin command, the $IFS assignment does not outlive it
IFS=', ' env; ## env is an external command, the $IFS assignment does not outlive it

বাশ ম্যানুয়াল থেকে প্রাসঙ্গিক উক্তি :

যদি কোনও কমান্ড নাম ফলাফল না করে, ভেরিয়েবল অ্যাসাইনমেন্টগুলি বর্তমান শেল পরিবেশকে প্রভাবিত করে। অন্যথায়, ভেরিয়েবলগুলি এক্সিকিউটড কমান্ডের পরিবেশে যুক্ত হয় এবং বর্তমান শেল পরিবেশকে প্রভাবিত করে না।

$IFSকেবলমাত্র অস্থায়ীভাবে পরিবর্তনের জন্য ভেরিয়েবল অ্যাসাইনমেন্টের এই বৈশিষ্ট্যটি কাজে লাগানো সম্ভব যা $OIFSপ্রথম ভেরিয়েবলের সাথে ভেরিয়েবলের সাহায্যে সম্পূর্ণ সেভ-অ্যান্ড-রিস্টোর গাম্বিটকে এড়াতে দেয় । তবে আমরা এখানে যে চ্যালেঞ্জের মুখোমুখি হই তা হ'ল আমাদের যে কমান্ডটি চালাতে হবে তা হ'ল একটি নিছক পরিবর্তনশীল অ্যাসাইনমেন্ট এবং তাই এই $IFSঅ্যাসাইনমেন্টটি অস্থায়ী করার জন্য কোনও আদেশ আদেশ জড়িত না । আপনি নিজেই ভাবতে পারেন, ভাল কেন অস্থায়ী : builtinকরার জন্য যেমন বিবৃতিতে কোনও অপ-কমান্ড শব্দ যুক্ত করবেন না $IFS? এটি কাজ করে না কারণ এরপরে $arrayঅ্যাসাইনমেন্টটিও অস্থায়ী করে তুলবে :

IFS=', ' array=($countries) :; ## fails; new $array value never escapes the : command

সুতরাং, আমরা কার্যকরভাবে একটি অচলাবস্থায়, কিছুটা ধরা -২২। তবে, যখন evalতার কোডটি চালায় এটি শেল পরিবেশে এটি চালায়, যেন এটি সাধারণ, স্থির উত্স কোড এবং তাই শেল পরিবেশে কার্যকর হওয়ার জন্য আমরা আর্গুমেন্টের $arrayভিতরে অ্যাসাইনমেন্টটি চালাতে পারি eval, যখন $IFSউপসর্গের অ্যাসাইনমেন্টটি evalকমান্ডের পূর্বনির্ধারিত কমান্ডটি বহিরাগত হবে না eval। এই সমাধানটির দ্বিতীয় ধরণেরটিতে হ'ল কৌশলটি হ'ল:

IFS=', ' eval 'array=($string)'; ## $IFS does not outlive the eval command, but $array does

সুতরাং, আপনি দেখতে পাচ্ছেন যে এটি আসলে একটি চতুর কৌশল এবং এটি অপ্রত্যাশিত উপায়ে সঠিকভাবে প্রয়োজনীয় যা প্রয়োজন (কমপক্ষে অন্তর্ভুক্তির কার্যপ্রণালীকরণের ক্ষেত্রে) আমি জড়িত থাকার পরেও আসলে এই কৌশলটির বিরুদ্ধে নই eval; সুরক্ষা হুমকির বিরুদ্ধে রক্ষা করার জন্য কেবল যুক্তি স্ট্রিংয়ের একক-উদ্ধৃতি দিতে সাবধান হন।

তবে আবার, "সমস্ত পৃথিবীর মধ্যে সবচেয়ে খারাপ" সমস্যার উত্থানের কারণে এটি এখনও ওপি-র প্রয়োজনীয়তার একটি ভুল উত্তর answer


ভুল উত্তর # 6

IFS=', '; array=(Paris, France, Europe)

IFS=' ';declare -a array=(Paris France Europe)

উম ... কি? ওপিতে একটি স্ট্রিং ভেরিয়েবল রয়েছে যা অ্যারেতে পার্স করা দরকার। এই "উত্তর" আরে আক্ষরিক মধ্যে আটকানো ইনপুট স্ট্রিংয়ের ভারব্যাটিম বিষয়বস্তু দিয়ে শুরু হয়। আমার ধারণা এটি করার এক উপায় one

দেখে মনে হচ্ছে যা উত্তরদাতা ধরে নিয়েছে যে $IFSভেরিয়েবলটি সমস্ত প্রসঙ্গে সমস্ত ব্যাশ পার্সিংকে প্রভাবিত করে, যা সত্য নয়। বাশ ম্যানুয়াল থেকে:

আইএফএস     অভ্যন্তরীণ ক্ষেত্র বিভাজক যা প্রসারণের পরে শব্দ বিভাজনের জন্য এবং পঠিত বিল্টিন কমান্ডের সাহায্যে শব্দের মধ্যে লাইনগুলি বিভক্ত করতে ব্যবহৃত হয় । ডিফল্ট মান হ'ল <স্পেস> << ট্যাব> <নিউলাইন>

সুতরাং $IFSবিশেষ পরিবর্তনশীলটি কেবলমাত্র দুটি প্রসঙ্গে ব্যবহৃত হয়: (1) শব্দ বিভাজন যা প্রসারণের পরে সঞ্চালিত হয় (যার অর্থ ব্যাশ উত্স কোডটি পার্সিং করার সময় নয় ) এবং (2) readবিল্টিন দ্বারা শব্দগুলিতে ইনপুট লাইনগুলি বিভক্ত করার জন্য ।

আমাকে এই পরিষ্কার করার চেষ্টা করুন। আমি মনে করি পার্সিং এবং কার্যকর করার মধ্যে পার্থক্য আঁকতে ভাল হতে পারে । ব্যাশ প্রথম আবশ্যক পার্স সোর্স কোড, যা সম্ভবত হয় পার্সিং ঘটনা, এবং তারপর পরে executes কোড, যা যখন সম্প্রসারণ ছবি আসে। সম্প্রসারণ সত্যিই একটি বাস্তবায়ন ইভেন্ট। তদুপরি, আমি $IFSভেরিয়েবলের বিবরণ দিয়ে বিষয়টি নিয়েছি যা আমি উপরে উপরে উদ্ধৃত করেছি; শব্দের বিভাজনটি প্রসারণের পরে সঞ্চালিত হওয়ার পরিবর্তে আমি বলব যে শব্দ বিভাজনটি প্রসারণের সময় সঞ্চালিত হয় বা সম্ভবত আরও স্পষ্টভাবে বলতে গেলে শব্দ বিভাজন হয় অংশসম্প্রসারণ প্রক্রিয়া। "শব্দ বিভাজন" শব্দগুচ্ছটি কেবল প্রসারিত এই পদক্ষেপকে বোঝায়; এটি কখনই ব্যাশ উত্স কোডটি বিশ্লেষণের জন্য ব্যবহার করা উচিত নয়, যদিও দুর্ভাগ্যক্রমে ডকগুলি "বিভাজন" এবং "শব্দ" শব্দের প্রচুর পরিমাণে ফেলেছে বলে মনে হয়। বাশ ম্যানুয়ালটির linux.die.net সংস্করণ থেকে একটি প্রাসঙ্গিক অংশ এখানে দেওয়া হয়েছে :

কমান্ড লাইনে এটি শব্দগুলিতে বিভক্ত হওয়ার পরে সম্প্রসারণ করা হয়। এখানে সাত ধরণের সম্প্রসারণ করা হয়: ব্রেস এক্সপেনশন , টিলডে এক্সপেনশন , প্যারামিটার এবং ভেরিয়েবল এক্সপেনশন , কমান্ড সাবস্টিটিউশন , পাটিগণিত সম্প্রসারণ , শব্দ বিভাজন এবং পথের নাম সম্প্রসারণ

বিস্তারের ক্রম: ব্রেস সম্প্রসারণ; টিলডে সম্প্রসারণ, প্যারামিটার এবং পরিবর্তনশীল সম্প্রসারণ, পাটিগণিতের সম্প্রসারণ এবং কমান্ড প্রতিস্থাপন (বাম থেকে ডান ফ্যাশনে সম্পন্ন); শব্দ বিভাজন; এবং পথের নাম সম্প্রসারণ।

আপনি ম্যানুয়ালটির জিএনইউ সংস্করণটি কিছুটা ভাল করতে পারেন, কারণ এটি সম্প্রসারণ বিভাগের প্রথম বাক্যে "শব্দের" পরিবর্তে "টোকেন" শব্দের জন্য বেছে নেওয়া হয়েছে:

কমান্ড লাইনে টোকেনে বিভক্ত হওয়ার পরে সম্প্রসারণ করা হয়।

গুরুত্বপূর্ণ বিষয়টি হ'ল $IFSবাশ সোর্স কোডকে পার্স করার উপায় পরিবর্তন করে না। বাশ উত্স কোড পার্সিং আসলে একটি খুব জটিল প্রক্রিয়া যার মধ্যে শেল ব্যাকরণের বিভিন্ন উপাদান যেমন কমান্ড সিকোয়েন্সস, কমান্ড তালিকাগুলি, পাইপলাইনগুলি, প্যারামিটার সম্প্রসারণ, পাটিগণিতের বিকল্প এবং কমান্ড বিকল্পের স্বীকৃতি জড়িত। বেশিরভাগ ক্ষেত্রে, ব্যাশ পার্সিং প্রক্রিয়াটি ভেরিয়েবল অ্যাসাইনমেন্টের মতো ব্যবহারকারী-স্তরের ক্রিয়া দ্বারা পরিবর্তন করা যায় না (আসলে, এই নিয়মের কিছু ছোট ব্যতিক্রম রয়েছে; উদাহরণস্বরূপ, বিভিন্নটি দেখুনcompatxx শেল সেটিংস দেখুন, যা ফ্লাইটে পার্সিং আচরণের নির্দিষ্ট কিছু দিক পরিবর্তন করতে পারে)। এই জটিল বিশ্লেষণ প্রক্রিয়া থেকে প্রাপ্ত প্রবাহিত "শব্দ" / "টোকেন" এরপরে উপরের ডকুমেন্টেশন অংশগুলিতে ভাঙা হিসাবে "সম্প্রসারণ" এর সাধারণ প্রক্রিয়া অনুসারে প্রসারিত হয়, যেখানে প্রসারিত (প্রসারিত?) পাঠ্যের শব্দ বিভাজনকে নিম্ন প্রবাহে বিভক্ত করা হয় শব্দগুলি কেবল সেই প্রক্রিয়াটির একটি পদক্ষেপ। শব্দ বিভাজন কেবল এমন পাঠ্যকে স্পর্শ করে যা পূর্ববর্তী বিস্তৃত পদক্ষেপের বাইরে থুথু ফেলেছে; এটি আক্ষরিক পাঠ্যকে প্রভাবিত করে না যা উত্স বাইস্ট্রিমে সরাসরি পার্স করা হয়েছিল।


ভুল উত্তর # 7

string='first line
        second line
        third line'

while read -r line; do lines+=("$line"); done <<<"$string"

এটি সেরা সমাধানগুলির মধ্যে একটি। লক্ষ্য করুন যে আমরা ব্যবহার করতে ফিরে এসেছি read। আমি কি আগে বলিনি যে readএটি অনুপযুক্ত কারণ এটি দুটি স্তরের বিভাজন করে, যখন আমাদের কেবল একটি প্রয়োজন? এখানে কৌশলটি আপনি readএইভাবে কল করতে পারেন যে এটি কার্যকরভাবে কেবলমাত্র এক স্তরের বিভাজন করে, বিশেষত অনুরোধ অনুসারে কেবলমাত্র একটি ক্ষেত্র বিভক্ত করে, যা বার বার লুপে ফোন করার জন্য প্রয়োজনীয় খরচ প্রয়োজন। এটি কিছুটা হাতের মুঠোয়, তবে এটি কার্যকর।

তবে সমস্যা আছে। প্রথম: আপনি যখন কমপক্ষে একটি NAME আর্গুমেন্ট সরবরাহ করেন read, তখন এটি স্বয়ংক্রিয়ভাবে প্রতিটি ক্ষেত্রের শীর্ষস্থানীয় এবং পিছনের শ্বেত স্পেসটিকে উপেক্ষা করে যা ইনপুট স্ট্রিং থেকে বিচ্ছিন্ন। $IFSএই পোস্টে আগে বর্ণিত হিসাবে এটির ডিফল্ট মানতে সেট করা আছে কি না তা ঘটে । এখন, ওপি তার নির্দিষ্ট ব্যবহারের ক্ষেত্রে এটি সম্পর্কে চিন্তা করবে না এবং আসলে এটি পার্সিং আচরণের একটি পছন্দসই বৈশিষ্ট্য হতে পারে। তবে যে ক্ষেত্রগুলিতে কোনও স্ট্রিংকে বিশ্লেষণ করতে চায় তারা সকলেই এটি চাইবে না। তবে এর একটি সমাধান রয়েছে: readশূন্য NAME আর্গুমেন্টগুলি পাস করার জন্য কিছুটা অপ্রকাশিত ব্যবহার । এই ক্ষেত্রে, readপুরো ইনপুট লাইনটি এটি ইনপুট স্ট্রিম থেকে পাওয়া ভেরিয়েবেলে সংরক্ষণ করবে $REPLYএবং একটি বোনাস হিসাবে, এটি নামান থেকে স্ট্রিপ শীর্ষস্থানীয় এবং পিছনে সাদা স্থান। এটি একটি অত্যন্ত শক্তিশালী ব্যবহার readযা আমি আমার শেল প্রোগ্রামিং ক্যারিয়ারে ঘন ঘন শোষণ করেছি। আচরণের পার্থক্যের একটি প্রদর্শন এখানে দেওয়া হয়েছে:

string=$'  a  b  \n  c  d  \n  e  f  '; ## input string

a=(); while read -r line; do a+=("$line"); done <<<"$string"; declare -p a;
## declare -a a=([0]="a  b" [1]="c  d" [2]="e  f") ## read trimmed surrounding whitespace

a=(); while read -r; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="  a  b  " [1]="  c  d  " [2]="  e  f  ") ## no trimming

এই সমাধানটির সাথে দ্বিতীয় ইস্যুটি হ'ল এটি কোনও কাস্টম ফিল্ড বিভাজকের ক্ষেত্রে যেমন ওপি'র কমা-স্পেসের ক্ষেত্রে সমাধান করে না। আগের মতো, মাল্টিচার্যাক্টর বিভাজকগুলি সমর্থিত নয়, যা এই সমাধানের দুর্ভাগ্যজনক সীমাবদ্ধতা। -dবিকল্পটিতে পৃথককারী নির্দিষ্ট করে আমরা কমপক্ষে কমপক্ষে বিভক্ত হওয়ার চেষ্টা করতে পারি , তবে কী ঘটে তা দেখুন:

string='Paris, France, Europe';
a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France")

অনুমানযোগ্যভাবে, অ্যাকাউন্টহীন আশেপাশের শ্বেতস্থানটি ক্ষেত্রের মানগুলিতে টান পড়েছে, এবং তাই এটি পরে ছাঁটাইয়ের ক্রিয়াকলাপগুলির মাধ্যমে সংশোধন করতে হবে (এটি সরাসরি লুপ-লুপেও করা যেতে পারে)। তবে আরও একটি স্পষ্ট ত্রুটি আছে: ইউরোপ অনুপস্থিত! এটা কি হয়েছে? উত্তরটি হ'ল readযদি একটি চূড়ান্ত ক্ষেত্রের চূড়ান্ত ক্ষেত্রের টার্মিনেটরের মুখোমুখি না হয়ে ফাইলের শেষের দিকে (এই ক্ষেত্রে আমরা এটিকে স্ট্রিং-এর স্ট্রিং বলতে পারি) আঘাত করে তবে একটি ব্যর্থ রিটার্ন কোড দেয় returns এর ফলে অল্প সময়ের মধ্যে লুপটি ভেঙে যায় এবং আমরা চূড়ান্ত ক্ষেত্রটি হারাতে পারি।

প্রযুক্তিগতভাবে এই একই ত্রুটি পূর্ববর্তী উদাহরণগুলিকেও ক্ষতিগ্রস্থ করেছিল; পার্থক্যটি হ'ল ক্ষেত্র বিভাজককে এলএফ হিসাবে নেওয়া হয়েছিল, আপনি -dঅপশনটি নির্দিষ্ট না করার সময় এটি ডিফল্ট এবং <<<("এখানে-স্ট্রিং") প্রক্রিয়াটি নিজের মতো করে ফিড দেওয়ার ঠিক আগে স্ট্রিংয়ে একটি এলএফ সংযুক্ত করে app কমান্ড ইনপুট। অতএব, এই ক্ষেত্রে, আমরা বাছাই করে দুর্ঘটনাবশত ইনপুটটিতে অতিরিক্ত ডামি টার্মিনেটর যুক্ত করে অজ্ঞাতসারে বাদ পড়ে যাওয়া চূড়ান্ত ক্ষেত্রের সমস্যাটি সমাধান করেছি। আসুন আমরা এই সমাধানটিকে "ডামি-টার্মিনেটর" সমাধান বলি। আমরা এখানে স্ট্রিং-এ ইনস্ট্যান্ট করার সময় ডামি-টার্মিনেটর সমাধানটিকে যেকোন কাস্টম ডেলিমিটারের জন্য ম্যানুয়ালি প্রয়োগ করতে পারি against

a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string,"; declare -p a;
declare -a a=([0]="Paris" [1]=" France" [2]=" Europe")

সেখানে, সমস্যার সমাধান হয়েছে। অন্য সমাধানটি হ'ল লুপটি কেবলমাত্র ভাঙা যদি উভয় (1) readব্যর্থতা ফিরে আসে এবং (2) $REPLYখালি থাকে, মানে readফাইলের শেষের দিকে আঘাত করার আগে কোনও অক্ষর পড়তে সক্ষম হয় নি। ডেমো:

a=(); while read -rd,|| [[ -n "$REPLY" ]]; do a+=("$REPLY"); done <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=$' Europe\n')

এই পদ্ধতির মাধ্যমে গোপনীয় এলএফও প্রকাশিত হয় যা <<<পুনঃনির্দেশ অপারেটর দ্বারা স্বয়ংক্রিয়ভাবে এখানে স্ট্রিংয়ে সংযুক্ত হয়ে যায় । কিছুক্ষণ আগে বর্ণিত স্পষ্ট ট্রিমিং অপারেশনের মাধ্যমে অবশ্যই এটি আলাদাভাবে ছিনিয়ে নেওয়া যেতে পারে তবে স্পষ্টতই ম্যানুয়াল ডামি-টার্মিনেটর পদ্ধতির এটি সরাসরি সমাধান করে, তাই আমরা কেবল এটির সাথে যেতে পারি। ম্যানুয়াল ডামি-টার্মিনেটর সমাধানটি আসলে বেশ সুবিধাজনক কারণ এটি একসাথে এই দুটি সমস্যা (বাদ পড়ে-চূড়ান্ত ক্ষেত্রের সমস্যা এবং সংযুক্ত-এলএফ সমস্যা) উভয়ই সমাধান করে।

সুতরাং, সামগ্রিকভাবে, এটি বেশ শক্তিশালী সমাধান। এটি কেবলমাত্র দূর্বলতা হ'ল মাল্টিচার্যাক্টার ডিলিমিটারদের জন্য সমর্থনের অভাব, যা আমি পরে উল্লেখ করব।


ভুল উত্তর # 8

string='first line
        second line
        third line'

readarray -t lines <<<"$string"

(এটি আসলে # 7 এর একই পোস্ট থেকে ; উত্তর পোস্টকারী একই পোস্টে দুটি সমাধান সরবরাহ করেছে))

readarrayBuiltin, যার জন্য একটি প্রতিশব্দ হয় mapfile, আদর্শ। এটি একটি বিল্টিন কমান্ড যা একটি শটে একটি অ্যারে ভেরিয়েবলের মধ্যে একটি বাইস্ট্রিমে পার্স করে; লুপস, শর্তসাপেক্ষ, বিকল্পগুলি বা অন্য কোনও কিছুর সাথে বিশৃঙ্খলা নেই। এবং এটি ইনপুট স্ট্রিং থেকে গোপনীয়ভাবে কোনও হোয়াইটস্পেস ছিনিয়ে নেবে না। এবং (যদি -Oদেওয়া না হয়) এটি নির্ধারিত হওয়ার আগে লক্ষ্য বিন্যাসটি সুবিধামত সাফ করে। তবে এটি এখনও নিখুঁত নয়, অতএব এটি আমার একটি "ভুল উত্তর" হিসাবে সমালোচনা।

প্রথমে, এটিকে বাইরে বের করার জন্য, খেয়াল করুন যে, readফিল্ড-পার্সিং করার সময় যেমন আচরণ করা হয়েছে readarrayঠিক তেমন পেছনের ক্ষেত্রটি খালি থাকলে ফেলে দেয়। আবার এটি সম্ভবত ওপিটির জন্য উদ্বেগ নয়, তবে এটি কিছু ব্যবহারের ক্ষেত্রেও হতে পারে। আমি এই মুহুর্তে ফিরে আসতে হবে।

দ্বিতীয়ত, আগের মতো, এটি মাল্টিচার্যাক্টর ডিলিমিটারগুলিকে সমর্থন করে না। আমি এর জন্য একটি মুহূর্তের মধ্যেও একটি সংশোধন দেব।

তৃতীয়ত, লিখিত হিসাবে সমাধানটি ওপি-র ইনপুট স্ট্রিংকে বিশ্লেষণ করে না এবং বাস্তবে এটি পার্স করার জন্য এটি ব্যবহার করা যায় না। আমি এই মুহুর্তে পাশাপাশি প্রসারিত করব।

উপরের কারণগুলির জন্য, আমি এখনও এটিকে ওপি-র প্রশ্নের "ভুল উত্তর" হিসাবে বিবেচনা করি। নীচে আমি যা সঠিক উত্তর বলে মনে করি তা দেব give


সঠিক উত্তর

এখানে কেবলমাত্র বিকল্পটি উল্লেখ করে # 8 টি কাজ করার একটি নির্বুদ্ধ প্রচেষ্টা -d:

string='Paris, France, Europe';
readarray -td, a <<<"$string"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=$' Europe\n')

আমরা দেখতে পাচ্ছি যে ফলাফলটি # 7-এread আলোচিত লুপিং সমাধানের দ্বৈত-শর্তসাপেক্ষ দৃষ্টিভঙ্গি থেকে প্রাপ্ত ফলাফলের সাথে সমান । আমরা ম্যানুয়াল ডামি-টার্মিনেটর ট্রিক দিয়ে প্রায় এটি সমাধান করতে পারি :

readarray -td, a <<<"$string,"; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=" Europe" [3]=$'\n')

এখানে সমস্যাটি হ'ল readarrayপিছনের ক্ষেত্রটি সংরক্ষণ করা হয়েছে , যেহেতু <<<পুনর্নির্দেশ অপারেটর এলএফকে ইনপুট স্ট্রিংয়ে সংযুক্ত করেছিল, এবং সুতরাং পিছনের ক্ষেত্রটি খালি ছিল না (অন্যথায় এটি বাদ দেওয়া হত)। আমরা বাস্তবতার পরে চূড়ান্ত অ্যারে উপাদানটি সুস্পষ্টভাবে সেট করে এই যত্ন নিতে পারি:

readarray -td, a <<<"$string,"; unset 'a[-1]'; declare -p a;
## declare -a a=([0]="Paris" [1]=" France" [2]=" Europe")

কেবলমাত্র দুটি সমস্যা রয়ে গেছে যা আসলে সম্পর্কিত are (1) এক্সটেনারিয়াস হোয়াইটস্পেস যা ছাঁটাই করা দরকার, এবং (2) মাল্টিচ্যার্যাক্টর ডিলিমিটরদের সমর্থন সমর্থন নেই।

হোয়াইটস্পেস অবশ্যই পরে ছাঁটাই করা যেতে পারে (উদাহরণস্বরূপ, বাশ ভেরিয়েবল থেকে হোয়াইটস্পেসটি কীভাবে ছাঁটাবেন দেখুন? ) তবে আমরা যদি কোনও মাল্টিচার্যাক্টর ডিলিমিটার হ্যাক করতে পারি, তবে এটি উভয়ই এক শটে সমাধান করবে shot

দুর্ভাগ্যক্রমে, কোনও মাল্টিচার্যাক্টর ডিলিমিটারে কাজ করার সরাসরি কোনও উপায় নেই । আমি যে সর্বোত্তম সমাধানটির কথা ভেবেছি তা হ'ল মাল্টিচ্যার্যাক্টর ডিলিমেটারকে একক অক্ষর ডিলিমিটারের সাথে প্রতিস্থাপনের জন্য ইনপুট স্ট্রিংটিকে প্রিপ্রসেস করা যা ইনপুট স্ট্রিংয়ের বিষয়বস্তুগুলির সাথে সংঘর্ষ না হওয়ার গ্যারান্টিযুক্ত হবে। এই গ্যারান্টিটি সহ কেবলমাত্র অক্ষরটি হল NUL বাইট । এটি কারণ, ব্যাশে (যদিও zsh না হলেও, ঘটনাচক্রে), ভেরিয়েবলগুলিতে NUL বাইট থাকতে পারে না। প্রক্রিয়া প্রতিস্থাপনে এই প্রিপ্রোসেসিং পদক্ষেপটি ইনলাইন করা যায়। অ্যাডকে ব্যবহার করে এটি কীভাবে করা যায় তা এখানে :

readarray -td '' a < <(awk '{ gsub(/, /,"\0"); print; }' <<<"$string, "); unset 'a[-1]';
declare -p a;
## declare -a a=([0]="Paris" [1]="France" [2]="Europe")

সেখানে, অবশেষে! এই সমাধানটি ভুলভাবে মাঝখানে ক্ষেত্রগুলি বিভক্ত করবে না, অকালে কাটবে না, খালি ক্ষেত্রগুলি ফেলে দেবে না, ফাইলের নাম প্রসারণে নিজেকে দূষিত করবে না, স্বয়ংক্রিয়ভাবে শীর্ষস্থানীয় এবং অনুসরণকারী শ্বেতস্পেসকে ছাঁটাবে না, শেষের দিকে স্টোওয়ে এলএফ ছাড়বে না, লুপগুলির প্রয়োজন হয় না এবং একটি একক-অক্ষর ডিলিমিটারের জন্য স্থির হয় না।


ট্রিমিং সমাধান

অবশেষে, আমি এর নিজের অস্পষ্ট -C callbackবিকল্পটি ব্যবহার করে আমার নিজের মোটামুটি জটিলতর ট্রিমিং সমাধানটি প্রদর্শন করতে চেয়েছিলাম readarray। দুর্ভাগ্যক্রমে, আমি স্ট্যাক ওভারফ্লোর 30,000 চরিত্রের পোস্টের সীমা ছাড়িয়ে গিয়েছি, তাই আমি এটি ব্যাখ্যা করতে পারব না। আমি পাঠকের জন্য অনুশীলন হিসাবে ছেড়ে দেব।

function mfcb { local val="$4"; "$1"; eval "$2[$3]=\$val;"; };
function val_ltrim { if [[ "$val" =~ ^[[:space:]]+ ]]; then val="${val:${#BASH_REMATCH[0]}}"; fi; };
function val_rtrim { if [[ "$val" =~ [[:space:]]+$ ]]; then val="${val:0:${#val}-${#BASH_REMATCH[0]}}"; fi; };
function val_trim { val_ltrim; val_rtrim; };
readarray -c1 -C 'mfcb val_trim a' -td, <<<"$string,"; unset 'a[-1]'; declare -p a;
## declare -a a=([0]="Paris" [1]="France" [2]="Europe")

8
এছাড়া নোট সহায়ক হতে পারে যে (যদিও এটা ঠিক যে আপনি কোন রুম তা করার ছিল) -dকরার বিকল্প readarrayব্যাশ 4.4 প্রথম প্রদর্শিত হয়।
fbicknel

2
দুর্দান্ত উত্তর (+1)। যদি আপনি আপনার জাগ্রত পরিবর্তন করে awk '{ gsub(/,[ ]+|$/,"\0"); print }'এবং ফাইনালের ", " সেই কনটেন্টেশনটি সরিয়ে ফেলেন তবে চূড়ান্ত রেকর্ডটি সরিয়ে দেওয়ার জন্য আপনাকে জিমন্যাস্টিকের মধ্য দিয়ে যেতে হবে না। সুতরাং: readarray -td '' a < <(awk '{ gsub(/,[ ]+/,"\0"); print; }' <<<"$string")বাশ যে সমর্থন করে readarray। উল্লেখ্য আপনার পদ্ধতি ব্যাশ হয় 4.4+ আমি কারণ মনে -dমধ্যেreadarray
Dawg

3
@ ডাট ইউজার এটি দুর্ভাগ্যজনক। আপনার বাশের সংস্করণটি অবশ্যই খুব পুরানো readarray। এই ক্ষেত্রে, আপনি অন্তর্নির্মিত দ্বিতীয়-সেরা সমাধানটি ব্যবহার করতে পারেন read। আমি এটি উল্লেখ করছি: a=(); while read -rd,; do a+=("$REPLY"); done <<<"$string,";( awkযদি আপনার মাল্টিচার্যাক্টর ডিলিমেটার সমর্থন প্রয়োজন হয় তবে বিকল্পের সাথে )। আপনি যদি কোনও সমস্যায় পড়ে থাকেন তবে আমাকে জানান; আমি নিশ্চিত যে এই দ্রবণটি দুই দশক আগের মত প্রকাশিত, সংস্করণ 2-কিছুতে ফিরে আসার মতো পুরানো সংস্করণের বাশগুলিতে কাজ করা উচিত pretty
বিগোল্ডস্ট

1
বাহ, কী উজ্জ্বল উত্তর! হি হি, আমার প্রতিক্রিয়া: বাশ স্ক্রিপ্টটি নিক্ষেপ করে অজগরটি ছড়িয়ে দিল!
আর্টফুল্রবট

1
ওএসএক্সে @ ডাড ইউজার বাশ এখনও 3.2 এ আটকে আছে (সিএ 2007 প্রকাশিত); আমি ওএস এক্সের 4.X বাশ সংস্করণ পেতে হোমব্রিউতে পাওয়া ব্যাশ ব্যবহার করেছি
জেডিএস

222

আইএফএস সেট না করেই এখানে একটি উপায়:

string="1:2:3:4:5"
set -f                      # avoid globbing (expansion of *).
array=(${string//:/ })
for i in "${!array[@]}"
do
    echo "$i=>${array[i]}"
done

ধারণাটি স্ট্রিং প্রতিস্থাপন ব্যবহার করছে:

${string//substring/replacement}

space এর সমস্ত ম্যাচকে সাদা স্থানের সাথে স্ট্রিং করে এবং তারপরে অ্যারে আরম্ভ করার জন্য বিকল্প স্ট্রিংটি ব্যবহার করে:

(element1 element2 ... elementN)

দ্রষ্টব্য: এই উত্তরটি স্প্লিট + গ্লোব অপারেটরটি ব্যবহার করে । সুতরাং কিছু চরিত্রের বিস্তার রোধ করতে (যেমন *) এই স্ক্রিপ্টটির জন্য গ্লোববিংকে বিরতি দেওয়া ভাল ধারণা।


1
এই পদ্ধতির ব্যবহার করুন ... যতক্ষণ না আমি বিচ্ছিন্ন হয়ে যাওয়ার জন্য একটি দীর্ঘ স্ট্রিং পেরিয়ে এসেছি। এক মিনিটেরও বেশি সময় ধরে 100% সিপিইউ (তারপরে আমি এটি মেরেছি)। এটি অত্যন্ত দুঃখের বিষয় কারণ এই পদ্ধতিটি স্ট্রিং দ্বারা বিভক্ত করতে দেয়, আইএফএসের কোনও অক্ষর নয়।
ওয়ার্নার লেহম্যান

এক মিনিটেরও বেশি সময় ধরে 100% সিপিইউ সময় আমার কাছে মনে হয় যেন কোথাও কোথাও কোনও সমস্যা আছে। এই স্ট্রিংটি কতক্ষণ ছিল, এটি এমবি বা জিবি আকারের? আমি মনে করি, সাধারণত, যদি আপনি কেবল একটি ছোট স্ট্রিং বিভাজন প্রয়োজন, আপনি বাশের মধ্যে থাকতে চান, তবে এটি যদি একটি বিশাল ফাইল হয় তবে আমি পার্লের মতো এটি সম্পাদন করব।

12
সতর্কতা: এই পদ্ধতির সাথে সবেমাত্র একটি সমস্যায় পড়ে। আপনার যদি * নামের একটি উপাদান থাকে তবে আপনি আপনার সিডব্লিউডের সমস্ত উপাদানও পাবেন। এইভাবে স্ট্রিং = "1: 2: 3: 4: *" আপনার প্রয়োগের উপর নির্ভর করে কিছু অপ্রত্যাশিত এবং সম্ভবত বিপজ্জনক ফলাফল দেবে। (আইএফএস = ',' পঠন-অ্যারে <<< "$ স্ট্রিং") এর সাথে একই ত্রুটিটি পেল না এবং এটি ব্যবহার করা নিরাপদ বলে মনে হচ্ছে।
ডিয়েটার গ্রিবনিটস

4
উদ্ধৃতি ${string//:/ }শেল সম্প্রসারণ রোধ করে
অ্যান্ড্রু হোয়াইট

1
আমাকে ওএসএক্সে নিম্নলিখিতটি ব্যবহার করতে হয়েছিল: array=(${string//:/ })
মার্ক থমসন

95
t="one,two,three"
a=($(echo "$t" | tr ',' '\n'))
echo "${a[2]}"

তিনটি মুদ্রণ


8
আমি আসলে এই পদ্ধতির পছন্দ। সহজ।
চিংড়িবাগান

4
আমি এটি অনুলিপি করে আটকালাম এবং এটি প্রতিধ্বনি দিয়ে কাজ করে নি, তবে আমি লুপের জন্য এটি ব্যবহার করার সময় কাজ করেছি।
বেন

2
এটি বলা হিসাবে কাজ করে না। @ Jmoney38 বা চিংড়িওয়ালা যদি আপনি এটি কোনও টার্মিনালে পেস্ট করতে পারেন এবং পছন্দসই আউটপুট পেতে পারেন তবে ফলাফলটি এখানে আটকান।
abalter

2
@ বা্যাল্টার আমার সাথে কাজ করে a=($(echo $t | tr ',' "\n"))। একই ফলাফল a=($(echo $t | tr ',' ' '))
পাতা

@ প্রলিনেটর আমি এটি কেবল VERSION="16.04.2 LTS (Xenial Xerus)"একটি bashশেলের মধ্যে চেষ্টা করেছিলাম এবং শেষটি echoকেবল একটি ফাঁকা রেখা মুদ্রণ করে। আপনি লিনাক্সের কোন সংস্করণ এবং কোন শেল ব্যবহার করছেন? দুর্ভাগ্যক্রমে, একটি মন্তব্যে টার্মিনাল সেশনটি প্রদর্শন করতে পারে না।
আবাল্টার

29

কখনও কখনও আমার ক্ষেত্রে এটি ঘটেছিল যে গৃহীত উত্তরে বর্ণিত পদ্ধতিটি কার্যকর হয়নি, বিশেষত যদি বিভাজক একটি গাড়ীর ফেরত।
এই ক্ষেত্রে আমি এইভাবে সমাধান করেছি:

string='first line
second line
third line'

oldIFS="$IFS"
IFS='
'
IFS=${IFS:0:1} # this is useful to format your code with tabs
lines=( $string )
IFS="$oldIFS"

for line in "${lines[@]}"
    do
        echo "--> $line"
done

2
+1 এটি পুরোপুরি আমার পক্ষে কাজ করেছে। আমার একাধিক স্ট্রিং লাগানো দরকার, একটি নিউলাইন দ্বারা বিভক্ত হয়ে অ্যারে তে পরিণত হয়েছিল, এবং এর read -a arr <<< "$strings"সাথে কাজ করিনি IFS=$'\n'
স্টিফান ভ্যান ড্যান আকার


এটি মূল প্রশ্নের পুরোপুরি উত্তর দেয় না।
মাইক

29

গৃহীত উত্তর এক লাইনে মানগুলির জন্য কাজ করে।
যদি ভেরিয়েবলের কয়েকটি লাইন থাকে:

string='first line
        second line
        third line'

সমস্ত লাইন পেতে আমাদের খুব আলাদা কমান্ডের প্রয়োজন:

while read -r line; do lines+=("$line"); done <<<"$string"

বা আরও সহজ বাশ রিডারারে :

readarray -t lines <<<"$string"

সমস্ত লাইন মুদ্রণ একটি মুদ্রণযন্ত্র বৈশিষ্ট্যের সুবিধা গ্রহণ করা খুব সহজ:

printf ">[%s]\n" "${lines[@]}"

>[first line]
>[        second line]
>[        third line]

2
যদিও প্রতিটি সমাধান প্রতিটি পরিস্থিতিতে কাজ করে না, আপনার পাঠকের কথা উল্লেখ ... আমার শেষ দুটি ঘন্টাটি 5 মিনিটের সাথে প্রতিস্থাপন করেছে ... আপনি আমার ভোট পেয়েছেন
ক্রুদ্ধ 84


6

আপনার স্ট্রিংকে একটি অ্যারেতে বিভক্ত করার মূলটি হ'ল মাল্টি ক্যারেক্টার ডিলিমিটার ", "IFSমাল্টি ক্যারেক্টার ডিলিমিটারগুলির জন্য যে কোনও সমাধান ব্যবহার করা সহজাতভাবে ভুল কারণ আইএফএস এই অক্ষরের একটি সেট, স্ট্রিং নয়।

আপনি যদি নির্ধারিত হন IFS=", "তবে স্ট্রিংটি EITHER ","OR " "বা তাদের কোনও সংমিশ্রণে ভেঙে যাবে যা দুটি চরিত্রের ডেলিমিটারের সঠিক উপস্থাপনা নয় ", "

প্রক্রিয়া প্রতিস্থাপনের সাথে আপনি স্ট্রিংটি ব্যবহার করতে awkবা sedবিভক্ত করতে পারেন :

#!/bin/bash

str="Paris, France, Europe"
array=()
while read -r -d $'\0' each; do   # use a NUL terminated field separator 
    array+=("$each")
done < <(printf "%s" "$str" | awk '{ gsub(/,[ ]+|$/,"\0"); print }')
declare -p array
# declare -a array=([0]="Paris" [1]="France" [2]="Europe") output

সরাসরি ব্যাশে আপনাকে একটি রেজেেক্স ব্যবহার করা আরও দক্ষ:

#!/bin/bash

str="Paris, France, Europe"

array=()
while [[ $str =~ ([^,]+)(,[ ]+|$) ]]; do
    array+=("${BASH_REMATCH[1]}")   # capture the field
    i=${#BASH_REMATCH}              # length of field + delimiter
    str=${str:i}                    # advance the string by that length
done                                # the loop deletes $str, so make a copy if needed

declare -p array
# declare -a array=([0]="Paris" [1]="France" [2]="Europe") output...

দ্বিতীয় ফর্মের সাথে কোনও সাব শেল নেই এবং এটি অন্তর্নিহিত আরও দ্রুত হবে।


বগলডস্ট দ্বারা সম্পাদনা করুন: ডগেরreadarray রেজেক্স সমাধানের সাথে আমার সমাধানটির সাথে তুলনা করার জন্য এখানে কিছু মানদণ্ড রয়েছে এবং আমি এটির হেকেরread জন্য সমাধানটিও অন্তর্ভুক্ত করেছি (দ্রষ্টব্য: আমার সমাধানের সাথে আরও বেশি সামঞ্জস্যের জন্য আমি রেজেক্স সমাধানটি কিছুটা সংশোধন করেছি) (নীচে আমার মন্তব্যগুলি দেখুন) পোস্ট):

## competitors
function c_readarray { readarray -td '' a < <(awk '{ gsub(/, /,"\0"); print; };' <<<"$1, "); unset 'a[-1]'; };
function c_read { a=(); local REPLY=''; while read -r -d ''; do a+=("$REPLY"); done < <(awk '{ gsub(/, /,"\0"); print; };' <<<"$1, "); };
function c_regex { a=(); local s="$1, "; while [[ $s =~ ([^,]+),\  ]]; do a+=("${BASH_REMATCH[1]}"); s=${s:${#BASH_REMATCH}}; done; };

## helper functions
function rep {
    local -i i=-1;
    for ((i = 0; i<$1; ++i)); do
        printf %s "$2";
    done;
}; ## end rep()

function testAll {
    local funcs=();
    local args=();
    local func='';
    local -i rc=-1;
    while [[ "$1" != ':' ]]; do
        func="$1";
        if [[ ! "$func" =~ ^[_a-zA-Z][_a-zA-Z0-9]*$ ]]; then
            echo "bad function name: $func" >&2;
            return 2;
        fi;
        funcs+=("$func");
        shift;
    done;
    shift;
    args=("$@");
    for func in "${funcs[@]}"; do
        echo -n "$func ";
        { time $func "${args[@]}" >/dev/null 2>&1; } 2>&1| tr '\n' '/';
        rc=${PIPESTATUS[0]}; if [[ $rc -ne 0 ]]; then echo "[$rc]"; else echo; fi;
    done| column -ts/;
}; ## end testAll()

function makeStringToSplit {
    local -i n=$1; ## number of fields
    if [[ $n -lt 0 ]]; then echo "bad field count: $n" >&2; return 2; fi;
    if [[ $n -eq 0 ]]; then
        echo;
    elif [[ $n -eq 1 ]]; then
        echo 'first field';
    elif [[ "$n" -eq 2 ]]; then
        echo 'first field, last field';
    else
        echo "first field, $(rep $[$1-2] 'mid field, ')last field";
    fi;
}; ## end makeStringToSplit()

function testAll_splitIntoArray {
    local -i n=$1; ## number of fields in input string
    local s='';
    echo "===== $n field$(if [[ $n -ne 1 ]]; then echo 's'; fi;) =====";
    s="$(makeStringToSplit "$n")";
    testAll c_readarray c_read c_regex : "$s";
}; ## end testAll_splitIntoArray()

## results
testAll_splitIntoArray 1;
## ===== 1 field =====
## c_readarray   real  0m0.067s   user 0m0.000s   sys  0m0.000s
## c_read        real  0m0.064s   user 0m0.000s   sys  0m0.000s
## c_regex       real  0m0.000s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 10;
## ===== 10 fields =====
## c_readarray   real  0m0.067s   user 0m0.000s   sys  0m0.000s
## c_read        real  0m0.064s   user 0m0.000s   sys  0m0.000s
## c_regex       real  0m0.001s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 100;
## ===== 100 fields =====
## c_readarray   real  0m0.069s   user 0m0.000s   sys  0m0.062s
## c_read        real  0m0.065s   user 0m0.000s   sys  0m0.046s
## c_regex       real  0m0.005s   user 0m0.000s   sys  0m0.000s
##
testAll_splitIntoArray 1000;
## ===== 1000 fields =====
## c_readarray   real  0m0.084s   user 0m0.031s   sys  0m0.077s
## c_read        real  0m0.092s   user 0m0.031s   sys  0m0.046s
## c_regex       real  0m0.125s   user 0m0.125s   sys  0m0.000s
##
testAll_splitIntoArray 10000;
## ===== 10000 fields =====
## c_readarray   real  0m0.209s   user 0m0.093s   sys  0m0.108s
## c_read        real  0m0.333s   user 0m0.234s   sys  0m0.109s
## c_regex       real  0m9.095s   user 0m9.078s   sys  0m0.000s
##
testAll_splitIntoArray 100000;
## ===== 100000 fields =====
## c_readarray   real  0m1.460s   user 0m0.326s   sys  0m1.124s
## c_read        real  0m2.780s   user 0m1.686s   sys  0m1.092s
## c_regex       real  17m38.208s   user 15m16.359s   sys  2m19.375s
##

খুব শীতল সমাধান! আমি একটি Regex ম্যাচ, এর ছিমছাম ব্যবহারের উপর একটি লুপ ব্যবহার করে কখনও ভাবি নি $BASH_REMATCH। এটি কাজ করে, এবং প্রকৃতপক্ষে স্পেলিং সাবসেলগুলি এড়ায় না। আমার কাছ থেকে +1 যাইহোক, সমালোচনার মাধ্যমে, রেজেক্স নিজেই কিছুটা অ-আদর্শ, এটি উপস্থিত হতে পারে যে আপনাকে ডিলিমিটার টোকেনের অংশটি (বিশেষত কমা) নকল করতে বাধ্য করা হয়েছিল যাতে অ-লোভী গুণকগুলির সমর্থনের অভাবকে ঘিরে কাজ করতে পারে (এছাড়াও তাত্পর্যপূর্ণ) এআরআর ("বর্ধিত" রেগেক্স গন্ধটি ব্যাশে তৈরি)। এটি এটিকে কিছুটা কম জেনেরিক এবং মজবুত করে তোলে।
বিগল্ডস্ট

দ্বিতীয়ত, আমি কিছু বেঞ্চমার্কিং করেছি, এবং যদিও ক্ষুদ্রতর স্ট্রিংগুলির জন্য অন্যান্য সমাধানগুলির তুলনায় পারফরম্যান্স ভাল, তবে এটি বার বার স্ট্রিং-পুনর্নির্মাণের কারণে খুব তীব্র স্ট্রিংয়ের জন্য বিপর্যয়কর হয়ে ওঠার কারণে দ্রুততর হয়ে ওঠে। আপনার উত্তরে আমার সম্পাদনা দেখুন।
বিগল্ডস্ট

@ বিগল্ডস্ট: কি দুর্দান্ত মানদণ্ড! রেজেক্সের প্রতিরক্ষার জন্য, 10 বা 100 এর হাজার হাজার ক্ষেত্রের জন্য (রেজেক্সটি কী বিভাজন করছে) সম্ভবত \nসেই ক্ষেত্রগুলির মধ্যে কিছু রেকর্ড (যেমন সীমিত পাঠ্য রেখাগুলি) থাকবে যাতে বিপর্যয়ী ধীরগতির সম্ভবত না ঘটে। আপনার যদি 100,000 ক্ষেত্রের সাথে স্ট্রিং থাকে - তবে বাশ আদর্শ নয় ;-) মানদণ্ডের জন্য ধন্যবাদ। আমি একটি বা দুটি জিনিস শিখেছি।
ডগ

4

খাঁটি বাশ মাল্টি-ক্যারেক্টার ডিলিমিটার সলিউশন।

যেহেতু অন্যরা এই থ্রেডটিতে ইঙ্গিত করেছে, ওপি-র প্রশ্নটি কমা সীমানাঙ্কিত স্ট্রিংয়ের একটি অ্যারেতে বিভক্ত হওয়ার উদাহরণ দিয়েছে, তবে তিনি / তিনি কেবল কমা সীমানা, একক চরিত্রের সীমানা বা বহু-চরিত্রের প্রতি আগ্রহী ছিলেন কিনা তা চিহ্নিত করে না বিভেদক।

গুগল যেহেতু অনুসন্ধানের ফলাফলগুলির শীর্ষে বা তার কাছাকাছি এই উত্তরটি র‌্যাঙ্ক করে, তাই আমি পাঠকদের একাধিক চরিত্রের ডেলিমিটারদের প্রশ্নের জোরালো উত্তর দিতে চেয়েছিলাম, যেহেতু এটি অন্তত একটি প্রতিক্রিয়াতেও উল্লেখ করা হয়েছে।

আপনি যদি কোনও বহু-চরিত্রের ডিলিমিটার সমস্যার সমাধানের সন্ধানে থাকেন তবে আমি মল্লিকার্জুন এম এর পোস্ট পর্যালোচনা করার পরামর্শ দিচ্ছি , বিশেষত gniourf_gniourf এর প্রতিক্রিয়া যা এই প্যারামিটার সম্প্রসারণ ব্যবহার করে এই মার্জিত খাঁটি BASH সমাধান সরবরাহ করে:

#!/bin/bash
str="LearnABCtoABCSplitABCaABCString"
delimiter=ABC
s=$str$delimiter
array=();
while [[ $s ]]; do
    array+=( "${s%%"$delimiter"*}" );
    s=${s#*"$delimiter"};
done;
declare -p array

লিঙ্ক করুন উদাহৃত মন্তব্য / উল্লেখিত পোস্ট

উদ্ধৃত প্রশ্নের লিঙ্ক: বাশ-এ একাধিক-অক্ষর ডিলিমিটারে একটি স্ট্রিং কীভাবে বিভক্ত করবেন?


1
অনুরূপ তবে উন্নত পদ্ধতির জন্য আমার মন্তব্য দেখুন ।
xebeche

3

এটি ওএসএক্সে আমার জন্য কাজ করে:

string="1 2 3 4 5"
declare -a array=($string)

যদি আপনার স্ট্রিংয়ের আলাদা আলাদা সীমানা থাকে তবে কেবল 1 তম স্থান সহ এগুলি প্রতিস্থাপন করুন:

string="1,2,3,4,5"
delimiter=","
declare -a array=($(echo $string | tr "$delimiter" " "))

সরল :-)


বাশ এবং জেশ উভয়ের জন্য কাজ করে যা একটি প্লাস!
এলিয়াহ ডাব্লু। গ্যাগেন

2

আইএফএস পরিবর্তন না করে এটি করার আর একটি উপায়:

read -r -a myarray <<< "${string//, /$IFS}"

আমাদের কাঙ্ক্ষিত ডিলিমিটারের সাথে মেলে আইএফএস পরিবর্তনের পরিবর্তে আমরা আমাদের কাঙ্ক্ষিত ডিলিমিটারের সমস্ত ঘটনাকে ", "উপকরণের $IFSমাধ্যমে প্রতিস্থাপন করতে পারি "${string//, /$IFS}"

যদিও এটি খুব বড় স্ট্রিংয়ের জন্য ধীর হবে?

এটি ডেনিস উইলিয়ামসনের উত্তরের ভিত্তিতে তৈরি।


2

আমি এই পোস্টটি জুড়ে এসেছি: ইনপুট যেমন ওয়ার্ড 1, ওয়ার্ড 2, ...

উপরের কেউই আমাকে সাহায্য করেনি। awk ব্যবহার করে এটি সমাধান করুন। যদি এটি কাউকে সহায়তা করে:

STRING="value1,value2,value3"
array=`echo $STRING | awk -F ',' '{ s = $1; for (i = 2; i <= NF; i++) s = s "\n"$i; print s; }'`
for word in ${array}
do
        echo "This is the word $word"
done

1

এটা চেষ্টা কর

IFS=', '; array=(Paris, France, Europe)
for item in ${array[@]}; do echo $item; done

ইহা সাধারণ. আপনি চাইলে, আপনি একটি ঘোষণাপত্র যুক্ত করতে পারেন (এবং কমাগুলিও সরিয়ে ফেলুন):

IFS=' ';declare -a array=(Paris France Europe)

আইএফএসকে উপরেরটি পূর্বাবস্থায় যোগ করা হয়েছে তবে এটি একটি তাজা বাশ উদাহরণ ছাড়াই কাজ করে


1

অ্যারে অবজেক্টে স্ট্রিং বিভক্ত করতে আমরা tr কমান্ড ব্যবহার করতে পারি। এটি ম্যাকওএস এবং লিনাক্স উভয়ই কাজ করে

  #!/usr/bin/env bash
  currentVersion="1.0.0.140"
  arrayData=($(echo $currentVersion | tr "." "\n"))
  len=${#arrayData[@]}
  for (( i=0; i<=$((len-1)); i++ )); do 
       echo "index $i - value ${arrayData[$i]}"
  done

আর একটি বিকল্প আইএফএস কমান্ড ব্যবহার করে

IFS='.' read -ra arrayData <<< "$currentVersion"
#It is the same as tr
arrayData=($(echo $currentVersion | tr "." "\n"))

#Print the split string
for i in "${arrayData[@]}"
do
    echo $i
done

0

এটা ব্যবহার কর:

countries='Paris, France, Europe'
OIFS="$IFS"
IFS=', ' array=($countries)
IFS="$OIFS"

#${array[1]} == Paris
#${array[2]} == France
#${array[3]} == Europe

3
খারাপ: শব্দ বিভাজন এবং পথের নাম প্রসারণ সাপেক্ষে। খারাপ উত্তর দেওয়ার জন্য দয়া করে পুরানো প্রশ্নগুলিকে ভাল উত্তর দিয়ে পুনরুদ্ধার করবেন না।
gniourf_gniourf

2
এটি একটি খারাপ উত্তর হতে পারে, তবে এটি এখনও একটি বৈধ উত্তর। ফ্ল্যাগার / পর্যালোচক: এই উত্তর হিসাবে ভুল উত্তরের জন্য, ডাউনভোট, মুছবেন না!
স্কট ওয়েলডন

2
@gniourf_gniourf আপনি দয়া করে ব্যাখ্যা করতে পারেন কেন এটি খারাপ উত্তর? কখন ব্যর্থ হয় আমি সত্যিই বুঝতে পারি না।
জর্জ সোভেটোভ

3
@ জর্জসোভেতভ: যেমনটি আমি বলেছিলাম, এটি শব্দ বিভাজন এবং পথের নাম প্রসারের বিষয় subject আরো সাধারণভাবে, বিভাজন একটি অ্যারের মধ্যে একটি স্ট্রিং হিসাবে array=( $string ): একটি (দুঃখিতভাবে খুব সাধারণ) antipattern হয় শব্দ বিভাজন ঘটে: string='Prague, Czech Republic, Europe'; পথের নাম সম্প্রসারণ ঘটে: string='foo[abcd],bar[efgh]'যদি আপনার নামের কোনও ফাইল থাকে তবে উদাহরণস্বরূপ foodবা barfআপনার ডিরেক্টরিতে ব্যর্থ হবে । যখন stringগ্লোব হয় তখন এ জাতীয় নির্মাণের একমাত্র বৈধ ব্যবহার ।
gniourf_gniourf

0

আপডেট: ওয়াল নিয়ে সমস্যার কারণে এটি করবেন না।

কিছুটা কম অনুষ্ঠানের সাথে:

IFS=', ' eval 'array=($string)'

যেমন

string="foo, bar,baz"
IFS=', ' eval 'array=($string)'
echo ${array[1]} # -> bar

4
খারাপ খারাপ! এটি করবেন না।
সিজারসোল

1
Pfft। আপনি যদি এটির জন্য বড় স্ক্রিপ্টগুলি লিখতে থাকেন তবে আপনি এটি ভুল করছেন। অ্যাপ্লিকেশন কোডে, alভালটি মন্দ। শেল স্ক্রিপ্টিং এ এটি সাধারণ, প্রয়োজনীয় এবং অসম্পূর্ণ।
ব্যবহারকারী 1009908

2
$আপনার পরিবর্তনশীলটিতে একটি রাখুন এবং আপনি দেখতে পাবেন ... আমি অনেকগুলি স্ক্রিপ্ট লিখি এবং আমাকে কখনও একক ব্যবহার করতে হয়নিeval
সিজারসোল

2
আপনি ঠিক বলেছেন, ইনপুটটি পরিষ্কার হিসাবে পরিচিত হলে এটি কেবল ব্যবহারযোগ্য be একটি দৃ solution় সমাধান নয়।
ব্যবহারকারী 1009908

কেবলমাত্র একবারই আমি ওয়াল ব্যবহার করতে হয়েছিল, এমন একটি অ্যাপ্লিকেশনের জন্য যা নিজের কোড / মডিউলগুলি নিজেই তৈরি করতে পারে ... এবং এটির
ক্রুদ্ধ 84

0

এই যে আমার হ্যাক!

স্ট্রিং দ্বারা স্ট্রিংগুলি বিভাজক করা ব্যাশ ব্যবহার করে করা বেশ সুন্দর একঘেয়ে কাজ। যা ঘটে তা হ'ল আমাদের কাছে সীমিত পন্থা রয়েছে যা কেবলমাত্র কয়েকটি ক্ষেত্রে কাজ করে (";", "/", "।" ইত্যাদি দ্বারা বিভক্ত) বা আউটপুটগুলিতে আমাদের বিভিন্ন ধরণের পার্শ্ব প্রতিক্রিয়া রয়েছে।

নীচের পদ্ধতির জন্য বেশ কয়েকটি কসরত প্রয়োজন, তবে আমি বিশ্বাস করি এটি আমাদের বেশিরভাগ প্রয়োজনের জন্য কাজ করবে!

#!/bin/bash

# --------------------------------------
# SPLIT FUNCTION
# ----------------

F_SPLIT_R=()
f_split() {
    : 'It does a "split" into a given string and returns an array.

    Args:
        TARGET_P (str): Target string to "split".
        DELIMITER_P (Optional[str]): Delimiter used to "split". If not 
    informed the split will be done by spaces.

    Returns:
        F_SPLIT_R (array): Array with the provided string separated by the 
    informed delimiter.
    '

    F_SPLIT_R=()
    TARGET_P=$1
    DELIMITER_P=$2
    if [ -z "$DELIMITER_P" ] ; then
        DELIMITER_P=" "
    fi

    REMOVE_N=1
    if [ "$DELIMITER_P" == "\n" ] ; then
        REMOVE_N=0
    fi

    # NOTE: This was the only parameter that has been a problem so far! 
    # By Questor
    # [Ref.: https://unix.stackexchange.com/a/390732/61742]
    if [ "$DELIMITER_P" == "./" ] ; then
        DELIMITER_P="[.]/"
    fi

    if [ ${REMOVE_N} -eq 1 ] ; then

        # NOTE: Due to bash limitations we have some problems getting the 
        # output of a split by awk inside an array and so we need to use 
        # "line break" (\n) to succeed. Seen this, we remove the line breaks 
        # momentarily afterwards we reintegrate them. The problem is that if 
        # there is a line break in the "string" informed, this line break will 
        # be lost, that is, it is erroneously removed in the output! 
        # By Questor
        TARGET_P=$(awk 'BEGIN {RS="dn"} {gsub("\n", "3F2C417D448C46918289218B7337FCAF"); printf $0}' <<< "${TARGET_P}")

    fi

    # NOTE: The replace of "\n" by "3F2C417D448C46918289218B7337FCAF" results 
    # in more occurrences of "3F2C417D448C46918289218B7337FCAF" than the 
    # amount of "\n" that there was originally in the string (one more 
    # occurrence at the end of the string)! We can not explain the reason for 
    # this side effect. The line below corrects this problem! By Questor
    TARGET_P=${TARGET_P%????????????????????????????????}

    SPLIT_NOW=$(awk -F"$DELIMITER_P" '{for(i=1; i<=NF; i++){printf "%s\n", $i}}' <<< "${TARGET_P}")

    while IFS= read -r LINE_NOW ; do
        if [ ${REMOVE_N} -eq 1 ] ; then

            # NOTE: We use "'" to prevent blank lines with no other characters 
            # in the sequence being erroneously removed! We do not know the 
            # reason for this side effect! By Questor
            LN_NOW_WITH_N=$(awk 'BEGIN {RS="dn"} {gsub("3F2C417D448C46918289218B7337FCAF", "\n"); printf $0}' <<< "'${LINE_NOW}'")

            # NOTE: We use the commands below to revert the intervention made 
            # immediately above! By Questor
            LN_NOW_WITH_N=${LN_NOW_WITH_N%?}
            LN_NOW_WITH_N=${LN_NOW_WITH_N#?}

            F_SPLIT_R+=("$LN_NOW_WITH_N")
        else
            F_SPLIT_R+=("$LINE_NOW")
        fi
    done <<< "$SPLIT_NOW"
}

# --------------------------------------
# HOW TO USE
# ----------------

STRING_TO_SPLIT="
 * How do I list all databases and tables using psql?

\"
sudo -u postgres /usr/pgsql-9.4/bin/psql -c \"\l\"
sudo -u postgres /usr/pgsql-9.4/bin/psql <DB_NAME> -c \"\dt\"
\"

\"
\list or \l: list all databases
\dt: list all tables in the current database
\"

[Ref.: /dba/1285/how-do-i-list-all-databases-and-tables-using-psql]


"

f_split "$STRING_TO_SPLIT" "bin/psql -c"

# --------------------------------------
# OUTPUT AND TEST
# ----------------

ARR_LENGTH=${#F_SPLIT_R[*]}
for (( i=0; i<=$(( $ARR_LENGTH -1 )); i++ )) ; do
    echo " > -----------------------------------------"
    echo "${F_SPLIT_R[$i]}"
    echo " < -----------------------------------------"
done

if [ "$STRING_TO_SPLIT" == "${F_SPLIT_R[0]}bin/psql -c${F_SPLIT_R[1]}" ] ; then
    echo " > -----------------------------------------"
    echo "The strings are the same!"
    echo " < -----------------------------------------"
fi

0

বহুবিধ উপাদানগুলির জন্য কেন এমন কিছু হয় না

$ array=($(echo -e $'a a\nb b' | tr ' ' '§')) && array=("${array[@]//§/ }") && echo "${array[@]/%/ INTERELEMENT}"

a a INTERELEMENT b b INTERELEMENT

-1

আর একটি উপায় হবে:

string="Paris, France, Europe"
IFS=', ' arr=(${string})

এখন আপনার উপাদানগুলি "অ্যারে" অ্যারেতে সঞ্চিত আছে। উপাদানগুলির মাধ্যমে পুনরাবৃত্তি করতে:

for i in ${arr[@]}; do echo $i; done

1
আমি আমার উত্তরে এই ধারণাটি আবরণ করি ; দেখতে ভুল উত্তর # 5 (আপনি বিশেষভাবে আমার আলোচনায় আগ্রহী হতে পারে evalকৌতুক)। আপনার সমাধানটি $IFSসত্যের পরে কমা-স্থানের মানকে সেট করে।
বিগোল্ডস্ট

-1

যেহেতু এটি সমাধান করার অনেকগুলি উপায় রয়েছে, আসুন আমরা আমাদের সমাধানে কী দেখতে চাই তা নির্ধারণ করে শুরু করি।

  1. বাশ readarrayএই উদ্দেশ্যে একটি বিল্টিন সরবরাহ করে । আসুন এটি ব্যবহার করুন।
  2. কুরুচিপূর্ণ এবং অপ্রয়োজনীয় কৌশল যেমন IFSএলোমেলো, লুপিং, ব্যবহার evalবা অতিরিক্ত উপাদান যুক্ত করে এড়িয়ে চলুন ।
  3. একটি সাধারণ, পঠনযোগ্য পদ্ধতির সন্ধান করুন যা সহজেই অনুরূপ সমস্যার সাথে মানিয়ে নেওয়া যায়।

readarrayকমান্ড সবচেয়ে সহজ পদ্ধিতি হল বিভেদক হিসাবে নতুন লাইন সঙ্গে ব্যবহার করার জন্য নয়। অন্যান্য ডিলিমিটারগুলির সাথে এটি অ্যারেতে অতিরিক্ত উপাদান যুক্ত করতে পারে। সবচেয়ে পরিষ্কার পন্থাটি হ'ল প্রথমে আমাদের ইনপুটটিকে এমন একটি ফর্মের সাথে অভিযোজিত করা যা এতে প্রবেশের readarrayআগে সুন্দরভাবে কাজ করে।

এই উদাহরণে ইনপুট নেই না একটি multicharacter বিভেদক আছে। আমরা যদি কিছুটা সাধারণ জ্ঞান প্রয়োগ করি তবে এটি কমা বিচ্ছিন্ন ইনপুট হিসাবে ভাল বোঝা যায় যার জন্য প্রতিটি উপাদানকে ছাঁটাই করা দরকার। আমার সমাধানটি হ'ল ইনপুটটি কমা দ্বারা একাধিক লাইনে বিভক্ত করা, প্রতিটি উপাদানকে ছাঁটাই করা এবং এগুলি সবগুলিতে প্রেরণ করা readarray

string='  Paris,France  ,   All of Europe  '
readarray -t foo < <(tr ',' '\n' <<< "$string" |sed 's/^ *//' |sed 's/ *$//')
declare -p foo

# declare -a foo='([0]="Paris" [1]="France" [2]="All of Europe")'

-2

অন্য পদ্ধতির হতে পারে:

str="a, b, c, d"  # assuming there is a space after ',' as in Q
arr=(${str//,/})  # delete all occurrences of ','

এর পরে 'আরর' হল চারটি স্ট্রিং সহ একটি অ্যারে। এর জন্য আইএফএসের পড়া বা পড়া বা অন্য কোনও বিশেষ স্টাফের প্রয়োজন নেই তাই খুব সহজ এবং সরাসরি।


অন্যান্য উত্তর হিসাবে একই (দুঃখজনকভাবে সাধারণ) অ্যান্টিপ্যাটার্ন: শব্দ বিভাজন এবং ফাইলের নাম বিস্তারের সাপেক্ষে।
gniourf_gniourf
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.