_gem_dec() { shift $# ; . /dev/fd/3
} 3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
for func in guard rspec rake ; do _gem_dec $func ; done
echo "_guard ; _rspec ; _rake are all functions now."
উপরের উইল . source /dev/fd/3যা _gem_dec()ফাংশনে প্রতিবার খাওয়ানো হয় here-document. _gem_dec'sকেবল প্রাক-মূল্যায়নকৃত কাজ হিসাবে এটি একটি প্যারামিটার গ্রহণ করা হয় এবং এটি bundle execলক্ষ্য হিসাবে এবং এটি যে ফাংশনটিতে লক্ষ্যবস্তু হয়েছিল তার নাম হিসাবে উভয়ই পূর্ব-মূল্যায়ন করে ।
NOTE: . sourcing shell expansions results in twice-evaluated variables - just like eval. It can be risky.
উপরের ক্ষেত্রে যদিও, আমি মনে করি না যে কোনও ঝুঁকি থাকতে পারে।
উপরের কোড-ব্লক একটি অনুলিপি করা হয়, তাহলে .bashrcফাইল না শুধুমাত্র শেল ফাংশন হবে _guard(), _rspec()এবং_rake() লগইন এ ঘোষণা, কিন্তু _gem_dec()ফাংশন এছাড়াও আপনার শেল যে কোনও সময়ে সঞ্চালনের জন্য উপলব্ধ করা হবে প্রম্পট (বা অন্যভাবে) এবং তাই নতুন টেমপ্লেট করা ফাংশন করতে পারেন আপনি যখনই ন্যায়সঙ্গতভাবে পছন্দ করবেন ঘোষণা করুন:
_gem_dec $new_templated_function_name
এবং আমাকে দেখানোর জন্য @ অ্যান্ড্রুকে ধন্যবাদ যে এগুলি খাওয়া হবে না for loop.
কিন্তু কিভাবে?
আমি 3উপরের ফাইল বর্ণনাকারীটিকে stdin, stdout, and stderr, or <&0 >&1 >&2অভ্যাসের বাইরে রাখার জন্য ব্যবহার করি - যদিও, আমি এখানে প্রয়োগ করা অন্যান্য পূর্বনির্ধারিত কয়েকটি সাবধানতার জন্য যেমন হয় - কারণ ফলস্বরূপ ফাংশনটি এত সহজ, এটি সত্যই প্রয়োজনীয় নয়। এটি ভাল অনুশীলন, যদিও। কল shift $#করা সেই অপ্রয়োজনীয় সাবধানতাগুলির মধ্যে একটি।
এখনও, যখন একটি ফাইল হিসাবে নির্দিষ্ট করা <inputবা>output সঙ্গে [optional num]<fileবা [optional num]>fileফেরৎ কার্নেল একটি ফাইল বর্ণনাকারী মাধ্যমে অ্যাক্সেস করা যেতে পারে যা এটি সার্চ character deviceবিশেষ ফাইল /dev/fd/[0-9]*। যদি [optional num]নির্দিষ্টকারীটি বাদ দেওয়া হয় তবে তারপরে 0<fileইনপুট এবং 1>fileআউটপুট গ্রহণ করা হয়। এই বিবেচনা:
l='line %d\n' ; printf "$l" 1 2 3 4 5 6 >/dev/fd/1
> line 1
> line 2
> line 3
> line 4
> line 5
> line 6
( printf "$l" 4 5 6 >/dev/fd/3 ; printf "$l" 1 2 3 ) >/tmp/sample 3>/tmp/sample2
( cat /tmp/sample2 ) </tmp/sample
> line 4
> line 5
> line 6
( cat /dev/fd/0 ) </tmp/sample
> line 1
> line 2
> line 3
( cat /dev/fd/3 ) </tmp/sample 3</tmp/sample2
> line 4
> line 5
> line 6
এবং কারণ here-documentযখন আমরা একটি কোড-ব্লকের মধ্যে কোনও ফাইলের ইনলাইন বর্ণনা করার কেবলমাত্র একটি মাধ্যম:
<<'HEREDOC'
[$CODE]
HEREDOC
আমরা পাশাপাশি করতে পারে:
echo '[$CODE]' >/dev/fd/0
একটি খুব গুরুত্বপূর্ণ পার্থক্য সঙ্গে। আপনি যদি এটির একটি না "'\quote'"করেন তবে শেলটি শেলের মতো এটির জন্য মূল্যায়ন করবে :<<"'\LIMITER"'here-document$expansion
echo "[$CODE]" >/dev/fd/0
সুতরাং, জন্য _gem_dec(),3<<-FUNC here-document ইনপুট একটি ফাইল, একই যেমন যদি এটা ছিল হবে যেমন মূল্যায়ন করা হয় 3<~/some.file ব্যতীত যে কারণ আমরা চলে FUNCসীমা কোট মুক্ত, এটা প্রথম মূল্যায়ন করা হয় $expansion.এই সম্পর্কে গুরুত্বপূর্ণ বিষয় এটি ইনপুট, অর্থ হয় এটি কেবলমাত্র এর জন্য বিদ্যমান _gem_dec(),তবে _gem_dec()ফাংশনটি চালানোর $expansionsআগে এটির মূল্যায়নও করা হয় কারণ আমাদের শেলটি ইনপুট হিসাবে বন্ধ করার আগে এটি পড়তে এবং মূল্যায়ন করতে হয়।
guard,উদাহরণস্বরূপ করা যাক :
_gem_dec guard
সুতরাং প্রথমে শেলটি ইনপুটটি পরিচালনা করতে হবে যার অর্থ পঠন:
3<<-FUNC
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
FUNC
ফাইল বর্ণনাকারী 3 এ প্রবেশ করুন এবং শেল বিস্তারের জন্য এটি মূল্যায়ন করছে। যদি আপনি এই সময় দৌড়েছিলেন:
cat /dev/fd/3
বা:
cat <&3
যেহেতু তারা উভয়ই সমান কমান্ড হ'ল আপনি দেখতে পাচ্ছেন *:
_guard() { [ ! -e 'Gemfile' ] && {
command guard "$@" ; return $?
} || bundle exec guard "$@"
}
... ফাংশনের কোনও কোড আগে কার্যকর করা যায়। এটি সব পরে <input, ফাংশন এর । আরও উদাহরণের জন্য আমার এখানে একটি পৃথক প্রশ্নের উত্তর দেখুন ।
(* প্রযুক্তিগতভাবে এটি পুরোপুরি সত্য নয়। কারণ আমি প্রথমটির -dashআগে একটি শীর্ষস্থানীয় ব্যবহার করি here-doc limiter, উপরের সমস্তটি বাম-ন্যায়সঙ্গত হবে But তবে আমি প্রথমটি পাঠযোগ্যতার জন্য -dashএতটা ব্যবহার করেছি যাতে আমি <tab-insert>এর <tab-inserts>আগে ফেলা যাব না not এটি পড়ার জন্য আপনাকে অফার ...)
এ সম্পর্কে সর্বোত্তম অংশটি উদ্ধৃতি - লক্ষ্য করুন যে '"উদ্ধৃতিগুলি রয়ে গেছে এবং কেবল \উদ্ধৃতিগুলি কেড়ে নেওয়া হয়েছিল। এটি সম্ভবত অন্য যে কোনও কারণের চেয়ে বেশি কারণ আপনি যদি একটি শেলকে দুবার মূল্যায়ন করতে হয় তবে $expansionআমি সুপারিশ করব here-documentকারণ উদ্ধৃতিগুলির চেয়ে অনেক সহজ eval।
যাইহোক, এখন উপরের কোডটি ঠিক তেমন একটি ফাইল খাওয়ানো ঠিক যেমন ফাংশনটির 3<~/heredoc.fileজন্য অপেক্ষা করার অপেক্ষা রাখে _gem_dec()এবং তার ইনপুটটি গ্রহণ করে /dev/fd/3।
সুতরাং আমরা যখন _gem_dec()প্রথম কাজটি শুরু করি তখন সমস্ত অবস্থানগত পরামিতি টস করা হয়, কারণ আমাদের পরবর্তী পদক্ষেপটি দ্বিগুণ মূল্যায়নকৃত শেল প্রসার এবং আমি চাই না যে থাকা কোনওটিই $expansionsআমার বর্তমান $1 $2 $3...পরামিতিগুলির মতো ব্যাখ্যা করা হোক । তাই আমি:
shift $#
shiftpositional parametersআপনি নির্দিষ্ট হিসাবে যতগুলি বাতিল এবং $1যা থেকে শুরু হয় তা থেকে শুরু করে । সুতরাং যদি আমি নামক _gem_dec one two threeপ্রম্পটে _gem_dec's $1 $2 $3অবস্থানগত পরামিতি হবে one two threeএবং মোট বর্তমান অবস্থানগত গণনা, বা $#3. হবে আমি তখন বলা যদি shift 2,মান oneএবংtwo হবে shifted মান দূরে, $1পরিবর্তন করবে threeএবং $#প্রসারিত হবে 1. সুতরাং shift $#শুধু তাদের সব ফেলে দেয়। এটি করা কঠোরভাবে সতর্কতামূলক এবং কিছুক্ষণের জন্য এই ধরণের কাজ করার পরে আমি বিকাশ করেছি habit (subshell)স্পষ্টতার স্বার্থে এটি এখানে কিছুটা ছড়িয়ে পড়েছে:
( set -- one two three ; echo "$1 $2 $3" ; echo $# )
> one two three
> 3
( set -- one two three ; shift 2 ; echo "$1 $2 $3" ; echo $# )
> three
> 1
( set -- one two three ; shift $# ; echo "$1 $2 $3" ; echo $# )
>
> 0
যাইহোক, পরবর্তী পদক্ষেপটি যেখানে যাদুটি ঘটে। আপনি যদি . ~/some.shশেল প্রম্পটে থাকেন তবে ঘোষণা করা সমস্ত ফাংশন এবং পরিবেশের ভেরিয়েবলগুলি ~/some.shআপনার শেল প্রম্পটে কলযোগ্য হবে। একই সত্য এখানে হল, আমরা ছাড়া আমাদের ফাইলের বর্ণনাকারী জন্য বিশেষ ফাইল, বা - যা যেখানে আমাদের ইন-লাইন ফাইল pathed করা হয়েছে - এবং আমরা আমাদের ফাংশন ঘোষণা করেছি। এবং এটি কিভাবে এটি কাজ করে।. sourcecharacter device. /dev/fd/3here-document
_guard
আপনার _guardফাংশনটি করার কথা এখনই তা করে ।
সংযোজন:
আপনার অবস্থানগুলি সংরক্ষণ করুন বলার দুর্দান্ত উপায়:
f() { . /dev/fd/3
} 3<<-ARGS
args='${args:-"$@"}'
ARGS
সম্পাদনা করুন:
আমি যখন প্রথম এই প্রশ্নের উত্তর দিয়েছি তখন আমি function()অন্যান্য $ENVশল্য ক্রিয়াকলাপের বিষয়ে সক্ষম শেল ঘোষণার সমস্যার দিকে আরও বেশি মনোযোগ দিয়েছিলাম যা বর্তমান শেল আইরনমেন্টে অবিচল থাকবে এবং জিজ্ঞাসাবাদীর অবিচ্ছিন্ন ক্রিয়াকলাপগুলির সাথে আমি কী করব তার চেয়ে বেশি করেছিলাম। সেই থেকে আমি বুঝতে পেরেছি যে আমার মূলত লাভযুক্ত সমাধান যা 3<<-FUNCফর্মটি নিয়েছিল:
3<<-FUNC
_${1}() {
if [ -e 'Gemfile' ]; then
bundle exec $1 "\$@"
else
command _${1} "\$@"
}
FUNC
সম্ভাবনা প্রশ্নকর্তা জন্য প্রত্যাশিত কারণ আমি বিশেষভাবে থেকে ঘোষণামূলক ফাংশনের নাম পরিবর্তন কাজ হতো না $1করতে _${1}, যা যদি মত নামক _gem_dec guardউদাহরণস্বরূপ, স্থাপিত হবে _gem_decনামে একটি ফাংশন ঘোষণা _guardশুধু বিরোধিতা guard।
দ্রষ্টব্য: এই জাতীয় আচরণ আমার পক্ষে অভ্যাসের বিষয় - আমি সাধারণত এই ধারণার উপর পরিচালনা করি যে শেল ফাংশনগুলি কেবল শেলযথাযথভাবে_namespaceতাদের অনুপ্রবেশ এড়ানোর জন্য কেবল তাদের নিজস্বদখল করা উচিত।namespacecommands
এটি কোনও সর্বজনীন অভ্যাস নয়, যদিও অনুরোধ করার জন্য commandজিজ্ঞাসাবাদীর ব্যবহারে এটি স্পষ্টভাবে প্রমাণিত $1।
আরও পরীক্ষা আমাকে নিম্নলিখিত বিশ্বাস করতে পরিচালিত করে:
প্রশ্নকারী শেল ফাংশন চায় যার নাম দেওয়া হয় guard, rspec, or rake, যখন ডাকা হয়, ফাইলটি rubyএকই নামের একটি ফাংশন নতুনভাবে সংকলন করবে বাif ফাইলটি Gemfileউপস্থিত নেই, শেল ফাংশনটি একই নামের ফাংশনটি সম্পাদন করবে ।$PATH
if Gemfileruby
এই পূর্বে কাজ হতো না কারণ আমি রদবদল $1উপর ডাকা commandপড়তে:
command _${1}
যার ফলে rubyশেল ফাংশনটি এইভাবে সংকলিত ফাংশনটি সম্পাদন করতে পারে না :
bundle exec $1
আমি আশা করি আপনি দেখতে পাচ্ছেন (শেষ পর্যন্ত আমি যেমন করেছিলাম) মনে হয় যে প্রশ্নকর্তা কেবল commandপরোক্ষভাবে নির্দিষ্ট করার জন্য ব্যবহার করছেন namespaceকারণ একই নামের শেল commandফাংশনটিতে একটি এক্সিকিউটেবল ফাইলকে কল করতে পছন্দ করবেন $PATH।
যদি আমার বিশ্লেষণটি সঠিক হয় (যেমনটি আমি আশা করি যে প্রশ্নকারী নিশ্চিত করবেন) তবে এটি:
_${1}() { [ ! -e 'Gemfile' ] && {
command $1 "\$@" ; return \$?
} || bundle exec $1 "\$@"
}
এই শর্তগুলি আরও ভালভাবে ব্যতিক্রম সহকারে সন্তুষ্ট করা উচিত যে guardপ্রম্পটে কল করা কেবলমাত্র$PATH নামমাত্র একটি এক্সিকিউটেবল ফাইল সম্পাদন করার চেষ্টা করবে guardযেখানে _guardপ্রম্পটে কল করা Gemfile'sঅস্তিত্বের জন্য যাচাই করবে এবং তদনুসারে সংকলন করবে বা এক্সিকিউটেবলকে guardসম্পাদন করবে $PATH। এইভাবে namespaceসুরক্ষিত এবং কমপক্ষে আমি এটি উপলব্ধি করেই, জিজ্ঞাসকের উদ্দেশ্য এখনও পূরণ হয়েছে।
আসলে, আমাদের শেল ফাংশন সাহসী _${1}()এবং এক্সিকিউটেবল ${PATH}/${1}হয় শুধুমাত্র দুটি উপায়ে আমাদের শেল একটি কল অর্থ বলতে পারল পারেন $1বা _${1}তারপর ব্যবহার commandএ সব ফাংশন মধ্যে এখন সম্পূর্ণরূপে অপ্রয়োজনীয় তৈরি করা হয়। তবুও, আমি এটিকে একইভাবে দু'বার ... একইভাবে দু'বার ভুল করতে পছন্দ করি না বলেই রেখে দিয়েছি।
যদি এটি প্রশ্নকারীর কাছে গ্রহণযোগ্য না হয় এবং তিনি _তার পুরো রূপটি সম্পূর্ণরূপে অগ্রাহ্য করতে পছন্দ করেন তবে তার বর্তমান আকারে, আউটটি সম্পাদনা _underscoreকরা সমস্ত অনুরোধকারীকে তার প্রয়োজনীয়তাগুলি পূরণ করার জন্য করা উচিত যেমন আমি তাদের বুঝতে পারি।
এই পরিবর্তনটি বাদ দিয়ে আমি মূল সিনট্যাক্সের চেয়ে শর্ট-সার্কিট শর্তাবলী ব্যবহার করতে &&এবং / অথবা|| শেল ব্যবহার করতে ফাংশনটি সম্পাদনা করেছি । এইভাবে বিবৃতিটি যদি না থাকে তবে কেবলমাত্র মূল্যায়ন করা হয় । ইভেন্টে স্টেটমেন্টটি চালু না রয়েছে তা নিশ্চিত করার জন্য এই সংশোধনটির সংযোজন প্রয়োজন তবে ফাংশনটি 0 ব্যতীত অন্য কোনও কিছু প্রদান করে ।if/thencommandGemfile$PATHreturn $?bundleGemfileruby $1
সর্বশেষে, আমার নোট করা উচিত যে এই সমাধানটি কেবল পোর্টেবল শেল কনস্ট্রাক্টসগুলি প্রয়োগ করে। অন্য কথায়, এটি পসিক্স সামঞ্জস্যতার দাবি করে যে কোনও শেলের ক্ষেত্রে অভিন্ন ফলাফল তৈরি করবে। যদিও এটা হবে, অবশ্যই, আজেবাজে কথা আমাকে দাবি করার জন্য যে POSIX সামঞ্জস্যপূর্ণ সিস্টেম হ্যান্ডেল করতে হবে ruby bundleডিরেক্টিভের, অন্তত শেল শর্তগুলো কলিং উপর এটা কিনা কলিং শেল হল নির্বিশেষে একই আচরণ করা উচিত shবা dash। এছাড়াও আশানুরূপ উপরে ইচ্ছা কাজ (অন্তত অর্ধেক-বিবেকী এ সাহসী shoptsযাহাই হউক না কেন) উভয় bashএবং zsh।
for loop?আমার অর্থ দ্বারা খাওয়া হচ্ছে না ,for loopসাধারণত ঘোষিত ভেরিয়েবলগুলি অদৃশ্য হয়ে যায় - আমি একই কারণে একই কার্যকারিতা আশা করব।