Kaç tane mermi derinim?


73

Sorun : Kaç tane mermi derin olduğumu bulun.

Detaylar : Kabuğu vimden çok açıyorum. İnşa et, koş ve çık. Bazen, başka bir vim'i ve sonra başka bir kabuğu daha unutup açarım. :(

Ne kadar derin olduğumu bilmek istiyorum, belki de her zaman kabuk ekranımda bile. (Bu kısmı yönetebilirim).

Benim çözüm : süreç ağacı Ayrıştırma ve vim ve bash / zsh aramaya ve içindeki akım sürecin derinliğini anlamaya.

Böyle bir şey zaten var mı? Hiçbir şey bulamadım.


27
Is $SHLVLdeğişken, aradığınızı (birkaç kabukları tarafından tutulan)?
Stéphane Chazelas

1
Netleştirmek için, SHLVL ile gösterilen kaç tane (doğrudan iç içe geçmiş) mermi ile gerçekten ilgilenmiyorsunuz, fakat şu anki kabuğunuzun vim soyundan gelip gelmediği?
Jeff Schaller

14
Bu biraz XY problemi gibi görünüyor - iş akışım vim örneğinden ana kabuğa kaçmak ve fgbu sorunu olmayan geri dönmek için ^ Z.
Doorknob

2
@Doorknob, ben de yapıyorum. ama bunu tercih ederim çünkü o zaman "işleri" kontrol etmeye devam etmek zorunda kalacağım. ve zaman zaman makinemde çalışan birçok olabilir. şimdi, denklem ile TMUX ekleyin. Karmaşık ve taşma oldu. Vim içine kabuk fırlatırsam, saçılma daha az olur. (Ancak sonunda karışıklık ve dolayısıyla soru yapma).
Pranay

3
@Doorknob: Tüm saygımla, “A noktasından B noktasından nasıl geçebilirim?” Sorusuna, “Sürmeyin; sadece bir Über alın. ”Kullanıcı aynı anda birden fazla dosyayı düzenlemeyi içeren bir iş akışına sahipse, birden çok paralel durdurulmuş vimişlere sahip olmak, iç içe geçmiş bir işlem yığınına sahip olmaktan daha kafa karıştırıcı olabilir. Bu arada, birden fazla pencereye sahip olmayı tercih ediyorum , bu yüzden hızlıca ileri geri atlayabiliyorum, ancak farklı bir iş akışını tercih ettiğim için buna XY problemi demem.
Scott

Yanıtlar:


45

Sorunuzu okuduğumda ilk düşüncem buydu $SHLVL. Sonra kabuk seviyelerine ek olarakvim seviyeleri saymak istediğini gördüm . Bunu yapmanın basit bir yolu, bir kabuk işlevi tanımlamaktır:

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

Bu, SHLVL bir vimkomut her yazışınızda otomatik olarak ve sessizce artar . Sen her varyant için bunu yapmak gerekir vi/ ' vimHiç kullanın; Örneğin,

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

Dış parantez seti alt kabuk yaratır, dolayısıyla değerdeki manüel değişim SHLVL mevcut (üst) kabuk ortamını kirletmez. Elbette commandanahtar kelime, işlevlerin kendilerini çağırmasını önlemek için vardır (bu da sonsuz bir özyinelemeyle sonuçlanır). Ve tabii ki bu tanımları sizin .bashrcya da başka bir kabuk başlatma dosyasına koymalısınız .


Yukarıdakilerde hafif bir verimsizlik var. Bazı mermilerde (bir bash bash)

( cmd 1 ;  cmd 2 ;;  cmd n )

Nerede bir dış, çalıştırılabilir bir program (yani değil yerleşik bir komutu), kabuk sadece beklemek, ortalıkta ekstra bir işlem tutar olduğunu sonlandırmak için. Bu (tartışmalı) gerekli değildir; avantajları ve dezavantajları tartışmalıdır. Biraz bellek ve bir işlem yuvası bağlamak (ve istediğinizde bir tane daha fazla kabuk işlemi görmek için) sakıncası yoksa, yukarıdakileri yapın ve bir sonraki bölüme geçin. Ekstra işlemin devam etmesini önleyen bir kabuk kullanıyorsanız, aynı işlemi yapın. Ancak, fazladan işlemden kaçınmak istiyorsanız, denenecek ilk şeycmdncmdnps

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

execKomut kalıcı ekstra kabuk sürecini engellemek için vardır.

Ama bir tane var. Kabuğun kullanımı SHLVLbiraz sezgiseldir: Kabuk başladığında, SHLVLayarlanmış olup olmadığını kontrol eder . Ayarlanmadıysa (veya sayıdan başka bir şeye ayarlanmışsa), kabuk onu 1'e ayarlar. Ayarlanmışsa (sayıya), kabuk ona 1 ekler.

Ancak, bu mantıkla söylerseniz exec sh, SHLVLyükselmelisiniz. Ancak bu istenmeyen bir durumdur çünkü gerçek kabuk seviyeniz artmamıştır. Kabuk bu işler bir çıkarılmadan gelen SHLVL bir yaptığınızda exec:

$ 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

Yani

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

bir yıkama; SHLVLyalnızca tekrar azaltmak için artar . vimBir işlevden yararlanmadan, sadece söyleyebilirsiniz .

Not:
(her şeyi bilir) Stéphane Chazelas göre , bazı kabukları yeterince akıllı değil , bunu yapmak için execbir kabuktaki olduğunu.

Bunu düzeltmek için yapardın

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

Sonra vimseviyelerini , kabuk seviyesinden bağımsız olarak saymak istediğini gördüm . Tam olarak aynı numara işe yarıyor (küçük bir değişiklikle):

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

(ve böylece için ilgili vi, viewvs.) exportiçin gerekli olan VILVLvarsayılan bir ortam değişkeni olarak tanımlanmamıştır. Fakat işlevin bir parçası olması gerekmez; Sadece export VILVLayrı bir komut olarak söyleyebilirsiniz (içinde .bashrc). Ve yukarıda tartışıldığı gibi, ekstra kabuk işlemi sizin için bir sorun değilse, command vimonun yerine yapabilir exec vimve SHLVLyalnız bırakabilirsiniz :

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

Kişisel Tercih:
Bunun VILVLgibi bir şeyi yeniden adlandırmak isteyebilirsiniz VIM_LEVEL. “ VILVL” Ye baktığımda gözlerim ağrıyor; “vinil” nin yanlış yazıldığını mı yoksa hatalı biçimlendirilmiş bir Roma rakamının mı olduğunu söyleyemezler.


Desteklemeyen bir kabuk kullanıyorsanız SHLVL(örneğin çizgi), kabuk bir başlangıç ​​dosyası uyguladığı sürece kendiniz uygulayabilirsiniz. Sadece böyle bir şey yap

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

senin içinde .profileya da ilgili dosyanın. (Büyük olasılıkla adı kullanmamalısınız, SHLVLçünkü destekleyen bir kabuk kullanmaya başlarsanız bu kaosa neden olur SHLVL.)


Diğer cevaplar, ortam değişken değerlerinin kabuk istemi içine gömülmesi sorununu çözmüştür, bu yüzden bunu tekrarlamam, özellikle de zaten nasıl yapılacağını bildiğinizi söylersiniz.


1
Bir çok cevabın , kabuk yerleşiklerle ne zaman psveya pstreene zaman yapabileceğiniz gibi harici bir çalıştırılabilir program yürütmeyi önerdiğini düşünüyorum.
Scott

Bu cevap mükemmel. Bunu çözüm olarak işaretledim (ne yazık ki henüz bu kadar oy kullanmıyor).
Pranay

Yaklaşımınız şaşırtıcı ve sadece .profile / .shellrc dosyasına dahil etmek anlamına gelen ilkelleri kullanıyorsunuz. Bunları üzerinde çalıştığım her makineye çekiyorum.
Pranay

1
dashAritmetik genişlemeye sahip olduğuna dikkat edin . SHELL_LEVEL=$((SHELL_LEVEL + 1))$ SHELL_LEVEL önceden ayarlanmamış veya boş olsa bile yeterli olmalıdır. Eğer başvurmak gerekiyordu Bourne kabuğuna taşınabilir olması olsaydı sadece var expr, ama o zaman da değiştirmek gerekiyordu $(...)ile `..`. SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
Stéphane Chazelas

2
@Pranay, bir sorun olması pek mümkün değil. Eğer bir saldırgan herhangi bir env değişkenine enjekte edebiliyorsa , PATH / LD_PRELOAD gibi şeyler daha belirgin seçimlerdir, ancak eğer problemli olmayan değişkenler reset_env olmadan yapılandırılmış sudo ile (ve biri bir bashbetiği ~ / .bashrc tarafından okunması için zorlayabilir) . stdin'i bir soket yapmak gibi), o zaman bu bir problem haline gelebilir. Bu çok "eğer" dir, ama aklının arkasında tutulacak bir şey (aritmetik bağlamda doğrulanmamış veriler tehlikelidir)
Stéphane Chazelas

37

Bir oturum lideri bulana kadar süreç ağacına gitmek için ihtiyacınız olan süreyi sayabilirsiniz. Olduğu gibi zshLinux üzerinde:

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
}

Veya POSIXly (ancak daha az verimli):

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"
)

Bu, terminal emülatörünüz tarafından başlatılan kabuk için 0 verecektir ve her soyundan gelen bir tane daha ve getty.

Bunu başlangıçta yalnızca bir kez yapmanız gerekir. Mesela:

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

Sizde ~/.zshrcveya eşdeğeri istemenizde olması için.

tcshve diğer bazı kabuklar ( zsh, ksh93, fishve bashen azından) bir muhafaza $SHLVLonlar başlangıçta artırmaz değişkeni (başka komutu çalıştırmadan önce, azaltmak exec(yani sürece execonlar arabası değiliz (ancak birçok eğer bir kabuktaki olan))). Bu sadece iç içe geçirme işlemini değil, yalnızca kabuk iç içe geçme miktarını izler . Ayrıca seviye 0 oturum lideri olarak garanti edilmez.


