कितने गोले गहरे मैं हूं?


73

समस्या : मैं कितने गोले खोजता हूँ।

विवरण : मैं खोल खोल बहुत से। बनाएँ और चलाएँ और बाहर निकलें। कभी-कभी मैं भूल जाता हूं और अंदर एक और विम खोल देता हूं और फिर एक और खोल। :(

मैं जानना चाहता हूं कि मैं कितने गोले गहरे हूं, शायद हर समय मेरी शेल स्क्रीन पर भी हो। (मैं उस हिस्से को प्रबंधित कर सकता हूं)।

मेरा समाधान : प्रोसेस ट्री को पार्स करें और vim और bash / zsh देखें और उसके भीतर मौजूदा प्रक्रिया की गहराई का पता लगाएं।

क्या ऐसा कुछ पहले से मौजूद है? मुझे कुछ भी नहीं मिला।


27
क्या $SHLVLचर (कई गोले द्वारा बनाए रखा गया है) आप क्या देख रहे हैं?
स्टीफन चेजलस

1
स्पष्ट करने के लिए, आपको SHLVL द्वारा इंगित किए गए कितने (सीधे नेस्टेड) ​​गोले में वास्तव में कोई दिलचस्पी नहीं है, लेकिन क्या आपका वर्तमान शेल विम का वंशज है?
जेफ स्कालर

14
यह एक XY समस्या का एक सा लगता है - मेरे वर्कफ़्लो ^ Z मूल माता पिता खोल में विम उदाहरण से बचने के लिए, और fgवापस पाने के लिए है, जो इस समस्या नहीं है।
Doorknob

2
@ डॉर्कनोब, मैं भी यही करता हूं। लेकिन मैं इसे पसंद करता हूं, क्योंकि तब मुझे "नौकरी" की जाँच करते रहना होगा। और मेरी मशीन पर कई बार चल सकता है। अब, समीकरण के साथ TMUX जोड़ें। यह जटिल और अतिप्रवाहित हो जाता है। अगर मैं विम के अंदर गोले को फैलाऊं, तो यह एक बिखराव से कम होगा। (हालांकि मैं गड़बड़ कर रहा हूं और इसलिए सवाल)।
प्रणय

3
@Doorknob: सभी उचित सम्मान के साथ, यह सवाल का जवाब देने जैसा लगता है, "मैं बिंदु B से बिंदु से ड्राइव कैसे करूं?" सुझाव के साथ, "ड्राइव न करें" बस एक उबर लें। ”यदि उपयोगकर्ता के पास एक वर्कफ़्लो है जिसमें समवर्ती रूप से कई फ़ाइलों को संपादित करना शामिल है, तो कई समानांतर रोकी गई vimनौकरियों में नेस्टेड प्रक्रियाओं का ढेर होने से अधिक भ्रमित हो सकता है। वैसे , मैं कई विंडो रखना पसंद करता हूं, इसलिए मैं आसानी से जल्दी से आगे पीछे कूद सकता हूं, लेकिन मैं इसे एक्सवाई समस्या नहीं कहूंगा क्योंकि मैं एक अलग वर्कफ़्लो पसंद करता हूं।
स्कॉट

जवाबों:


45

जब मैंने आपका प्रश्न पढ़ा, तो मेरा पहला विचार था $SHLVL। तब मैंने देखा कि आप शेल स्तरों के अतिरिक्तvim स्तरों को गिनना चाहते थे । ऐसा करने का एक सरल तरीका एक शेल फ़ंक्शन को परिभाषित करना है:

vim()  { ( ((SHLVL++)); command vim  "$@");}

SHLVL हर बार जब आप एक vimकमांड टाइप करेंगे तो यह स्वतः और चुपचाप बढ़ेगा । आपको प्रत्येक संस्करण के लिए ऐसा करने की आवश्यकता होगी vi/ vimजिसे आप कभी भी उपयोग करते हैं; जैसे,

vi()   { ( ((SHLVL++)); command vi   "$@");}
view() { ( ((SHLVL++)); command view "$@");}

कोष्ठक का बाहरी सेट एक उप-प्रकार बनाता है, इसलिए मान के मैन्युअल परिवर्तन SHLVL वर्तमान (मूल) शेल वातावरण को दूषित नहीं करता है। बेशक commandकीवर्ड वहाँ कार्यों को खुद को कॉल करने से रोकने के लिए है (जिसके परिणामस्वरूप अनंत पुनरावर्तन लूप होगा)। और निश्चित रूप से आपको इन परिभाषाओं को अपनी .bashrcया अन्य शेल आरंभीकरण फ़ाइल में रखना चाहिए ।


उपरोक्त में थोड़ी अक्षमता है। कुछ गोले में (एक होने के नाते), यदि आप कहते हैं

( cmd 1 ;  cmd 2 ;;  cmd n )

जहां एक बाहरी, निष्पादन योग्य कार्यक्रम (यानी, अंतर्निहित कमांड नहीं है), शेल एक अतिरिक्त प्रक्रिया को चारों ओर लेटा रहता है, बस समाप्त होने की प्रतीक्षा करने के लिए। यह (यकीनन) आवश्यक नहीं है; फायदे और नुकसान बहस योग्य हैं। यदि आपको थोड़ी सी मेमोरी और एक प्रक्रिया स्लॉट (और जब आप की आवश्यकता होती है , तो एक से अधिक शेल प्रक्रिया को देखने के लिए ) का मन नहीं करता है, तो उपरोक्त करें और अगले अनुभाग पर जाएं। यदि आप एक शेल का उपयोग कर रहे हैं तो डिट्टो जो अतिरिक्त प्रक्रिया को आसपास नहीं रखता है। लेकिन, अगर आप अतिरिक्त प्रक्रिया से बचना चाहते हैं, तो पहली कोशिश यह हैcmdncmdnps

vim()  { ( ((SHLVL++)); exec vim  "$@");}

execआदेश सुस्त से अतिरिक्त खोल प्रक्रिया को रोकने के लिए है।

लेकिन, एक गोत्र है। शेल की हैंडलिंग SHLVLकुछ सहज है: जब शेल शुरू होता है, तो यह जांचता है कि क्या SHLVLसेट है। यदि यह सेट नहीं है (या किसी संख्या के अलावा किसी अन्य चीज़ पर सेट है), तो शेल इसे 1 पर सेट करता है। यदि यह सेट है (एक नंबर पर), तो शेल इसमें 1 जोड़ता है।

लेकिन, इस तर्क से, यदि आप कहते हैं exec sh, तो आपको SHLVLऊपर जाना चाहिए। लेकिन यह अवांछनीय है, क्योंकि आपका वास्तविक शेल स्तर नहीं बढ़ा है। जब आप ऐसा करते हैं तो शेल एक को घटाकर इसे संभालता है :SHLVLexec

$ echo "$SHLVL"
1

$ set | grep SHLVL
SHLVL=1

$ env | grep SHLVL
SHLVL=1

$ (env | grep SHLVL)
SHLVL=1

$ (env) | grep SHLVL
SHLVL=1

$ (exec env) | grep SHLVL
SHLVL=0

इसलिए

vim()  { ( ((SHLVL++)); exec vim  "$@");}

धोना है; इसे SHLVLफिर से बढ़ाने के लिए ही वेतन वृद्धि होती है । आप vimकिसी फ़ंक्शन के लाभ के बिना भी बस कह सकते हैं।

नोट:
स्टीफन चेज़लस (जो सब कुछ जानता है) के अनुसार , कुछ गोले काफी स्मार्ट होते हैं यदि ऐसा नहीं है तो execएक उपधारा में है।

इसे ठीक करने के लिए, आप करेंगे

vim()  { ( ((SHLVL+=2)); exec vim  "$@");}

फिर मैंने देखा कि आप गिनती करने के लिए चाहता था vimस्तरों की स्वतंत्र रूप से खोल स्तरों। ठीक है, ठीक वही चाल काम करती है (ठीक है, एक मामूली संशोधन के साथ):

vim() { ( ((SHLVL++, VILVL++)); export VILVL; exec vim "$@");}

(और के लिए पर इसलिए vi, view, आदि) exportआवश्यक है क्योंकि VILVLडिफ़ॉल्ट रूप से एक वातावरण चर के रूप में परिभाषित नहीं है। लेकिन इसे समारोह का हिस्सा बनने की आवश्यकता नहीं है; आप बस export VILVLएक अलग कमांड के रूप में कह सकते हैं (अपने में .bashrc)। और, जैसा कि ऊपर चर्चा की गई है, यदि अतिरिक्त शेल प्रक्रिया आपके लिए कोई समस्या नहीं है, तो आप इसके command vimबजाय कर सकते हैं exec vim, और SHLVLअकेले छोड़ सकते हैं:

vim() { ( ((VILVL++)); command vim "$@");}

व्यक्तिगत पसंद:
आप VILVLकुछ का नाम बदलना चाह सकते हैं VIM_LEVEL। जब मैं " VILVL" देखता हूं, तो मेरी आंखें दुखती हैं; वे यह नहीं बता सकते हैं कि यह "विनाइल" की गलत वर्तनी है या एक विकृत रोमन अंक है।


यदि आप ऐसे शेल का उपयोग कर रहे हैं जो समर्थन नहीं करता है SHLVL(उदाहरण के लिए, डैश), तो आप इसे तब तक स्वयं कार्यान्वित कर सकते हैं जब तक शेल किसी स्टार्टअप फ़ाइल को लागू करता है। जैसे कुछ करो

if [ "$SHELL_LEVEL" = "" ]
then
    SHELL_LEVEL=1
else
    SHELL_LEVEL=$(expr "$SHELL_LEVEL" + 1)
fi
export SHELL_LEVEL

आपकी .profileया लागू फ़ाइल में। (आपको शायद नाम का उपयोग नहीं करना चाहिए SHLVL, क्योंकि यह अराजकता का कारण होगा यदि आप कभी भी शेल का उपयोग करना शुरू करते हैं जो समर्थन करता है SHLVL।)


अन्य उत्तरों ने आपके शेल प्रॉम्प्ट में पर्यावरण चर मान (ओं) को एम्बेड करने के मुद्दे को संबोधित किया है, इसलिए मैं इसे नहीं दोहराऊंगा, विशेष रूप से आप कहते हैं कि आप पहले से ही जानते हैं कि यह कैसे करना है।


1
मैं कुछ हद तक हैरान हूं कि इतने सारे जवाब बाहरी निष्पादन योग्य कार्यक्रम को निष्पादित करने का सुझाव देते हैं, जैसे psया pstree, जब आप शेल बिल्डरों के साथ ऐसा कर सकते हैं।
स्कॉट

यह उत्तर एकदम सही है। मैंने इसे समाधान के रूप में चिह्नित किया है (दुर्भाग्य से इसमें इतने वोट नहीं हैं)।
प्रणय

आपका दृष्टिकोण अद्भुत है और आप केवल प्राइमेट का उपयोग कर रहे हैं, जिसका अर्थ है कि यह मेरे .profile / .shellrc में शामिल है और कुछ भी नहीं टूटेगा। मैं किसी भी मशीन पर काम करता हूं।
प्रणय

1
ध्यान दें कि dashअंकगणितीय विस्तार है। SHELL_LEVEL=$((SHELL_LEVEL + 1))यदि $ SHELL_LEVEL पहले से परेशान या खाली था, तब भी पर्याप्त होना चाहिए। यह केवल अगर आपको बॉर्न शेल के लिए पोर्टेबल होना था, जिसका आपको सहारा लेना होगा expr, लेकिन तब आपको इसके $(...)साथ बदलने की आवश्यकता होगी `..`SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
स्टीफन चेज़लस

2
@Pranay, यह एक समस्या होने की संभावना नहीं है। यदि कोई हमलावर किसी भी मनमानी env var को इंजेक्ट कर सकता है , तो PATH / LD_PRELOAD जैसी चीजें अधिक स्पष्ट विकल्प हैं, लेकिन यदि गैर-समस्याग्रस्त चर मिलते हैं, जैसे कि sudo बिना reset_env के कॉन्फ़िगर किया गया है (और कोई bashस्क्रिप्ट को पढ़ने के लिए बाध्य कर सकता है ~ / .bashrc द्वारा। उदाहरण के लिए स्टड सॉकेट बनाना), तो यह एक समस्या बन सकती है। यह "अगर" का एक बहुत है, लेकिन किसी के दिमाग के पीछे रखने के लिए कुछ (अंकगणित के संदर्भ में अज्ञात डेटा खतरनाक है)
स्टीफन चेज़लस

37

जब तक आप सत्र के नेता को नहीं खोज लेते, तब तक आपको कई बार प्रक्रिया पेड़ पर चढ़ने की आवश्यकता होती है। zshलिनक्स पर की तरह :

lvl() {
  local n=0 pid=$$ buf
  until
    IFS= read -rd '' buf < /proc/$pid/stat
    set -- ${(s: :)buf##*\)}
    ((pid == $4))
  do
    ((n++))
    pid=$2
  done
  echo $n
}

या POSIXly (लेकिन कम कुशल):

lvl() (
  unset IFS
  pid=$$ n=0
  until
    set -- $(ps -o ppid= -o sid= -p "$pid")
    [ "$pid" -eq "$2" ]
  do
    n=$((n + 1)) pid=$1
  done
  echo "$n"
)

यह उस शेल के लिए 0 देगा जो आपके टर्मिनल एमुलेटर या गेट्टी द्वारा शुरू किया गया था और प्रत्येक वंश के लिए एक और।

आपको केवल एक बार स्टार्टअप पर करने की आवश्यकता है। उदाहरण के लिए:

PS1="[$(lvl)]$PS1"

आपके ~/.zshrcया इसके बराबर में आपके प्रॉम्प्ट में है।

tcshऔर कई अन्य गोले ( zsh, ksh93, fishऔर bashकम से कम) एक बनाए रखने के $SHLVLचर जो वे स्टार्टअप पर बढ़ा देते हैं (और घटती के साथ एक और आदेश चलाने से पहले exec(जब तक कि execएक subshell में है अगर वे गाड़ी नहीं कर रहे हैं (लेकिन कई हैं)))। यह केवल शेल नेस्टिंग की मात्रा को ट्रैक करता है , हालांकि नेस्टिंग को प्रोसेस नहीं करता है। साथ ही लेवल 0 को सेशन लीडर होने की गारंटी नहीं है।


हाँ .. यह या इसी तरह। मैं अपने खुद के इस लिखने की इच्छा नहीं करता था, और किसी के लिए यह लिखना मेरा उद्देश्य नहीं था। :(। मैं विम या शेल या कुछ प्लगइन में कुछ फीचर की उम्मीद कर रहा था जो नियमित रूप से बनाए रखा जाता है। मैंने खोजा लेकिन कुछ नहीं मिला।
प्रणय

31

का उपयोग करें echo $SHLVL। का प्रयोग करें KISS सिद्धांत । आपके कार्यक्रम की जटिलता के आधार पर, यह पर्याप्त हो सकता है।


2
के लिए काम करता है bash, लेकिन के लिए नहीं dash
18

SHLVL मेरी मदद नहीं करता है। मुझे इसके बारे में पता था, और जब मैंने खोज की तो यह भी पता चला। :) प्रश्न में अधिक विवरण हैं।
प्रणय

@Pranay क्या आप निश्चित हैं कि विम खुद यह जानकारी प्रदान नहीं करता है?
user2497

@ user2497, मैं कुछ हद तक हूँ। यह प्रश्न का आधार है। मैंने हर जगह खोज की, मुझे SHLVL के बारे में भी पता था। मैं चाहता था -> क) ऐसी कोई बात नहीं है। बी) यह निर्भरता / रखरखाव की कम से कम राशि के साथ करते हैं।
प्रणय

