রাষ্ট্রীয় বাশ ফাংশন


16

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

এখানে আমার প্রচেষ্টা:

PS_COUNT=0

ps_count_inc() {
    let PS_COUNT=PS_COUNT+1
    echo $PS_COUNT
}

ps_count_reset() {
    let PS_COUNT=0
}

এটি নিম্নলিখিত হিসাবে ব্যবহৃত হবে (এবং সেইজন্য আমার একটি সাব-শেল থেকে ফাংশনগুলি প্রার্থনা করার প্রয়োজন):

PS1='$(ps_count_reset)> '
PS2='$(ps_count_inc)   '

এইভাবে, আমার কাছে একটি সংখ্যাযুক্ত বহু-লাইন প্রম্পট থাকবে:

> echo 'this
1   is
2   a
3   test'

চতুর। তবে উল্লিখিত সীমাবদ্ধতার কারণে কাজ হয় না।

একটি অ-কার্যক্ষম সমাধান হ'ল একটি ভেরিয়েবলের পরিবর্তে কোনও ফাইলকে গণনা লিখতে। তবে এটি একযোগে চলমান সেশনগুলির মধ্যে দ্বন্দ্ব তৈরি করবে। আমি অবশ্যই শেলটির প্রসেস আইডি ফাইলের নামের সাথে যুক্ত করতে পারতাম। তবে আমি আশা করছি এর চেয়ে ভাল সমাধান আছে যা আমার সিস্টেমকে প্রচুর ফাইলের সাথে বিশৃঙ্খলা করবে না।


ফাইল স্ট্যাশ দেখুন ব্যবহার করে ডাব্লুআরটি সংঘর্ষ man 1 mktemp
স্বর্ণলোক

আপনার আমার সম্পাদনাটি দেখতে হবে - আমার মনে হয় আপনি এটি পছন্দ করবেন।
মাইক্রজারভ

উত্তর:


14

এখানে চিত্র বর্ণনা লিখুন

আপনার প্রশ্নে আপনি যে একই আউটপুট নোট করেছেন তা পেতে, যা প্রয়োজন তা হ'ল:

PS1='${PS2c##*[$((PS2c=0))-9]}- > '
PS2='$((PS2c=PS2c+1)) > '

আপনার দরকার নেই এই দুটি লাইন এটি পসিক্সের সামঞ্জস্যের কাছের যে কোনও কিছুতে ভান করে এমন কোনও শেলের মধ্যে এটি করবে।

- > cat <<HD
1 >     line 1
2 >     line $((PS2c-1))
3 > HD
    line 1
    line 2
- > echo $PS2c
0

তবে আমি এটি পছন্দ করেছি। এবং আমি এই কাজটি আরও কিছুটা আরও ভাল করে তোলে এর মূলসূত্রগুলি প্রদর্শন করতে চেয়েছিলাম। সুতরাং আমি এটি একটি সামান্য সম্পাদিত। আমি /tmpআপাতত এটি আটকে রেখেছি তবে আমি মনে করি এটি নিজের জন্যও রাখব। এটা এখানে:

cat /tmp/prompt

প্রম্পট লিপি:

ps1() { IFS=/
    set -- ${PWD%"${last=${PWD##/*/}}"}
    printf "${1+%c/}" "$@" 
    printf "$last > "
}

PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
PS2='$((PS2c=PS2c+1)) > '

দ্রষ্টব্য: সম্প্রতি যশ শিখেছি, গতকালই আমি এটি তৈরি করেছি। যে কারণেই হোক না কেন এটি %cস্ট্রিং সহ প্রতিটি যুক্তির প্রথম বাইট মুদ্রণ করে না - যদিও দস্তাবেজগুলি সেই ফর্ম্যাটটির জন্য প্রশস্ত-চর এক্সটেনশন সম্পর্কে নির্দিষ্ট ছিল এবং তাই এটি সম্পর্কিত হতে পারে - তবে এটি দিয়ে ঠিক আছে%.1s

এটাই পুরো জিনিস। সেখানে দুটি মূল বিষয় চলছে। এবং এটি দেখতে এটির মতো:

