"আইএফএস = পঠন-রেখা" বোঝা


60

আমি স্পষ্টতই বুঝতে পারি যে কোনও একটি অভ্যন্তরীণ ক্ষেত্র বিভাজক ভেরিয়েবলের মান যোগ করতে পারে। উদাহরণ স্বরূপ:

$ IFS=blah
$ echo "$IFS"
blah
$ 

আমি এটিও বুঝতে পারি যে নামগুলি পরিবর্তনশীল read -r lineথেকে ডেটা সংরক্ষণ করবে :stdinline

$ read -r line <<< blah
$ echo "$line"
blah
$ 

যাইহোক, একটি কমান্ড কিভাবে চলক মান নির্ধারণ করতে পারে? এবং এটি কি প্রথমে ডেটা stdinভেরিয়েবল থেকে স্টোর করে lineএবং তারপরে এর মান lineদেয় IFS?


3

উত্তর:


104

কিছু লোকের এমন ভুল ধারণা রয়েছে যা readএকটি লাইন পড়ার আদেশ command এটা না।

readএকটি (সম্ভবত ব্যাকস্ল্যাশ-অবিরত) লাইন থেকে শব্দগুলি পড়ে , যেখানে শব্দগুলি $IFSসীমানাযুক্ত হয় এবং সীমানা ছাড়ানো (বা অবিরত রেখাগুলি) থেকে বাঁচতে ব্যাকস্ল্যাশ ব্যবহার করা যেতে পারে।

জেনেরিক সিনট্যাক্সটি হ'ল:

read word1 word2... remaining_words

readএকটি সময়ে stdin এক বাইট লেখা পর্যন্ত এটি খুঁজে বের করে একটি unescaped newline অক্ষর (অথবা শেষ অফ ইনপুট), splits যে বিভাজন ফল মধ্যে জটিল নিয়ম এবং দোকান অনুযায়ী $word1, $word2... $remaining_words

উদাহরণস্বরূপ একটি ইনপুট যেমন:

  <tab> foo bar\ baz   bl\ah   blah\
whatever whatever

এবং এর ডিফল্ট মান সহ $IFS, read a b cবরাদ্দ করা হবে:

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

এখন যদি কেবল একটি যুক্তি পাস হয় তবে তা হয়ে যায় না read line। এখনও আছে read remaining_words। ব্যাকস্ল্যাশ প্রসেসিং এখনও সম্পন্ন হয়েছে, আইএফএস শ্বেতস্পেস অক্ষরগুলি এখনও শুরু এবং শেষ থেকে সরানো হয়েছে।

-rবিকল্প ব্যাকস্ল্যাশ প্রক্রিয়াকরণ সরিয়ে ফেলা হয়। সুতরাং উপরে একই কমান্ড -rপরিবর্তে বরাদ্দ করা হবে

  • $afoo
  • $bbar\
  • $cbaz bl\ah blah\

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

সঙ্গে IFS=:( :না একটি IFS হোয়াইটস্পেস অক্ষর হচ্ছে), মত একটি ইনপুট :foo::bar::বিভক্ত করা হবে "", "foo", "", barএবং ""(এবং একটি অতিরিক্ত ""কিছু বাস্তবায়নের সঙ্গে যে যদিও ছাড়া কোন ব্যাপার না read -a)। আমরা যদি :স্থানটির সাথে এটি প্রতিস্থাপন করি তবে বিভাজনটি কেবল fooএবং এর মধ্যেই করা হয় bar। এটি শীর্ষস্থানীয় এবং পিছনের দিকগুলি অগ্রাহ্য করা হয় এবং সেগুলির ক্রমগুলি একের মতো বিবেচিত হয়। হোয়াইটস্পেস এবং অ-শ্বেতস্পেস অক্ষরগুলিকে একত্রিত করার সময় অতিরিক্ত নিয়ম রয়েছে $IFS। কিছু বাস্তবায়ন আইএফএস ( IFS=::বা IFS=' ') এর অক্ষরগুলিকে দ্বিগুণ করে বিশেষ চিকিত্সা যুক্ত / সরাতে পারে ।

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

এমনকি আইএফএস-নন-হোয়াইটস্পেস অক্ষরগুলি সহ, যদি ইনপুট লাইনে সেই অক্ষরগুলির মধ্যে একটি (এবং কেবলমাত্র একটি) থাকে এবং এটি পসিক্স শেলগুলির ( IFS=: read -r wordযেমন কোনও সংস্করণ foo:নয়) লাইনটির শেষ অক্ষর (যেমন কোনও ইনপুটে পছন্দ করে ) থাকে তবে সেই ইনপুট একটি শব্দ হিসাবে বিবেচনা করা হয় কারণ এই শেলগুলিতে, অক্ষরগুলি টার্মিনেটর হিসাবে বিবেচিত হয় , সুতরাং এটি থাকবে , না ।zshpdkshfoo$IFSwordfoofoo:

সুতরাং, readবিল্টিনের সাথে ইনপুটগুলির একটি লাইন পড়ার আধ্যাত্মিক উপায় হ'ল:

IFS= read -r line

(নোট করুন যে বেশিরভাগ readবাস্তবায়নের জন্য, এটি কেবল পাঠ্য লাইনের জন্য কাজ করে কারণ NUL অক্ষরটি ব্যতীত সমর্থনযোগ্য নয় zsh)।

var=value cmdসিনট্যাক্স ব্যবহার নিশ্চিত IFSকরে যে এই cmdআদেশের সময়কালের জন্য আলাদাভাবে সেট করা আছে ।

ইতিহাস নোট

readBuiltin বোর্ন শেল চালু এবং পড়তে ইতিমধ্যে ছিল শব্দ , না লাইন। আধুনিক পসিক্স শেলগুলির সাথে কয়েকটি গুরুত্বপূর্ণ পার্থক্য রয়েছে।

বোর্ন শেল readএকটি -rবিকল্প (যা কর্ন শেল দ্বারা প্রবর্তিত হয়েছিল) সমর্থন করে না , সুতরাং সেখানে কিছু sed 's/\\/&&/g'আছে এমন ইনপুট প্রাক প্রসেসিং ছাড়া ব্যাকস্ল্যাশ প্রক্রিয়াকরণ অক্ষম করার কোন উপায় নেই।

বোর্ন শেলটিতে দুটি শ্রেণির অক্ষরের ধারণা নেই (যা আবার কেএসএস দ্বারা প্রবর্তিত হয়েছিল)। বোর্ন শেল সমস্ত অক্ষর একই চিকিত্সা ভুগা যেমন IFS হোয়াইটস্পেস অক্ষর ksh না, যে IFS=: read a b cমত একটি ইনপুট উপর foo::barদায়িত্ব অর্পণ করবে barকরার $b, না খালি স্ট্রিং।

বোর্ন শেল এ, সাথে:

var=value cmd

যদি cmdকোনও অন্তর্নির্মিত (যেমন readহয়) varথাকে তবে শেষ হয়ে যাওয়ার valueপরে সেট থাকে cmd। এটি বিশেষত সমালোচিত $IFSকারণ বোর্ন শেলের মধ্যে, $IFSসমস্ত বিস্তৃত করতে ব্যবহৃত হয়, কেবল বিস্তৃতি নয়। এছাড়াও, আপনি যদি $IFSবোর্ন শেল থেকে স্থানের অক্ষরটি সরিয়ে ফেলেন তবে "$@"আর কাজ করবে না।

বোর্ন শেল-এ, একটি যৌগিক কমান্ড পুনর্নির্দেশের ফলে এটি একটি সাব-শেলের মধ্যে চালিত হয় (প্রথম দিকের সংস্করণগুলিতে, এমনকি পছন্দ মতো read var < fileবা exec 3< file; read var <&3কাজ করে না), সুতরাং readটার্মিনালের ব্যবহারকারীর ইনপুট ব্যতীত বোর্ন শেল ব্যবহার করা বিরল was (যেখানে সেই লাইন ধারাবাহিকতা পরিচালনা করে তোলে)

কিছু ইউনিসে (এইচপি / ইউএক্সের মতো, এর মধ্যে একটিও রয়েছে util-linux) এখনও একটি lineইনপুট পড়ার জন্য কমান্ড রয়েছে ( এটি সিঙ্গেল ইউনিক্স স্পেসিফিকেশন সংস্করণ 2 অবধি স্ট্যান্ডার্ড ইউনিক্স কমান্ড আপ হিসাবে ব্যবহৃত হত )।

এটি মূলত একইরকম head -n 1যে এটি একবারে এক বাইট পড়ে তা নিশ্চিত করার জন্য এটি এক লাইনের বেশি পড়বে না। এই সিস্টেমগুলিতে, আপনি এটি করতে পারেন:

line=`line`

অবশ্যই, এর অর্থ একটি নতুন প্রক্রিয়া তৈরি করা, একটি কমান্ড চালানো এবং পাইপের মাধ্যমে তার আউটপুটটি পড়ুন, সুতরাং ksh এর চেয়ে অনেক কম দক্ষ IFS= read -r line, তবে এখনও অনেক বেশি স্বজ্ঞাত।