16

एक संभावित समाधान के उत्पादन को देखने के लिए है pstree। जब एक शेल के अंदर चला जाता है जो भीतर से पैदा हुआ था vi, तो पेड़ के पेड़ का वह भाग जो सूचीबद्ध करता है, आपको pstreeयह दिखाना चाहिए कि आप कितने गहरे हैं। उदाहरण के लिए:

$ pstree <my-user-ID>
...
       ├─gnome-terminal-─┬─bash───vi───sh───vi───sh───pstree
...

हाँ, वही है जो मैंने अपने समाधान के रूप में सुझाया था (प्रश्न में)। हालांकि मैं पास्ट्री को पार्स नहीं करना चाहता :( यह मैन्युअल रूप से पढ़ने के लिए अच्छा है, मैं इसे करने के लिए एक कार्यक्रम लिखने के बारे में सोच रहा था और मुझे बता दूं। मैं एक प्लगइन लिखने के लिए बहुत इच्छुक नहीं हूं / यदि कोई प्लगइन / उपकरण पहले से ही यह :)।
प्रणय

11

पहला संस्करण - केवल शैल गहराई।

इसके लिए सरल समाधान bash: .bashrcअगली दो पंक्तियों में जोड़ें (या अपना वर्तमान PS1मूल्य बदलें ):