/u/s/m/man3 > cat <<HERE
1 >     line 1
2 >     line 2
3 >     line $((PS2c-1))
4 > HERE
    line 1
    line 2
    line 3
/u/s/m/man3 >

পদান্বয় $PWD

প্রতিটি সময় $PS1মূল্যায়ন করা $PWDহয় এটি প্রম্পটে যোগ করার জন্য পার্স এবং প্রিন্ট করে। তবে আমি $PWDআমার স্ক্রিনটি পুরো ভিড় পছন্দ করি না , তাই আমি বর্তমান পাতায় প্রতিটি রুটিচক্রের প্রথম অক্ষরটি বর্তমান ডিরেক্টরিতে চাই, যা আমি সম্পূর্ণরূপে দেখতে চাই। এটার মত:

/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cd /
/ > cd ~
/h/mikeserv > 

এখানে কয়েকটি পদক্ষেপ রয়েছে:

IFS=/

আমরা বর্তমান বিভক্ত করতে আছে চলুন $PWDএবং যে সঙ্গে কি সবচেয়ে নির্ভরযোগ্য উপায় $IFSউপর বিভক্ত /। এটির পরে একেবারেই বিরক্ত করার দরকার নেই - এখান থেকে সমস্ত বিভাজন $@পরবর্তী কমান্ডের শেলের পজিশনাল প্যারামিটার অ্যারে দ্বারা সংজ্ঞায়িত করা হবে :

set -- ${PWD%"${last=${PWD##/*/}}"}

সুতরাং এটি একটি খুব কৃপণ, কিন্তু মূল বিষয় হ'ল আমরা চিহ্নগুলিতে বিভক্ত $PWDহয়ে যাচ্ছি /$lastবাম-সর্বাধিক এবং ডান-সর্বাধিক /স্ল্যাশের মধ্যে যে কোনও মান হওয়ার পরে সমস্ত কিছুর জন্য নির্ধারণ করতে আমি প্যারামিটার সম্প্রসারণও ব্যবহার করি । এইভাবে আমি জানি যে আমি যদি কেবলমাত্র থাকি /এবং কেবল একটি থাকে /তবে $lastএখনও পুরোটির সমান হবে $PWDএবং $1খালি থাকবে। এই বিষয়টি। আমি এটি নির্ধারণের আগে $lastলেজ প্রান্ত থেকে $PWDফালাও $@

printf "${1+%c/}" "$@"

সুতরাং এখানে - যতক্ষণ না ${1+is set}আমরা আমাদের শেলটির প্রতিটি আর্গুমেন্টের printfপ্রথম %cহ্যার্যাক্টর - যা আমরা আমাদের বর্তমান প্রতিটি ডিরেক্টরিতে রেখেছি $PWD- শীর্ষ ডিরেক্টরিটি কম - বিভক্ত হয়ে গেছে /। সুতরাং আমরা মূলত কেবল প্রতিটি ডিরেক্টরিতে প্রথম অক্ষরটি ছাপিয়ে যাচ্ছি $PWDতবে শীর্ষস্থানীয়। এটি গুরুত্বপূর্ণ যদিও উপলব্ধি করা এই শুধুমাত্র কিছু ঘটে তাহলে এর $1সব এ সেট, যার মূলে ঘটবে না /বা এক থেকে সরানো /মধ্যে যেমন /etc

printf "$last > "

$lastআমি সবেমাত্র আমাদের শীর্ষ ডিরেক্টরিতে নির্ধারিত পরিবর্তনশীল। সুতরাং এখন এটি আমাদের শীর্ষ ডিরেক্টরি। এটি শেষ বিবৃতিটি করেছে কিনা তা মুদ্রণ করে। এবং >ভাল পরিমাপের জন্য এটি একটি ঝরঝরে সামান্য লাগে ।

তবে কী বাড়বে?