3
+1 ব্যাশে আইএফএসে স্থান / ট্যাব বনাম "অন্য" এর বিবিধ চিকিত্সার জন্য কিছু দরকারী অন্তর্দৃষ্টি দেওয়ার জন্য ধন্যবাদ ... আমি জানতাম যে তাদের সাথে অন্যরকম আচরণ করা হয়েছিল, তবে এই ব্যাখ্যাটি এটিকে অনেক সহজ করে দেয়। (এবং বাশ (এবং অন্যান্য পক্সিক শেলগুলির মধ্যে অন্তর্দৃষ্টি এবং নিয়মিত shপার্থক্যগুলিও বহনযোগ্য স্ক্রিপ্টগুলি লেখার জন্য কার্যকর!))
অলিভিয়ার ডুলাক

কমপক্ষে bash-4.4.19, while read -r; do echo "'$REPLY'"; doneহিসাবে কাজ করে while IFS= read -r line; do echo "'$line'"; done
এক্স-ইউরি

এটি: "... সেই ভ্রান্ত ধারণাটি যে পড়তে পারে তা একটি লাইন পড়ার আদেশ ..." আমাকে ভাবতে পরিচালিত করে, যে readকোনও লাইন পড়তে ব্যবহার করা যদি ভুল হয় তবে অবশ্যই অন্য কিছু থাকতে হবে be এই ভুল-ধারণাটি কী হতে পারে? বা এটি প্রথম বক্তব্যটি প্রযুক্তিগতভাবে সঠিক, তবে সত্যই অ-ভুল ধারণাটি হ'ল: "একটি লাইন থেকে শব্দগুলি পড়ার আদেশ পড়ুন Because কারণ এটি এত শক্তিশালী, আপনি এটি দ্বারা একটি ফাইলের লাইনগুলি পড়তে এটি ব্যবহার করতে পারেন: IFS= read -r line"
মাইক এস

8

তত্ত্বটি

এখানে দুটি ধারণা প্রচলিত আছে:

  • IFSইনপুট ফিল্ড বিভাজক, যার অর্থ স্ট্রিং রিডটি অক্ষরের উপর ভিত্তি করে বিভক্ত হবে IFS। একটি কমান্ড লাইনে IFSসাধারণত কোনও শ্বেতস্পেস অক্ষর থাকে, এ কারণেই কমান্ড লাইন স্পেসে বিভক্ত হয়।
  • এর মতো কিছু করার VAR=value commandঅর্থ "কমান্ডের পরিবেশটি পরিবর্তন করুন যাতে VARতার মান হবে value"। মূলত, কমান্ডটি মান হিসাবে commandদেখবে , তবে এর পরে কার্যকর হওয়া কোনও কমান্ড তার পূর্ববর্তী মান হিসাবে দেখতে পাবে । অন্য কথায়, সেই পরিবর্তনশীল কেবলমাত্র সেই বিবৃতিতে পরিবর্তিত হবে।VARvalueVAR

এক্ষেত্রে

কাজটি করার সময় IFS= read -r line, আপনি যা করছেন তা IFSএকটি খালি স্ট্রিংয়ে সেট করা হচ্ছে (কোনও অক্ষর বিভাজন হিসাবে ব্যবহৃত হবে না, সুতরাং কোনও বিভাজন ঘটবে না) যাতে readপুরো লাইনটি পড়বে এবং এটিকে একটি শব্দ হিসাবে দেখা যাবে যা lineভেরিয়েবলের জন্য বরাদ্দ করা হবে । পরিবর্তনগুলি IFSকেবলমাত্র সেই বিবৃতিটিকেই প্রভাবিত করে, যাতে নিম্নলিখিত কোনও আদেশগুলি পরিবর্তনের দ্বারা প্রভাবিত না হয়।

পার্শ্ব নোট হিসাবে

যদিও কমান্ড সঠিক এবং অভিপ্রেত হিসেবে কাজ করবে স্থাপন IFSএই ক্ষেত্রে নয় যথাসাধ্য 1 নাও হতে প্রয়োজনীয়। যেমনটি বিল্টিন বিভাগে bashম্যান পৃষ্ঠায় লেখা হয়েছে read:

একটি লাইন স্ট্যান্ডার্ড ইনপুট থেকে পড়া হয় [...] এবং প্রথম শব্দটির প্রথম নামটি দেওয়া হয়, দ্বিতীয় শব্দটির দ্বিতীয় নামটি দেওয়া হয়, এবং এই জাতীয়ভাবে বাকী শব্দ এবং তাদের অন্তর্বর্তী পৃথক পৃথক পৃথক নামের সাথে অন্তর্ভুক্ত করা হয় । যদি নামের চেয়ে ইনপুট স্ট্রিম থেকে কম শব্দ পড়ে থাকে তবে অবশিষ্ট নামগুলি খালি মান নির্ধারিত হয়। অক্ষরগুলি IFSশব্দগুলিতে রেখাটি বিভক্ত করতে ব্যবহৃত হয়। [...]

