Berapa banyak kerang dalam diriku?

73

Masalah : Temukan berapa banyak kerang dalam diriku.

Detail : Saya sering membuka shell dari vim. Bangun dan jalankan dan keluar. Kadang-kadang saya lupa dan membuka vim lain di dalam dan kemudian shell lain. :(

Saya ingin tahu berapa banyak kerang dalam diri saya, mungkin bahkan ada di layar shell saya setiap saat. (Saya dapat mengatur bagian itu).

Solusi saya : Parsing pohon proses dan cari vim dan bash / zsh dan cari tahu kedalaman proses saat ini di dalamnya.

Apakah sesuatu seperti itu sudah ada? Saya tidak dapat menemukan apa pun.

Pranay
sumber
27
Apakah $SHLVLvariabel (dikelola oleh beberapa shell) apa yang Anda cari?
Stéphane Chazelas
1
Untuk memperjelas, Anda tidak benar-benar tertarik pada berapa banyak (langsung bersarang) shell, ditunjukkan oleh SHLVL, tetapi apakah shell Anda saat ini adalah keturunan vim?
Jeff Schaller
14
Ini tampaknya menjadi sedikit masalah XY - alur kerja saya adalah ^ Z untuk melarikan diri dari contoh vim ke dalam shell induk, dan fguntuk kembali, yang tidak memiliki masalah ini.
Gagang pintu
2
@ Doorknob, saya juga melakukannya. tapi saya lebih suka yang ini, karena saya harus terus memeriksa "pekerjaan". dan Ada banyak yang bisa berjalan di komputer saya. sekarang, tambahkan TMUX dengan persamaan. Itu menjadi kompleks dan meluap. Jika saya menelurkan shell di dalam vim, itu akan menjadi kurang sebar. (Namun saya akhirnya membuat kekacauan dan karenanya menjadi pertanyaan).
Pranay
3
@Doorknob: Dengan segala hormat, sepertinya menanggapi pertanyaan, "Bagaimana saya mengemudi dari titik A dari titik B?" Dengan saran, "Jangan mengemudi; ambil saja Uber. ”Jika pengguna memiliki alur kerja yang melibatkan pengeditan beberapa file secara bersamaan, maka memiliki beberapa vimpekerjaan paralel yang terhenti mungkin lebih membingungkan daripada memiliki setumpuk proses bersarang. Oleh karena itu, saya lebih suka memiliki beberapa jendela , jadi saya dapat dengan mudah melompat-lompat dengan cepat, tetapi saya tidak akan menyebut ini masalah XY hanya karena saya lebih suka alur kerja yang berbeda.
Scott

Jawaban:

45

Ketika saya membaca pertanyaan Anda, pikiran pertama saya adalah $SHLVL. Lalu saya melihat bahwa Anda ingin menghitung vimlevel selain level shell. Cara sederhana untuk melakukan ini adalah dengan mendefinisikan fungsi shell:

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

Ini akan secara otomatis dan diam-diam bertambah SHLVL setiap kali Anda mengetik vimperintah. Anda perlu melakukan ini untuk setiap varian vi/ vimyang pernah Anda gunakan; misalnya,

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

Set kurung luar membuat subkulit, sehingga perubahan manual dalam nilai SHLVL tidak mencemari lingkungan shell (induk) saat ini. Tentu saja commandkata kunci itu ada untuk mencegah fungsi memanggil diri mereka sendiri (yang akan menghasilkan loop rekursi tak terbatas). Dan tentu saja Anda harus meletakkan definisi ini ke dalam .bashrcfile inisialisasi shell Anda atau lainnya.


Ada sedikit inefisiensi di atas. Dalam beberapa shell (bash menjadi satu), jika Anda mengatakannya

( cmd 1 ;  cmd 2 ; ... ;  cmd n )

di mana ada program eksternal yang dapat dieksekusi (yaitu, bukan perintah bawaan), shell menyimpan proses tambahan di sekitar, hanya untuk menunggu untuk mengakhiri. Ini (bisa dibilang) tidak perlu; kelebihan dan kekurangannya masih bisa diperdebatkan. Jika Anda tidak keberatan mengikat sedikit memori dan slot proses (dan untuk melihat satu proses shell lebih dari yang Anda butuhkan saat melakukan a ), maka lakukan hal di atas dan lewati ke bagian berikutnya. Ditto jika Anda menggunakan shell yang tidak membuat proses ekstra berbaring. Tetapi, jika Anda ingin menghindari proses ekstra, hal pertama yang harus dicoba adalahcmdncmdnps

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

The execperintah yang ada untuk mencegah proses shell ekstra dari berlama-lama.

Tapi, ada gotcha. Penanganan shell SHLVLagak intuitif: Ketika shell mulai, ia memeriksa apakah SHLVLsudah diatur. Jika tidak disetel (atau disetel ke selain angka), shell menetapkannya ke 1. Jika disetel (ke angka), shell menambahkan 1 ke dalamnya.

Tetapi, dengan logika ini, jika Anda berkata exec sh, Anda SHLVLharus naik. Tapi itu tidak diinginkan, karena level shell asli Anda belum meningkat. Shell menangani ini dengan mengurangi satu dari SHLVL saat Anda melakukan 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

Begitu

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

adalah mencuci; itu akan menambahkan SHLVLhanya untuk pengurangan lagi. Anda mungkin juga hanya mengatakan vim, tanpa manfaat dari suatu fungsi.

Catatan:
Menurut Stéphane Chazelas (siapa yang tahu segalanya) , beberapa cangkang cukup pintar untuk tidak melakukan ini jika execada dalam subkulit.

Untuk memperbaikinya, Anda harus melakukannya

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

Lalu saya melihat bahwa Anda ingin menghitung vimlevel secara terpisah dari level shell. Nah, trik yang sama persis berfungsi (well, dengan sedikit modifikasi):

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

(dan seterusnya untuk vi,, viewdll.) exportDiperlukan karena VILVLtidak didefinisikan sebagai variabel lingkungan secara default. Tetapi tidak harus menjadi bagian dari fungsi; Anda bisa mengatakan export VILVLsebagai perintah terpisah (di Anda .bashrc). Dan, seperti yang dibahas di atas, jika proses shell tambahan tidak menjadi masalah bagi Anda, Anda dapat melakukan command vimalih - alih exec vim, dan biarkan SHLVLsendiri:

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

Preferensi Pribadi:
Anda mungkin ingin mengubah nama VILVLmenjadi sesuatu seperti VIM_LEVEL. Ketika saya melihat " VILVL", mata saya sakit; mereka tidak tahu apakah itu salah mengeja “vinil” atau angka Romawi yang salah.


Jika Anda menggunakan shell yang tidak mendukung SHLVL(mis., Dash), Anda bisa mengimplementasikannya sendiri selama shell mengimplementasikan file startup. Lakukan saja sesuatu seperti

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

dalam .profilefile Anda atau yang berlaku. (Anda mungkin tidak boleh menggunakan nama SHLVL, karena itu akan menyebabkan kekacauan jika Anda mulai menggunakan shell yang mendukung SHLVL.)


Jawaban lain telah membahas masalah menanamkan nilai variabel lingkungan ke prompt shell Anda, jadi saya tidak akan mengulanginya, terutama Anda mengatakan Anda sudah tahu bagaimana melakukannya.

Scott
sumber
1
Saya agak bingung bahwa begitu banyak jawaban menyarankan untuk menjalankan program yang dapat dieksekusi eksternal, seperti psatau pstree, ketika Anda dapat melakukan ini dengan shell builtins.
Scott
Jawaban ini sempurna. Saya telah menandai ini sebagai solusinya (sayangnya belum memiliki banyak suara)
Pranay
Pendekatan Anda luar biasa dan Anda hanya menggunakan primitif yang berarti memasukkan ini dalam .profile / .shellrc saya tidak akan merusak apa pun. Saya tarik itu pada mesin yang saya kerjakan.
Pranay
1
Perhatikan bahwa dashmemiliki ekspansi aritmatika. SHELL_LEVEL=$((SHELL_LEVEL + 1))seharusnya cukup walaupun $ SHELL_LEVEL sebelumnya tidak disetel atau kosong. Ini hanya jika Anda harus portabel untuk shell Bourne yang Anda akan perlu untuk menggunakan expr, tapi kemudian Anda juga akan perlu mengganti $(...)dengan `..`. SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
Stéphane Chazelas
2
@ Pray, itu tidak akan menjadi masalah. Jika penyerang dapat menyuntikkan setiap env var sewenang-wenang, maka hal-hal seperti PATH / LD_PRELOAD adalah pilihan yang lebih jelas, tetapi jika variabel non-bermasalah melewati, seperti dengan sudo dikonfigurasi tanpa reset_env (dan satu dapat memaksa bashscript untuk membaca ~ / .bashrc oleh membuat stdin soket misalnya), maka itu bisa menjadi masalah. Itu banyak "jika", tetapi sesuatu yang harus diingat seseorang (data yang tidak bersih dalam konteks aritmatika berbahaya)
Stéphane Chazelas
37

Anda bisa menghitung sebanyak waktu yang Anda butuhkan untuk naik pohon proses sampai Anda menemukan pemimpin sesi. Suka dengan zshdi Linux:

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
}