এবং তারপরে $PS2শর্তযুক্ত বিষয়টি আছে । আমি এটি পূর্বে দেখিয়েছি এটি কীভাবে করা যায় যা আপনি এখনও নীচে খুঁজে পেতে পারেন - এটি মূলত একটি সুযোগের বিষয়। তবে আপনি যদি printf \bঅ্যাক্স স্পেসগুলির একগুচ্ছ কাজ শুরু করতে না চান এবং তারপরে তাদের চরিত্রের গণনাটি সামঞ্জস্য করার চেষ্টা না করেন তবে এর সাথে আরও কিছু আছে ... সুতরাং আমি এটি করি:

PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'

আবারও ${parameter##expansion}দিন বাঁচায়। যদিও এটি এখানে কিছুটা অদ্ভুত - আমরা এটির নিজের থেকে আলাদা করে নেওয়ার সময় আমরা আসলে পরিবর্তনশীলটি সেট করি। আমরা এর নতুন মানটি সেট করি - মিড-স্ট্রিপ সেট করি - যে গ্লোব থেকে আমরা ফালাটি পাই। দেখেন তো? আমরা ##*আমাদের ইনক্রিমেন্ট ভেরিয়েবলের মাথা থেকে শেষ চরিত্রের মধ্যে থেকে সমস্ত কিছু ফেলা যা যা হতে পারে [$((PS2c=0))-9]। আমাদের মান এইভাবে আউটপুট না দেওয়ার গ্যারান্টিযুক্ত এবং এখনও আমরা এটি নির্ধারণ করি। এটি বেশ দুর্দান্ত - আমি এর আগে কখনও করিনি। তবে পসিক্স আমাদের গ্যারান্টি দেয় যে এটি করা যায় এটি সবচেয়ে বহনযোগ্য উপায়।

এবং এটি পসিএক্স-নির্দিষ্ট করার জন্য ধন্যবাদ ${parameter} $((expansion))যা এই সংজ্ঞাগুলি বর্তমান শেলটিতে রাখে না requ আর এই কেন এটা কাজ করে dashএবং shশুধু সেইসাথে এতে করে bashএবং zsh। আমরা কোনও শেল / টার্মিনাল নির্ভর পলায়ন ব্যবহার করি না এবং আমরা ভেরিয়েবলগুলি তাদের পরীক্ষা করতে পারি। এটিই পোর্টেবল কোডটিকে দ্রুত করে তোলে

বাকিগুলি মোটামুটি সহজ - প্রতিটি বারের জন্য আমাদের কাউন্টারটিকে কেবলমাত্র পুনরায় সেট না করা $PS2পর্যন্ত মূল্যায়ন করা $PS1হয়। এটার মত:

PS2='$((PS2c=PS2c+1)) > '

সুতরাং এখন আমি করতে পারি:

ড্যাশ ডেমো

ENV=/tmp/prompt dash -i

/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cat <<HERE
1 >     line 1
2 >     line 2
3 >     line $((PS2c-1))
4 > HERE
    line 1
    line 2
    line 3
/u/s/m/man3 > printf '\t%s\n' "$PS1" "$PS2" "$PS2c"
    $(ps1)${PS2c##*[$((PS2c=0))-9]}
    $((PS2c=PS2c+1)) >
    0
/u/s/m/man3 > cd ~
/h/mikeserv >

এসএইচ ডেমো

এটিতে bashবা একই কাজ করে sh:

ENV=/tmp/prompt sh -i

/h/mikeserv > cat <<HEREDOC
1 >     $( echo $PS2c )
2 >     $( echo $PS1 )
3 >     $( echo $PS2 )
4 > HEREDOC
    4
    $(ps1)${PS2c##*[$((PS2c=0))-9]}
    $((PS2c=PS2c+1)) >
/h/mikeserv > echo $PS2c ; cd /
0
/ > cd /usr/share
/u/share > cd ~
/h/mikeserv > exit

আমি উপরে যেমন বলেছি, প্রাথমিক সমস্যা হ'ল আপনি কোথায় আপনার গণনা করেন তা বিবেচনা করা উচিত। আপনি প্যারেন্ট শেলের মধ্যে রাজ্যটি পাবেন না - সুতরাং আপনি সেখানে গণনা করবেন না। আপনি সাব-শেলের মধ্যে রাজ্যটি পাবেন - সুতরাং যেখানে আপনি গণনা করছেন। তবে আপনি প্যারেন্ট শেলের মধ্যে সংজ্ঞাটি করেন।

ENV=/dev/fd/3 sh -i  3<<\PROMPT
    ps1() { printf '$((PS2c=0)) > ' ; }
    ps2() { printf '$((PS2c=PS2c+1)) > ' ; }
    PS1=$(ps1)
    PS2=$(ps2)
PROMPT

0 > cat <<MULTI_LINE
1 > $(echo this will be line 1)
2 > $(echo and this line 2)
3 > $(echo here is line 3)
4 > MULTI_LINE
this will be line 1
and this line 2
here is line 3
0 >

1
@ মিমকিজার্ভ আমরা চেনাশোনাগুলিতে পরিণত করছি। আমি এই সব জানি। তবে আমি কীভাবে এটি আমার সংজ্ঞাতে ব্যবহার করব PS2? এটি জটিল অংশ। আমি মনে করি না এখানে আপনার সমাধান প্রয়োগ করা যেতে পারে। আপনি যদি অন্যথায় মনে করেন তবে দয়া করে আমাকে কীভাবে তা দেখান।
কনরাড রুডল্ফ

1
@ মাইক্রোজার না, এটি সম্পর্কিত নয়, দুঃখিত। বিস্তারিত জানতে আমার প্রশ্ন দেখুন। PS1এবং PS2শেলের বিশেষ ভেরিয়েবল যা কমান্ড প্রম্পট হিসাবে মুদ্রিত হয় ( PS1একটি নতুন শেল উইন্ডোতে আলাদা মান স্থাপন করে এটি ব্যবহার করে দেখুন ), সেগুলি আপনার কোড থেকে খুব আলাদাভাবে ব্যবহৃত হয়। তাদের ব্যবহারের জন্য এখানে আরও কিছু তথ্য দেওয়া হয়েছে: linuxconfig.org/bash-prompt-basics
Konrad Rudolph

1
@ কনরাড রুডল্ফ আপনাকে দু'বার সংজ্ঞা দেওয়ার থেকে বিরত রাখবে? আমার আসল জিনিসটি যা যা করেছিল ... আপনার উত্তরটি আমাকে দেখতে হবে ... এটি সমস্ত সময় করা হয়।
মাইক্রজারভ

1
@ মিমকিজার echo 'thisপ্রম্পটে টাইপ করুন , তারপরে PS2ক্লোজিং একক উদ্ধৃতি টাইপ করার আগে কীভাবে তার মান আপডেট করবেন তা ব্যাখ্যা করুন ।
চিপনার

1
ঠিক আছে, এই উত্তরটি এখন সরকারীভাবে আশ্চর্যজনক। আমি ব্রেডক্র্যাম্বগুলিও পছন্দ করি, যদিও আমি যেহেতু পৃথক লাইনে পুরো পথটি মুদ্রণ করি সেহেতু আমি এটি গ্রহণ করব না: i.imgur.com/xmqrVxL.png
Konrad Rudolph

8

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

PROMPT_COMMANDভেরিয়েবলের মান একটি কমান্ড হিসাবে ইন্টারপ্রেটেড হয় যা PS1প্রম্পট প্রিন্ট করার আগে কার্যকর করা হয় ।

কারণ PS2তুলনার মতো কিছুই নেই। তবে আপনি এর পরিবর্তে একটি কৌশল ব্যবহার করতে পারেন: যেহেতু আপনি যা করতে চান তা একটি পাটিগণিত অপারেশন, আপনি পাটিগণিতের সম্প্রসারণ ব্যবহার করতে পারেন, এতে কোনও সাবস্কেল জড়িত না।

PROMPT_COMMAND='PS_COUNT=0'
PS2='$((++PS_COUNT))  '

গাণিতিক গণনার ফলাফল প্রম্পটে শেষ হয়। আপনি যদি এটি আড়াল করতে চান তবে আপনি এটিকে একটি অ্যারে সাবস্ক্রিপ্ট হিসাবে পাস করতে পারেন যা বিদ্যমান নেই।

PS1='${nonexistent_array[$((PS_COUNT=0))]}\$ '

4

এটি সামান্য আই / ও-নিবিড়, তবে আপনাকে গণনার মান ধরে রাখতে একটি অস্থায়ী ফাইল ব্যবহার করতে হবে।

ps_count_inc () {
   read ps_count < ~/.prompt_num
   echo $((++ps_count)) | tee ~/.prompt_num
}

ps_count_reset () {
   echo 0 > ~/.prompt_num
}

যদি আপনি শেল সেশনের জন্য পৃথক ফাইলের প্রয়োজনের বিষয়ে উদ্বিগ্ন হন (যা একটি ছোটখাটো উদ্বেগের মতো বলে মনে হয়; আপনি কি একই সময়ে দুটি পৃথক শেলের মধ্যে বহু-লাইন কমান্ডগুলি টাইপ করবেন?), আপনার mktempপ্রতিটিটির জন্য একটি নতুন ফাইল তৈরি করতে ব্যবহার করা উচিত ব্যবহার করুন।

ps_count_reset () {
    rm -f "$prompt_count"
    prompt_count=$(mktemp)
    echo 0 > "$prompt_count"
}

ps_count_inc () {
    read ps_count < "$prompt_count"
    echo $((++ps_count)) | tee "$prompt_count"
}

+1 সম্ভবত ফাইলটি I / O খুব তাত্পর্যপূর্ণ নয় কারণ যদি ফাইলটি ছোট হয় এবং প্রায়শই অ্যাক্সেস করা হয় তবে এটি ক্যাশে হবে, অর্থাত এটি মূলত ভাগ করা মেমরিরূপে কাজ করে।
স্বর্ণিলোক

1

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

অন্যান্য উত্তর অনুসারে করণীয় সবচেয়ে সহজ কাজটি কোনও ফাইলের সেই ডেটা স্ট্যাশ করা।

echo $count > file
count=$(<file)

প্রভৃতি


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

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

মানুষ, হয় আমি এখানে যা প্রয়োজন তা পুরোপুরি ভুল বুঝেছি, বা অন্য সবার কাছে আছে। এটি আমার কাছে সত্যিই সহজ বলে মনে হচ্ছে। আপনি আমার সম্পাদনা দেখতে পাচ্ছেন? এতে দোষ কী?
মাইক্রজারভ

@ মেকজার্ভ আমি মনে করি না আপনি ভুল বুঝেছেন এবং ন্যায্য হতে পারেন, আপনার কাছে যা আছে তা আইপিসির একটি রূপ এবং কাজ করতে পারে । এটা আমার কাছে পরিষ্কার না কেন কনরাড এটা পছন্দ করে না, কিন্তু যদি এটা নমনীয় যথেষ্ট নয়, তারপরে ফাইলটিকে লুকোবার জায়গা প্রশংসনীয় সহজবোধ্য (এবং তাই এড়ানোর দুর্ঘটনায়, যেমন উপায় আছে mktemp)।
স্বর্ণলোকগুলি

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

0

রেফারেন্সের জন্য, অস্থায়ী ফাইলগুলি ব্যবহার করে আমার সমাধানটি এখানে দেওয়া হয়েছে, যা শেল প্রক্রিয়া অনুসারে অনন্য এবং যত তাড়াতাড়ি সম্ভব মুছে ফেলা হয় (প্রশ্নে ইঙ্গিত হিসাবে বিশৃঙ্খলা এড়ানোর জন্য):

# Yes, I actually need this to work across my systems. :-/
_mktemp() {
    local tmpfile="${TMPDIR-/tmp}/psfile-$$.XXX"
    local bin="$(command -v mktemp || echo echo)"
    local file="$($bin "$tmpfile")"
    rm -f "$file"
    echo "$file"
}

PS_COUNT_FILE="$(_mktemp)"

ps_count_inc() {
    local PS_COUNT
    if [[ -f "$PS_COUNT_FILE" ]]; then
        let PS_COUNT=$(<"$PS_COUNT_FILE")+1
    else
        PS_COUNT=1
    fi

    echo $PS_COUNT | tee "$PS_COUNT_FILE"
}

ps_count_reset() {
    rm -f "$PS_COUNT_FILE"
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.