PS1="${SHLVL} \w\$ "
export PS1

नतीजा:

1 ~$ bash
2 ~$ bash
3 ~$ exit
exit
2 ~$ exit
exit
1 ~$

प्रॉम्प्ट स्ट्रिंग की शुरुआत में संख्या शेल स्तर को दर्शाती है।

दूसरा वेरिएंट, नेस्टेड विम और शेल दोनों स्तरों के साथ।

इस लाइनों को जोड़ें .bashrc

branch=$(pstree -ls $$)
vim_lvl=$(grep -o vim <<< "$branch" | wc -l)
sh_lvl=$(grep -o bash <<< "$branch" | wc -l)
PS1="v:${vim_lvl};s:$((sh_lvl - 1)):\w\$ "
export PS1

नतीजा:

v:0;s:1:/etc$ bash
v:0;s:2:/etc$ bash
v:0;s:3:/etc$ vim
##### do ':sh' command in the vim, shell level is increasing by 1
v:1;s:4:/etc$ vim
##### do ':sh' command in the vim, shell level is increasing by 1
v:2;s:5:/etc$ bash
v:2;s:6:/etc$

v: 1 - विम गहराई स्तर
s: 3 - शैल गहराई स्तर


इससे मुझे बैश नेस्टिंग मिलेगी। यह मुझे विम घोंसला नहीं देगा। :)
प्रणय