যেহেতু আপনার কেবলমাত্র lineপরিবর্তনশীল, তাই প্রতিটি শব্দই এটির জন্য নির্ধারিত হবে, সুতরাং আপনার যদি পূর্ববর্তী এবং অনুগামী শ্বেত স্পেস অক্ষর 1 এর কোনও প্রয়োজন না হয় তবে আপনি কেবল এটি লিখতে read -r lineএবং এটি দিয়ে সম্পন্ন করতে পারেন।

[1] ঠিক কীভাবে কোনও unsetডিফল্ট $IFSমান আইএফএস হোয়াইটস্পেসেরread শীর্ষস্থানীয় / অনুসরণের কারণ হিসাবে বিবেচনা করে তার উদাহরণ হিসাবে আপনি চেষ্টা করতে পারেন:

echo ' where are my spaces? ' | { 
    unset IFS
    read -r line
    printf %s\\n "$line"
} | sed -n l

এটি চালান এবং আপনি দেখতে পাবেন যে পূর্বনির্ধারিত এবং পিছনের অক্ষরগুলি IFSআনসেট না করা থাকলে বেঁচে থাকবে না । তদ্ব্যতীত, $IFSস্ক্রিপ্টের আগে কোথাও সংশোধন করা হলে কিছু অদ্ভুত জিনিস ঘটতে পারে ।


5

আপনার এই বিবৃতিটি দুটি অংশে পড়া উচিত, প্রথমটি আইএফএস ভেরিয়েবলের মান পরিষ্কার করে, অর্থাত্ আরও পঠনযোগ্যের সমতুল্য IFS="", দ্বিতীয়টি lineস্টিডিন থেকে ভেরিয়েবলটি পড়ছে read -r line

এই বাক্য গঠনতে সুনির্দিষ্ট যা আইএফএসের প্রভাবটি প্রাচীন এবং কেবলমাত্র readকমান্ডের জন্য বৈধ ।

যতক্ষণ না আমি কিছু মিস করছি, সেই নির্দিষ্ট ক্ষেত্রে ক্লিয়ারিংয়ের IFSকোনও প্রভাব নেই যদিও যা IFSকিছু সেট করা আছে, পুরো লাইনটি lineভেরিয়েবলটিতে পড়বে । readনির্দেশের ক্ষেত্রে প্যারামিটার হিসাবে একাধিক ভেরিয়েবল কেবল ক্ষেত্রেই পরিবর্তিত হত ।

সম্পাদনা:

-rইনপুট দিয়ে শেষ করার অনুমতি নেই \না, বিশেষ করে প্রক্রিয়া হওয়ার অর্থাত্ জন্য ব্যাকস্ল্যাশ অন্তর্ভুক্ত করা lineনা ধারাবাহিকতায় চরিত্র বহু-লাইন ইনপুট করার অনুমতি যেমন পরিবর্তনশীল এবং।

$ read line; echo "[$line]"   
abc\
> def
[abcdef]
$ read -r line; echo "[$line]"  
abc\
[abc\]

ক্লিয়ারিং আইএফএসের সম্ভাব্য নেতৃস্থানীয় এবং পেছনের স্থান বা ট্যাব অক্ষরগুলি ছাঁটাইতে পাঠ্য প্রতিরোধের পার্শ্ব প্রতিক্রিয়া রয়েছে, যেমন:

$ echo "   a b c   " | { IFS= read -r line; echo "[$line]" ; }   
[   a b c   ]
$ echo "   a b c   " | { read -r line; echo "[$line]" ; }     
[a b c]

পার্থক্যটি নির্দেশ করার জন্য রিচিকে ধন্যবাদ।


আপনি যা অনুপস্থিত তা হ'ল যদি আইএফএসটি পরিবর্তন না করা read -r lineহয় তবে lineভেরিয়েবলটিকে ইনপুট বরাদ্দের আগে হোয়াইটস্পেসের শীর্ষস্থানীয় এবং ট্রেলিং করবে ।
ধনী

টুইট এই সত্যটি দেখানোর জন্য ধন্যবাদ!
jlliagre

ক্লিয়ারিং আইএফএস একাধিক ভেরিয়েবল (পার্শ্ব প্রতিক্রিয়া) এর কার্যভারকেও আটকাবে। IFS= read a b <<< 'aa bb' ; echo "-$a-$b-"দেখাব-aa bb--
kyodev
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.