Atau POSIXly (tetapi kurang efisien):

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

Itu akan memberi 0 untuk shell yang dimulai oleh emulator terminal Anda atau getty dan satu lagi untuk setiap keturunan.

Anda hanya perlu melakukannya sekali saat startup. Misalnya dengan:

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

di ~/.zshrcatau setara dengan memilikinya di prompt Anda.

tcshdan beberapa kerang lainnya ( zsh, ksh93, fishdan bashsetidaknya) mempertahankan $SHLVLvariabel yang mereka kenaikan pada startup (dan penurunan sebelum menjalankan perintah lain dengan exec(kecuali yang execada di subkulit jika mereka tidak kereta (tapi banyak yang))). Itu hanya melacak jumlah shell nesting, bukan proses nesting. Level 0 juga tidak dijamin menjadi pemimpin sesi.

Stéphane Chazelas
sumber
Ya .. ini atau serupa. Saya tidak ingin menulis ini sendiri, dan bukan niat saya untuk meminta siapa pun menulis ini untuk saya. . :( Saya berharap untuk beberapa fitur dalam vim atau shell atau plugin yang secara teratur dipertahankan saya mencari tetapi tidak menemukan sesuatu..
Pranay
31

Gunakan echo $SHLVL. Gunakan prinsip KISS . Tergantung pada kompleksitas program Anda, ini mungkin cukup.

pengguna2497
sumber
2
Bekerja untuk bash, tetapi tidak untuk dash.
agc
SHLVL tidak membantu saya. Saya tahu tentang itu, dan itu juga muncul dalam pencarian ketika saya mencari. :) Ada lebih banyak detail dalam pertanyaan.
Pranay
@ Paray Apakah Anda yakin vim itu sendiri tidak memberikan informasi ini?
user2497
@ user2497, saya agak. Ini adalah premis dari pertanyaan itu. Saya mencari kemana-mana, saya juga tahu SHLVL. Saya ingin -> a) memastikan tidak ada hal seperti itu. b) melakukannya dengan paling sedikit jumlah dependensi / pemeliharaan.
Pranay
16