@ चरण नए समाधान की जाँच करें। यह वही कर रहा है जो आप चाहते हैं।
मिनीमैक्स

हाँ, यह एक अच्छा उपाय है। मैं अधिक गोले जोड़ सकता हूं और यह काम करेगा :)।
प्रणय

8

प्रश्न में आपने पार्सिंग का उल्लेख किया है pstree। यहाँ एक अपेक्षाकृत सरल तरीका है:

bash-4.3$ pstree -Aals $$ | grep -E '^ *`-((|ba|da|k|c|tc|z)sh|vim?)( |$)'
                  `-bash
                      `-bash --posix
                          `-vi -y
                              `-dash
                                  `-vim testfile.txt
                                      `-tcsh
                                          `-csh
                                              `-sh -
                                                  `-zsh
                                                      `-bash --norc --verbose

pstreeविकल्प:

  • -A- आसान फ़िल्टरिंग के लिए ASCII आउटपुट (हमारे मामले में प्रत्येक आदेश से पहले `-)
  • -a - शो कमांड आर्ग्युमेंट्स, एक साइड-इफेक्ट के रूप में हर कमांड को एक अलग लाइन पर दिखाया गया है और हम आसानी से उपयोग करके आउटपुट को फ़िल्टर कर सकते हैं grep
  • -l - लंबी लाइनों को न काटें
  • -s- चयनित प्रक्रिया के माता-पिता को दिखाएँ
    (दुर्भाग्य से पुराने संस्करणों में समर्थित नहीं pstree)
  • $$ - चयनित प्रक्रिया - वर्तमान शेल का पीआईडी

हाँ, मैं यह बहुत कर रहा था। मेरे पास "बैश" और "विम" आदि को गिनने के लिए भी कुछ था। मैं इसे बनाए रखना नहीं चाहता था। जब आप बहुत से वीएम पर स्विच करना और कभी-कभी उन पर विकास करना हो तो बहुत सी कस्टम कार्यक्षमता होना भी संभव नहीं है।
प्रणय

3

यह सवाल का कड़ाई से जवाब नहीं देता है लेकिन कई मामलों में ऐसा करना अनावश्यक कर सकता है:

जब आप पहली बार अपना शेल लॉन्च करते हैं, तो चलाएं set -o ignoreeof। इसे अपने अंदर मत डालो ~/.bashrc

जब आपको लगता है कि आप शीर्ष स्तर के शेल में हैं तो Ctrl-D टाइप करना एक आदत बना लें और सुनिश्चित होना चाहते हैं।

यदि आप नहीं शीर्ष स्तर खोल में, Ctrl-D वर्तमान खोल करने के लिए "इनपुट के अंत" संकेत जाएगा और तुम वापस एक स्तर छोड़ देंगे।

यदि आप कर रहे हैं शीर्ष स्तर खोल में, आप एक संदेश प्राप्त होगा:

Use "logout" to leave the shell.

मैं इस समय का उपयोग SSH सत्रों के लिए करता हूं , ताकि SSH श्रृंखला के एक विशिष्ट स्तर पर वापस जाना आसान हो जाए। यह नेस्टेड शेल के लिए भी काम करता है।


1
यह निश्चित रूप से मदद करता है और हाँ यह बहुत सारी जटिलताओं को दूर करेगा :)। मैं इसे केवल स्वीकृत उत्तर के साथ जोड़ सकता हूं :))। सशर्त रूप से सेट है, इसलिए मुझे हर समय अपने प्रॉम्प्ट को देखने की आवश्यकता नहीं हो सकती है।
प्रणय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.