Evet .. bu veya benzeri. Bunu kendim yazmak istemedim ve kimsenin bunu benim için yazmasını istemedim. :(. Vim veya kabukta bir özellik veya düzenli olarak tutulan bir eklenti için umuyordum, aradım ama bir şey bulamadım
Pranay

31

Kullanın echo $SHLVL. KISS prensibini kullanın . Programınızın karmaşıklığına bağlı olarak bu yeterli olabilir.


2
İçin çalışıyor bashama değil dash.
agc

SHLVL bana yardımcı olmuyor. Bunu biliyordum ve araştırdığımda araştırmaya da girdi. :) Soruda daha fazla ayrıntı var.
Pranay

@Pranay Vim'in bu bilgiyi sağlamadığından emin misiniz?
user2497,

@ user2497, ben bir miktarım. Sorunun öncülü budur. Her yeri aradım, SHLVL'nin de farkındaydım. Ben istedim -> a) öyle bir şey olmadığından emin olun. b) en az miktarda bağımlılık / bakımla yapın.
Pranay

16

Potansiyel bir çözüm çıktısına bakmaktır pstree. İçeriden yumurtlanan bir kabuğun içinde koşarken vi, listelenen ağaç ağacının bir kısmı pstreesize ne kadar derin olduğunuzu göstermelidir. Örneğin:

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

Evet, çözüm olarak önerdiğim şey bu (soruda). Ancak pstree'yi ayrıştırmak istemiyorum :(. Bu, manuel olarak okumak için iyi, benim için yapmak için bir program yazmayı düşünüyordum ve bilmeme izin ver. Bir eklenti varsa bir çözümleyici yazma eğiliminde değilim. aracı zaten yapıyor :).
Pranay

11

İlk değişken - sadece kabuk derinliği.

Şunun için basit bir çözüm bash: .bashrcsonraki iki satıra ekleyin (veya geçerli PS1değerinizi değiştirin ):

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

Sonuç:

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

Bilgi istemi dizesinin başındaki sayı, kabuk seviyesini gösterir.

Her ikisi de iç içe vim ve kabuk seviyeleri ile ikinci değişken.

bu satırları .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

Sonuç:

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 - vim derinlik seviyesi
s: 3 - kabuk derinlik seviyesi


Bu bana bash iç içe geçmesini sağlayacak. Bana vim yuvalarını vermiyor. :)
Pranay

@Pranay Yeni çözümü kontrol edin. İstediğini yapıyor.
MiniMax

Evet, bu iyi bir çözüm. Daha fazla kabuk ekleyebilirim ve işe yarayacak :).
Pranay

8

Ayrıştırmadan bahsettiğiniz soruda pstree. İşte nispeten basit bir yol:

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

pstreeseçenekleri:

  • -A- Daha kolay filtreleme için ASCII çıkışı (bizim durumumuzda her komuttan önce gelir `-)
  • -a - ayrıca komut argümanlarını göster, yan etkiler olarak her komut ayrı bir satırda gösterilir ve çıktıyı kullanarak kolayca filtreleyebiliriz. grep
  • -l - uzun çizgileri kesmeyin
  • -s- seçilen sürecin ebeveynlerini göster
    (ne yazık ki eski sürümlerinde desteklenmiyor pstree)
  • $$ - seçilen süreç - mevcut kabuğun PID değeri

Evet bunu çok yapıyordum. Ayrıca "bash" ve "vim" vb. Saymak için bir şeyim vardı. Sadece bunu korumak istemedim. Ayrıca, çok sayıda VM'yi değiştirmeniz ve bazen üzerinde geliştirme yapmanız gerektiğinde, çok sayıda özel işlevselliğe sahip olmanız da mümkün değildir.
Pranay

3

Bu kesinlikle soruyu yanıtlamıyor, ancak çoğu durumda bunu gereksiz hale getirebilir:

Kabuğunuzu ilk başlattığınızda, koşun set -o ignoreeof. İçine koyma ~/.bashrc.

Üst düzey bir kabukta olduğunuzu ve emin olmak istediğinizde Ctrl-D yazmayı alışkanlık haline getirin.

Eğer konum değil üst düzey kabuğunda, Ctrl-D akım kabuğa "girdi sonunu" sinyali verecektir ve geri bir seviye düşecek.

Eğer varsa vardır üst düzey kabuğunda, bir mesaj alacak:

Use "logout" to leave the shell.

Bunu , SSH zincirinin belirli bir seviyesine geri dönmeyi kolaylaştırmak için her zaman zincirlenmiş SSH oturumları için kullanıyorum . İç içe geçmiş kabukları için de aynı şekilde çalışır.


1
Bu kesinlikle yardımcı olur ve evet, bir çok komplikasyonları giderir :). Bunu sadece kabul edilen cevaplarla birleştirebilirim :)). Şartlı olarak ayarlanmış, bu yüzden her zaman benim istemi bakmak zorunda olmayabilir.
Pranay
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.