Salah satu solusi potensial adalah dengan melihat output dari pstree. Ketika dijalankan di dalam cangkang yang muncul dari dalam vi, bagian dari pohon pohon yang ada di daftar pstreeharus menunjukkan seberapa dalam diri Anda. Sebagai contoh:

$ pstree <my-user-ID>
...
       ├─gnome-terminal-─┬─bash───vi───sh───vi───sh───pstree
...
John
sumber
Ya itulah yang saya sarankan sebagai solusi saya (dalam pertanyaan). Saya tidak ingin menguraikan pstree :(. Ini bagus untuk membacanya secara manual, saya berpikir untuk menulis program untuk melakukannya untuk saya dan beri tahu saya. Saya tidak terlalu cenderung untuk menulis parser jika plugin / alat sudah melakukannya :).
Pranay
11

Varian pertama - kedalaman shell saja.

Solusi sederhana untuk bash: tambahkan ke .bashrcdua baris berikutnya (atau ubah PS1nilai Anda saat ini ):

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

Hasil:

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

Nomor di awal string cepat akan menunjukkan level shell.

Varian kedua, dengan vim bersarang dan level shell keduanya.

tambahkan baris ini ke .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

Hasil:

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 level kedalaman
s: 3 - level kedalaman shell

MiniMax
sumber
ini akan memberiku bash nesting. Itu tidak akan memberiku sarang vim. :)
Pranay
@Pranay Periksa solusi baru. Melakukan apa yang Anda inginkan.
MiniMax
Ya, ini solusi yang bagus. Saya dapat menambahkan lebih banyak kerang dan itu akan berhasil :).
Pranay
8

Dalam pertanyaan yang Anda sebutkan parsing pstree. Ini cara yang relatif sederhana:

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

The pstreepilihan:

  • -A- Output ASCII untuk memudahkan penyaringan (dalam kasus kami setiap perintah didahului oleh `-)
  • -a - tampilkan juga perintah argumen, sebagai efek samping setiap perintah ditampilkan pada baris terpisah dan kita dapat dengan mudah memfilter output menggunakan grep
  • -l - jangan memotong garis panjang
  • -s- perlihatkan orang tua dari proses yang dipilih
    (sayangnya tidak didukung di versi lama pstree)
  • $$ - proses yang dipilih - PID dari shell saat ini
pabouk
sumber
Ya saya cukup banyak melakukan ini. Saya juga punya sesuatu untuk dihitung "bash" dan "vim" dll. Saya hanya tidak ingin mempertahankannya. Ini juga tidak layak untuk memiliki banyak fungsi kustom ketika Anda harus beralih banyak VM dan kadang-kadang mengembangkannya.
Pranay
3

Ini tidak sepenuhnya menjawab pertanyaan tetapi dalam banyak kasus mungkin membuatnya tidak perlu untuk melakukannya:

Saat pertama kali meluncurkan shell Anda, jalankan set -o ignoreeof. Jangan memasukkannya ke dalam ~/.bashrc.

Biasakan untuk mengetikkan Ctrl-D ketika Anda berpikir Anda berada di shell tingkat atas dan ingin memastikan.

Jika Anda tidak berada di shell tingkat atas, Ctrl-D akan memberi sinyal "end of input" ke shell saat ini dan Anda akan mundur satu tingkat.

Jika Anda berada di shell tingkat atas, Anda akan mendapatkan pesan:

Use "logout" to leave the shell.

Saya menggunakan ini sepanjang waktu untuk sesi SSH dirantai, untuk membuatnya mudah untuk kembali ke tingkat tertentu dari rantai SSH. Ia bekerja untuk cangkang bersarang juga.

Wildcard
sumber
1
Ini pasti membantu dan ya itu akan menghilangkan banyak komplikasi :). Saya mungkin hanya menggabungkan ini dengan jawaban yang diterima :)). Diatur secara kondisional, jadi saya mungkin tidak harus melihat prompt saya sepanjang waktu.
Pranay