সর্বদা পরিবর্তনশীল বদল ও কমান্ড বদল প্রায় উদ্ধৃতি চিহ্ন ব্যবহার করুন: "$foo"
,"$(foo)"
আপনি যদি $foo
অব্যক্ত ব্যবহার করেন তবে আপনার স্ক্রিপ্টটি ইনপুট বা প্যারামিটারগুলিতে (বা কমান্ড আউটপুট, দিয়ে $(foo)
) শ্বেতস্পেস বা চিট করবে \[*?
।
সেখানে, আপনি পড়া বন্ধ করতে পারেন। ঠিক আছে, ঠিক আছে, এখানে আরও কয়েকটি রয়েছে:
read
- বিল্টিনের সাথে read
সামঞ্জস্য রেখে ইনপুট লাইনটি পড়ার জন্য ,while IFS= read -r line; do …
বিশেষভাবে
সমতল read
আচরণের ব্যাকস্ল্যাশ এবং হোয়াইটস্পেস ব্যবহার করুন ।
xargs
- এড়ানোxargs
। আপনি যদি অবশ্যই ব্যবহার করেন তবে xargs
এটি তৈরি করুন xargs -0
। পরিবর্তে find … | xargs
, পছন্দfind … -exec …
।
xargs
শ্বেতস্থান এবং চরিত্রগুলি \"'
বিশেষভাবে বিবেচনা করে।
এই উত্তরটি বোর্ন / POSIX ধাঁচের শাঁস (প্রযোজ্য sh
, ash
, dash
, bash
, ksh
, mksh
, yash
...)। Zsh ব্যবহারকারীদের এটি এড়িয়ে যাওয়া উচিত এবং ডাবল-কোটিংয়ের প্রয়োজনীয়তা কখন শেষ করা উচিত ? পরিবর্তে. আপনি যদি পুরো ব্যস্ততা চান, মান বা আপনার শেলের ম্যানুয়ালটি পড়ুন।
নোট করুন যে নীচের ব্যাখ্যায় কয়েকটি আনুমানিকতা রয়েছে (বিবৃতি যা বেশিরভাগ ক্ষেত্রে সত্য তবে পার্শ্ববর্তী প্রসঙ্গে বা কনফিগারেশন দ্বারা প্রভাবিত হতে পারে)।
আমার লেখার দরকার কেন "$foo"
? উদ্ধৃতি ছাড়া কি হয়?
$foo
"ভেরিয়েবলের মান গ্রহণ করুন" এর অর্থ এই নয় foo
। এর অর্থ আরও জটিল কিছু:
- প্রথমে ভেরিয়েবলের মান নিন।
- ক্ষেত্র বিভাজন: সেই মানটিকে একটি সাদা ক্ষেত্র-বিচ্ছিন্ন ক্ষেত্রের তালিকা হিসাবে বিবেচনা করুন এবং ফলাফলটি তৈরি করুন। উদাহরণস্বরূপ, পরিবর্তনশীল থাকে
foo * bar
তাহলে এই পদক্ষেপ ফল 3-উপাদান তালিকা foo
, *
, bar
।
- ফাইলের নাম জেনারেশন: প্রতিটি ক্ষেত্রকে একটি গ্লোব হিসাবে বিবেচনা করুন, যেমন একটি ওয়াইল্ডকার্ড প্যাটার্ন হিসাবে, এবং এই প্যাটার্নের সাথে মেলে এমন ফাইলের নামের সাথে এটি প্রতিস্থাপন করুন। যদি প্যাটার্নটি কোনও ফাইলের সাথে মেলে না, তবে এটি অশোধিত ছেড়ে দেওয়া হবে। আমাদের উদাহরণস্বরূপ,
foo
বর্তমান ডিরেক্টরিতে ফাইলগুলির তালিকা অনুসরণ করে এবং শেষ পর্যন্ত এই ফলাফলের ফলাফল রয়েছে bar
। তাহলে বর্তমান ডিরেক্টরী খালি থাকে, ফল foo
, *
, bar
।
নোট করুন যে ফলাফলটি স্ট্রিংগুলির একটি তালিকা। শেল সিনট্যাক্সে দুটি কনটেক্সট রয়েছে: তালিকা প্রসঙ্গে এবং স্ট্রিং প্রসঙ্গে। ফিল্ড বিভাজন এবং ফাইলের নাম উত্পাদন কেবল তালিকা প্রসঙ্গে হয়, তবে এটি বেশিরভাগ সময়। ডাবল উদ্ধৃতিগুলি একটি স্ট্রিং প্রসঙ্গে ডিলিট করে: পুরো ডাবল-কোটেড স্ট্রিংটি একটি একক স্ট্রিং, বিভক্ত হতে হবে না। (ব্যতিক্রম: "$@"
অবস্থানগত প্যারামিটারগুলির তালিকায় প্রসারিত করার জন্য, যেমন তিনটি অবস্থানগত পরামিতি রয়েছে "$@"
সমান "$1" "$2" "$3"
See দেখুন * and * এবং $ @ এর মধ্যে পার্থক্য কী? )
একই সাথে $(foo)
বা এর সাথে বিকল্পের আদেশের ক্ষেত্রেও ঘটে `foo`
। সাইড নোটে, ব্যবহার করবেন না `foo`
: এর উদ্ধৃতি বিধিগুলি অদ্ভুত এবং অ-বহনযোগ্য এবং সমস্ত আধুনিক শেল সমর্থন করে $(foo)
যা স্বজ্ঞাত উদ্ধৃতি বিধি ব্যতীত একেবারে সমতুল্য।
পাটিগণিত প্রতিস্থাপনের আউটপুটও একই বিস্তৃতি বহন করে, তবে এটি সাধারণত উদ্বেগের বিষয় নয় কারণ এতে কেবল অ-প্রসারণযোগ্য অক্ষর রয়েছে (ধরে নেওয়া যায় IFS
যে সংখ্যাগুলি নেই বা রয়েছে -
)।
দেখুন কখন ডাবল-কোটিং দরকার? মামলাগুলি সম্পর্কে আরও বিশদের জন্য যখন আপনি উদ্ধৃতিগুলি ছাড়তে পারেন।
যদি না আপনি এই সমস্ত প্রতিবন্ধকতা ঘটানোর জন্য বোঝাতে চান তবে কেবল ভেরিয়েবল এবং কমান্ড বিকল্পের পরিবর্তে সর্বদা ডাবল উদ্ধৃতি ব্যবহার করা মনে রাখবেন। যত্ন নিন: উদ্ধৃতিগুলি না রেখে কেবল ত্রুটিই নয় সুরক্ষা গর্তের দিকেও যেতে পারে ।
আমি কীভাবে ফাইলের নামের একটি তালিকা প্রক্রিয়া করব?
আপনি যদি myfiles="file1 file2"
ফাইলগুলি পৃথক করার জন্য স্পেস দিয়ে লিখেন তবে এটি ফাঁকা থাকা ফাইলের নামের সাথে কাজ করতে পারে না। ইউনিক্স ফাইলের নামগুলিতে /
(যা সর্বদা ডিরেক্টরি বিভাজক) এবং নাল বাইট (যা আপনি বেশিরভাগ শেল সহ শেল স্ক্রিপ্টগুলিতে ব্যবহার করতে পারবেন না) ব্যতীত অন্য কোনও অক্ষর ধারণ করতে পারে ।
একই সমস্যা myfiles=*.txt; … process $myfiles
। আপনি যখন এটি করেন, ভেরিয়েবলটিতে myfiles
5-অক্ষরের স্ট্রিং থাকে *.txt
এবং আপনি যখন লিখবেন $myfiles
যে ওয়াইল্ডকার্ডটি প্রসারিত। আপনি নিজের স্ক্রিপ্টটিকে পরিবর্তন না করা পর্যন্ত এই উদাহরণটি আসলে কাজ করবে myfiles="$someprefix*.txt"; … process $myfiles
। যদি someprefix
সেট করা থাকে তবে এটি final report
কাজ করবে না।
যে কোনও ধরণের তালিকার প্রক্রিয়া করতে (যেমন ফাইলের নাম), এটি অ্যারেতে রাখুন। এর জন্য mksh, ksh93, যশ বা বাশ (বা zsh, যা এই সমস্ত উদ্ধৃতি সংক্রান্ত সমস্যাগুলির মধ্যে নেই) প্রয়োজন; একটি সরল POSIX শেল (যেমন ছাই বা ড্যাশ) এর অ্যারে ভেরিয়েবলগুলি নেই।
myfiles=("$someprefix"*.txt)
process "${myfiles[@]}"
Ksh88 এর বিভিন্ন অ্যাসাইনমেন্ট সিনট্যাক্স সহ অ্যারে ভেরিয়েবল রয়েছে set -A myfiles "someprefix"*.txt
( আপনার যদি ksh88 / ব্যাশ বহনযোগ্যতার প্রয়োজন হয় তবে বিভিন্ন ksh পরিবেশের অধীনে অ্যাসাইনমেন্ট ভেরিয়েবল দেখুন )। বোর্ন / পসআইএক্স-স্টাইল শেলগুলির একটি একক অ্যারে থাকে, আপনি যে পজিশনাল প্যারামিটারগুলির সাথে অ্যারে "$@"
সেট করেন set
এবং যা কোনও ফাংশনে লোকাল তা:
set -- "$someprefix"*.txt
process -- "$@"
ফাইল নামগুলির সাথে -
কী শুরু হয় ?
সম্পর্কিত নোটে, মনে রাখবেন যে ফাইলের নামগুলি একটি -
(ড্যাশ / বিয়োগ) দিয়ে শুরু হতে পারে , যা বেশিরভাগ কমান্ড একটি বিকল্প চিহ্নিতকরণ হিসাবে ব্যাখ্যা করে। আপনার যদি কোনও ফাইলের নাম থাকে যা ভেরিয়েবল অংশ দিয়ে শুরু --
হয় তবে উপরের স্নিপেটের মতো এটির আগে অবশ্যই পাস করবেন তা নিশ্চিত হন । এটি কমান্ডকে নির্দেশ করে যে এটি বিকল্পগুলির শেষে পৌঁছেছে, সুতরাং এর পরে যে কোনও কিছুই ফাইলের নাম হলেও এটি শুরু হয় -
।
বিকল্পভাবে, আপনি নিশ্চিত করতে পারেন যে আপনার ফাইলের নামগুলি ব্যতীত অন্য একটি অক্ষর দিয়ে শুরু হয় -
। সম্পূর্ণ ফাইলের নামগুলি শুরু হয় /
এবং আপনি ./
আপেক্ষিক নামের শুরুতে যুক্ত করতে পারেন । নীচের স্নিপেটটি ভেরিয়েবলের সামগ্রীটি f
একই ফাইলটিতে পুনরায় উল্লেখ করার "নিরাপদ" উপায়ে রূপান্তরিত করে যা শুরু না করার গ্যারান্টিযুক্ত -
।
case "$f" in -*) "f=./$f";; esac
এই বিষয়টির একটি চূড়ান্ত নোটে, সাবধান থাকুন যে কোনও কোনও কমান্ড -
স্ট্যান্ডার্ড ইনপুট বা স্ট্যান্ডার্ড আউটপুট এমনকি পরে বোঝায় --
। যদি আপনাকে নামের কোনও আসল ফাইলটি উল্লেখ করতে হয় -
বা আপনি যদি এই জাতীয় প্রোগ্রামটি কল করে থাকেন এবং আপনি এটি স্টিডিন থেকে পড়তে বা স্টডআউটে লিখতে না চান তবে -
উপরের মত পুনরায় লেখার বিষয়টি নিশ্চিত করুন । দেখুন "du -sh *" এবং "du -sh ./*" এর মধ্যে পার্থক্য কী? আরও আলোচনার জন্য।
আমি একটি চলকটিতে একটি কমান্ড কীভাবে সংরক্ষণ করব?
"কমান্ড" এর অর্থ তিনটি জিনিস হতে পারে: একটি কমান্ডের নাম (একটি এক্সিকিউটেবলের নাম, পুরো পাথ সহ বা ছাড়াই বা কোনও ফাংশনের নাম, বিল্টিন বা উপনাম), আর্গুমেন্ট সহ একটি কমান্ডের নাম, বা শেল কোডের একটি অংশ। সেগুলি ভেরিয়েবলের মধ্যে সঞ্চয় করার বিভিন্ন উপায় রয়েছে।
আপনার যদি কমান্ডের নাম থাকে তবে কেবল এটি সংরক্ষণ করুন এবং যথারীতি ডাবল উদ্ধৃতি সহ ভেরিয়েবলটি ব্যবহার করুন।
command_path="$1"
…
"$command_path" --option --message="hello world"
আপনার যদি আর্গুমেন্ট সহ একটি কমান্ড থাকে, সমস্যাটি উপরের ফাইল নামের তালিকার মতোই: এটি স্ট্রিংগুলির তালিকা নয়, স্ট্রিং নয়। আপনি কেবলমাত্র একক স্ট্রিংয়ের মধ্যে ফাঁক দিয়ে আর্গুমেন্টগুলি স্টাফ করতে পারবেন না, কারণ আপনি যদি এটি করেন তবে আপনি যুক্তিগুলির অংশ এবং ফাঁকা স্থানগুলির মধ্যে পৃথক যুক্তিগুলির স্পেসের মধ্যে পার্থক্য বলতে পারবেন না। যদি আপনার শেলটিতে অ্যারে থাকে তবে আপনি সেগুলি ব্যবহার করতে পারেন।
cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"
আপনি যদি অ্যারে ছাড়াই শেল ব্যবহার করছেন? আপনি এখনও অবস্থানগত প্যারামিটারগুলি ব্যবহার করতে পারেন, যদি সেগুলি সংশোধন করতে আপনার আপত্তি না থাকে।
set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"
আপনার যদি কোনও জটিল শেল কমান্ড, যেমন পুনঃনির্দেশ, পাইপ ইত্যাদির সাথে সঞ্চয় করতে হয়? বা আপনি যদি অবস্থানগত পরামিতিগুলি পরিবর্তন করতে চান না? তারপরে আপনি কমান্ড যুক্ত একটি স্ট্রিং তৈরি করতে পারেন এবং eval
বিল্টিন ব্যবহার করতে পারেন ।
code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"
সংজ্ঞায় নেস্টেড কোটগুলি নোট করুন code
: একক উদ্ধৃতিগুলি '…'
একটি স্ট্রিং আক্ষরিকভাবে সীমিত করে দেয়, যাতে ভেরিয়েবলের মান code
স্ট্রিং হয় /path/to/executable --option --message="hello world" -- /path/to/file1
। eval
Builtin শেল বলে স্ট্রিং হিসেবে যদি এটা স্ক্রিপ্ট হাজির একটি আর্গুমেন্ট হিসাবে পাস বিশ্লেষণ করতে, যাতে সময়ে কোট এবং পাইপ পার্স করা হয়, ইত্যাদি
ব্যবহার eval
করা কঠিন। কখন পার্স করা যায় সে সম্পর্কে সাবধানতার সাথে চিন্তা করুন। বিশেষত, আপনি কেবল কোডে কোনও ফাইলের নাম স্টাফ করতে পারবেন না: আপনার যেমনটি উত্স কোড ফাইলে থাকত তেমনই আপনাকে এটিকে উদ্ধৃত করতে হবে। এটি করার সরাসরি কোনও উপায় নেই। ভালো কিছু code="$code $filename"
বিরতি যদি ফাইল নামের কোনো শেল বিশেষ অক্ষর আছে (স্পেস, $
, ;
, |
, <
, >
, ইত্যাদি)। code="$code \"$filename\""
এখনও বিরতি "$\`
। এমনকি code="$code '$filename'"
বিরতি যদি ফাইল নামের একটি রয়েছে '
। দুটি সমাধান আছে।
ফাইলের নামের চারপাশে উদ্ধৃতিগুলির একটি স্তর যুক্ত করুন। এটি করার সবচেয়ে সহজ উপায় হল এর চারপাশে একক উদ্ধৃতি যুক্ত করা এবং একক উদ্ধৃতি দ্বারা প্রতিস্থাপন করা '\''
।
quoted_filename=$(printf %s. "$filename" | sed "s/'/'\\\\''/g")
code="$code '${quoted_filename%.}'"
কোডের অভ্যন্তরে ভেরিয়েবল প্রসারিত রাখুন, যাতে কোডটি মূল্যায়ন করার সময় সন্ধান করা হয়, কোড খণ্ডটি নির্মিত না হয়ে নয়। এটি সহজ তবে কেবল তখনই কার্যকর হয় যখন কোডটি কার্যকর করার সময় ভেরিয়েবল একই মান সহ প্রায় থাকে তবে উদাহরণস্বরূপ নয় কোডটি কোনও লুপে নির্মিত কিনা।
code="$code \"\$filename\""
শেষ অবধি, আপনার কি সত্যিকারের কোডযুক্ত একটি ভেরিয়েবলের প্রয়োজন? কোড ব্লকে নাম দেওয়ার সর্বাধিক প্রাকৃতিক উপায় হ'ল কোনও ফাংশন সংজ্ঞায়িত করা।
কি হয়েছে read
?
ছাড়া -r
, read
ধারাবাহিকতা রেখাগুলি অনুমতি দেয় - এটি ইনপুটটির একক লজিকাল লাইন:
hello \
world
read
ইনপুট লাইনটি অক্ষর দ্বারা সীমানাযুক্ত ক্ষেত্রগুলিতে বিভক্ত করে $IFS
(ব্যতীত -r
, ব্যাকস্ল্যাশগুলিও সেগুলি এড়িয়ে যায়)। উদাহরণস্বরূপ, যদি ইনপুটটি তিনটি শব্দের সমন্বিত একটি লাইন হয়, তবে read first second third
ইনপুটটির first
প্রথম শব্দের সাথে, second
দ্বিতীয় শব্দের সাথে এবং third
তৃতীয় শব্দের সাথে সেট করে। যদি আরও শব্দ থাকে তবে শেষের ভেরিয়েবলটিতে পূর্বেরগুলি সেট করার পরে বাকি সমস্ত কিছু রয়েছে। শীর্ষস্থানীয় এবং পিছনের সাদা অংশ ছাঁটাই করা হয়।
IFS
খালি স্ট্রিংয়ে সেট করা কোনও ছাঁটাই এড়ায়। দেখুন কেন `যখন IFS = read` তাই প্রায়ই, এর পরিবর্তে 'এর IFS = ব্যবহার করা হয়; পড়ার সময়..`? একটি দীর্ঘ ব্যাখ্যার জন্য।
এর সাথে কী হয়েছে xargs
?
এর ইনপুট ফর্ম্যাটটি xargs
হোয়াইটস্পেস-বিচ্ছিন্ন স্ট্রিং যা allyচ্ছিকভাবে একক- বা ডাবল-কোটেড হতে পারে। কোনও মানক সরঞ্জাম এই ফর্ম্যাটটিকে আউটপুট দেয় না।
ইনপুট xargs -L1
বা xargs -l
প্রায় লাইনের একটি তালিকা বেশ, কিন্তু না - একটি লাইন শেষে ব্যবধান আছে তা হলে, নিম্নলিখিত লাইন ধারাবাহিকতায় লাইন।
xargs -0
প্রযোজ্য যেখানে আপনি ব্যবহার করতে পারেন (এবং যেখানে উপলব্ধ: জিএনইউ (লিনাক্স, সাইগউইন), ব্যাসিবক্স, বিএসডি, ওএসএক্স, তবে এটি পসিক্সে নেই)। এটি নিরাপদ, কারণ নাল বাইটগুলি বেশিরভাগ ডেটাতে দেখা যায় না, বিশেষত ফাইলের নামগুলিতে। ফাইলের নামের একটি নাল দ্বারা পৃথক করা তালিকা তৈরি find … -print0
করতে ব্যবহার করুন (বা আপনি find … -exec …
নীচে বর্ণিত হিসাবে ব্যবহার করতে পারেন )।
আমি ফাইলগুলি কীভাবে প্রক্রিয়াকরণ করব find
?
find … -exec some_command a_parameter another_parameter {} +
some_command
একটি বাহ্যিক কমান্ড হওয়া দরকার, এটি শেল ফাংশন বা ওরফে হতে পারে না। ফাইলগুলি প্রক্রিয়া করার জন্য যদি আপনাকে শেলটি চাওয়ার প্রয়োজন হয় তবে sh
স্পষ্টভাবে কল করুন ।
find … -exec sh -c '
for x do
… # process the file "$x"
done
' find-sh {} +
আমার আরও কিছু প্রশ্ন আছে
এই সাইটে বা শেল বা শেল-স্ক্রিপ্টে উদ্ধৃতি ট্যাগ ব্রাউজ করুন । (কিছু সাধারণ টিপস এবং সাধারণ প্রশ্নগুলির একটি হাতে-নির্বাচিত তালিকা দেখতে "আরও শিখুন ..." এ ক্লিক করুন you've) আপনি যদি অনুসন্ধান করেছেন এবং উত্তর খুঁজে না পেয়ে থাকেন, তবে জিজ্ঞাসা করুন ।
shellcheck
আপনার প্রোগ্রামের মান উন্নত করতে আপনাকে সহায়তা করে।