Tôi có bao nhiêu vỏ ốc sâu?


73

Vấn đề : Tìm bao nhiêu vỏ tôi sâu.

Chi tiết : Tôi mở shell từ vim rất nhiều. Xây dựng và chạy và thoát. Đôi khi tôi quên và mở một vim khác bên trong và sau đó là một vỏ khác. :

Tôi muốn biết tôi sâu bao nhiêu vỏ, thậm chí có thể có nó trên màn hình vỏ của tôi mọi lúc. (Tôi có thể quản lý phần đó).

Giải pháp của tôi : Phân tích cây quy trình và tìm vim và bash / zsh và tìm ra độ sâu của quy trình hiện tại trong nó.

Có một cái gì đó như thế đã tồn tại? Tôi không thể tìm thấy bất cứ điều gì.


27
$SHLVLbiến (được duy trì bởi một số shell) những gì bạn đang tìm kiếm?
Stéphane Chazelas

1
Để làm rõ, bạn không thực sự quan tâm đến việc có bao nhiêu vỏ (được lồng trực tiếp), được chỉ định bởi SHLVL, nhưng liệu vỏ hiện tại của bạn có phải là hậu duệ của vim không?
Jeff Schaller

14
Đây có vẻ là một vấn đề của XY - quy trình làm việc của tôi là ^ Z để thoát khỏi phiên bản vim vào trình bao cha mẹ và fgđể quay lại, điều này không có vấn đề này.
Doorknob

2
@Doorknob, tôi cũng vậy. nhưng tôi thích cái này hơn, vì sau đó tôi sẽ phải tiếp tục kiểm tra "công việc". và có thể có nhiều lần chạy trên máy của tôi. Bây giờ, thêm TMUX với phương trình. Nó trở nên phức tạp và tràn đầy. Nếu tôi sinh ra vỏ bên trong vim, nó sẽ ít bị phân tán hơn. (Tuy nhiên, cuối cùng tôi đã làm cho sự lộn xộn và do đó câu hỏi).
Cầu nguyện

3
@Doorknob: Với tất cả sự tôn trọng, có vẻ như trả lời câu hỏi, Làm thế nào để tôi lái xe từ điểm A từ điểm B?, Với lời đề nghị, Lái xe Đừng lái xe; chỉ cần sử dụng Uber. Nếu người dùng có quy trình làm việc liên quan đến việc chỉnh sửa đồng thời nhiều tệp, thì việc có nhiều vimcông việc bị dừng song song có thể gây nhầm lẫn hơn là có một chồng các quy trình lồng nhau. Nhân tiện, tôi thích có nhiều cửa sổ hơn , vì vậy tôi có thể dễ dàng nhảy qua lại nhanh chóng, nhưng tôi sẽ không gọi đây là vấn đề XY chỉ vì tôi thích một quy trình làm việc khác.
Scott

Câu trả lời:


45

Khi tôi đọc câu hỏi của bạn, suy nghĩ đầu tiên của tôi là $SHLVL. Sau đó, tôi thấy rằng bạn muốn đếm vimcấp độ ngoài cấp độ vỏ. Một cách đơn giản để làm điều này là xác định hàm shell:

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

Điều này sẽ tự động và âm thầm tăng lên SHLVL mỗi khi bạn gõ một vimlệnh. Bạn sẽ cần phải làm điều này cho từng biến thể của vi/ vimmà bạn từng sử dụng; ví dụ,

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

Tập hợp dấu ngoặc đơn bên ngoài tạo ra một lớp con, do đó, thay đổi thủ công trong giá trị của SHLVL không làm nhiễm bẩn môi trường vỏ (cha mẹ) hiện tại. Tất nhiên commandtừ khóa là có để ngăn các hàm tự gọi (điều này sẽ dẫn đến một vòng lặp đệ quy vô hạn). Và tất nhiên bạn nên đặt các định nghĩa này vào .bashrctệp khởi tạo shell của bạn hoặc khác.


Có một chút không hiệu quả ở trên. Trong một số shell (bash là một), nếu bạn nói

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

trong đó là một chương trình thực thi bên ngoài (nghĩa là không phải là lệnh tích hợp), shell giữ một quá trình bổ sung nằm xung quanh, chỉ để chờ kết thúc. Điều này (được cho là) ​​không cần thiết; những lợi thế và bất lợi đang tranh cãi. Nếu bạn không phiền khi buộc một chút bộ nhớ và khe quá trình (và để xem thêm một quy trình shell hơn bạn cần khi bạn thực hiện a ), thì hãy làm như trên và chuyển sang phần tiếp theo. Ditto nếu bạn đang sử dụng một vỏ không giữ quá trình thêm nằm xung quanh. Nhưng, nếu bạn muốn tránh quá trình bổ sung, điều đầu tiên cần thử làcmdncmdnps

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

Các execlệnh ở đó để ngăn chặn quá trình shell thêm từ kéo dài.

Nhưng, có một vấn đề. Cách xử lý của shell SHLVLcó phần trực quan: Khi shell khởi động, nó sẽ kiểm tra xem đã SHLVLđược đặt chưa. Nếu nó không được đặt (hoặc được đặt thành một số khác với một số), shell sẽ đặt nó thành 1. Nếu nó được đặt (thành một số), shell sẽ thêm 1 vào nó.

Nhưng, theo logic này, nếu bạn nói exec sh, bạn SHLVLnên đi lên. Nhưng đó là điều không mong muốn, bởi vì mức độ vỏ thực sự của bạn đã không tăng lên. Shell xử lý việc này bằng cách trừ đi một từ SHLVL khi bạn thực hiện 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

Vì thế

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

là nước rửa; nó SHLVLchỉ tăng để giảm nó một lần nữa. Bạn cũng có thể chỉ nói vim, mà không có lợi ích của một chức năng.

Lưu ý:
Theo Stéphane Chazelas (người biết tất cả mọi thứ) , một số vỏ đủ thông minh để không làm điều này nếu execnó nằm trong một vỏ con.

Để khắc phục điều này, bạn sẽ làm

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

Sau đó, tôi thấy rằng bạn muốn đếm vimcấp độ độc lập với cấp độ vỏ. Vâng, thủ thuật chính xác tương tự hoạt động (tốt, với một sửa đổi nhỏ):

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

(và vân vân cho vi, viewvv) Các exportlà cần thiết vì VILVLkhông được định nghĩa như là một biến môi trường bằng cách mặc định. Nhưng nó không cần phải là một phần của chức năng; bạn chỉ có thể nói export VILVLnhư một lệnh riêng biệt (trong của bạn .bashrc). Và, như đã thảo luận ở trên, nếu quy trình bổ sung không phải là vấn đề đối với bạn, bạn có thể thực hiện command vimthay vì exec vimvà để SHLVLyên:

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

Sở thích cá nhân:
Bạn có thể muốn đổi tên VILVLthành một cái gì đó như VIM_LEVEL. Khi tôi nhìn vào đỉnh điểm VILVL, mắt tôi đau nhói; họ không thể biết đó là lỗi chính tả của vinyl vinyl hay một chữ số La Mã không đúng.


Nếu bạn đang sử dụng trình bao không hỗ trợ SHLVL(ví dụ: dấu gạch ngang), bạn có thể tự thực hiện nó miễn là trình bao thực hiện tệp khởi động. Chỉ cần làm một cái gì đó như

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

trong .profiletập tin của bạn hoặc áp dụng. (Bạn có thể không nên sử dụng tên này SHLVL, vì điều đó sẽ gây ra sự hỗn loạn nếu bạn bắt đầu sử dụng shell hỗ trợ SHLVL.)


Các câu trả lời khác đã giải quyết vấn đề nhúng (các) giá trị biến môi trường vào dấu nhắc shell của bạn, vì vậy tôi sẽ không lặp lại điều đó, đặc biệt là bạn nói rằng bạn đã biết cách thực hiện.


1
Tôi hơi bối rối khi có rất nhiều câu trả lời đề nghị thực hiện một chương trình thực thi bên ngoài, như ps, hoặc pstreekhi bạn có thể làm điều này với các nội dung shell.
Scott

Câu trả lời này là hoàn hảo. Tôi đã đánh dấu đây là giải pháp (tiếc là nó chưa có nhiều phiếu bầu).
Cầu nguyện

Cách tiếp cận của bạn thật tuyệt vời và bạn chỉ sử dụng các nguyên thủy có nghĩa là bao gồm điều này trong .profile / .shellrc của tôi sẽ không phá vỡ bất cứ điều gì. Tôi kéo chúng trên bất kỳ máy nào tôi làm việc.
Cầu nguyện

1
Lưu ý rằng dashcó mở rộng số học. SHELL_LEVEL=$((SHELL_LEVEL + 1))nên là đủ ngay cả khi $ SHELL_LEVEL trước đây chưa được đặt hoặc để trống. Đó là chỉ khi bạn có để được cầm tay vào vỏ Bourne mà bạn sẽ cần phải dùng đến expr, nhưng sau đó bạn cũng sẽ cần phải thay thế $(...)với `..`. SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
Stéphane Chazelas

2
@Pranay, không có vấn đề gì. Nếu kẻ tấn công có thể tiêm bất kỳ var env tùy ý nào , thì những thứ như PATH / LD_PRELOAD là những lựa chọn rõ ràng hơn, nhưng nếu các biến không có vấn đề được thông qua, như với sudo được cấu hình mà không cần reset_env (và người ta có thể buộc bashtập lệnh đọc ~ / .bashrc làm cho stdin trở thành một socket chẳng hạn), thì điều đó có thể trở thành một vấn đề. Đó là rất nhiều "nếu", nhưng một cái gì đó giữ ở phía sau tâm trí của một người (dữ liệu không được xác nhận trong bối cảnh số học là nguy hiểm)
Stéphane Chazelas

37

Bạn có thể tính bao nhiêu thời gian bạn cần để đi lên cây quy trình cho đến khi bạn tìm thấy một người lãnh đạo phiên. Giống như zshtrên 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
}

Hoặc POSIXly (nhưng kém hiệu quả hơn):

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

Điều đó sẽ cho 0 cho shell được bắt đầu bởi trình giả lập thiết bị đầu cuối hoặc getty của bạn và một cái nữa cho mỗi hậu duệ.

Bạn chỉ cần làm điều đó một lần khi khởi động. Ví dụ với:

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

trong ~/.zshrchoặc tương đương để có nó trong lời nhắc của bạn.

tcshvà một số shell khác ( zsh, và ksh93, ít nhất) duy trì một biến mà chúng tăng khi khởi động (và giảm dần trước khi chạy một lệnh khác với (trừ khi đó là trong một lớp con nếu chúng không bị lỗi (nhưng nhiều)). Điều đó chỉ theo dõi số lượng lồng vỏ mặc dù, không xử lý lồng. Ngoài ra cấp 0 không được đảm bảo là người lãnh đạo phiên.fishbash$SHLVLexecexec


Vâng .. cái này hoặc tương tự. Tôi không muốn viết cái này cho riêng mình và tôi không có ý định viết cái này cho tôi. :(. Tôi đã hy vọng một số tính năng trong vim hoặc shell hoặc một số plugin được duy trì thường xuyên. Tôi đã tìm kiếm nhưng không tìm thấy gì.
Pranay

31

Sử dụng echo $SHLVL. Sử dụng nguyên tắc KISS . Tùy thuộc vào độ phức tạp của chương trình của bạn, điều này có thể là đủ.


2
Hoạt động cho bash, nhưng không cho dash.
agc

SHLVL không giúp tôi. Tôi biết về nó, và nó cũng xuất hiện trong tìm kiếm khi tôi tìm kiếm. :) Có nhiều chi tiết hơn trong câu hỏi.
Ngày

@Pranay Bạn có chắc chắn vim không cung cấp thông tin này?
user2497

@ user2497, tôi có phần. Đây là tiền đề của câu hỏi. Tôi đã tìm kiếm khắp nơi, tôi cũng biết về SHLVL. Tôi muốn -> a) chắc chắn không có điều đó. b) làm điều đó với số lượng phụ thuộc / bảo trì ít nhất.
Cầu nguyện

16

Một giải pháp tiềm năng là nhìn vào đầu ra của pstree. Khi chạy bên trong một cái vỏ được sinh ra từ bên trong vi, một phần của cây cây liệt kê pstreesẽ cho bạn thấy bạn sâu đến mức nào. Ví dụ:

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

Vâng, đó là những gì tôi đề nghị là giải pháp của tôi (trong câu hỏi). Tôi không muốn phân tích pstree mặc dù :(. Điều này tốt cho việc đọc thủ công, tôi đã nghĩ đến việc viết một chương trình để làm điều đó cho tôi và cho tôi biết. Tôi không có xu hướng viết một trình phân tích cú pháp nếu một plugin / công cụ đã làm điều đó :).
Cầu nguyện

11

Biến thể đầu tiên - chỉ độ sâu vỏ.

Giải pháp đơn giản cho bash: thêm .bashrcvào hai dòng tiếp theo (hoặc thay đổi PS1giá trị hiện tại của bạn ):

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

Kết quả:

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

Số ở đầu chuỗi nhắc sẽ biểu thị mức vỏ.

Biến thể thứ hai, với cả cấp độ vim và vỏ lồng nhau.

thêm dòng này vào .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

Kết quả:

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 - cấp độ sâu vim
s: 3 - cấp độ sâu vỏ


Điều này sẽ cho tôi bash lồng. Nó sẽ không cho tôi tổ yến vim. :)
Ngày

@Pranay Kiểm tra giải pháp mới. Nó làm những gì bạn muốn.
MiniMax

Vâng, đây là một giải pháp tốt. Tôi có thể thêm nhiều shell và nó sẽ hoạt động :).
Cầu nguyện

8

Trong câu hỏi bạn đã đề cập phân tích cú pháp pstree. Đây là một cách tương đối đơn giản:

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

Các pstreetùy chọn:

  • -A- Đầu ra ASCII để lọc dễ dàng hơn (trong trường hợp của chúng tôi, mọi lệnh được đi trước `-)
  • -a - cũng hiển thị các đối số lệnh, vì hiệu ứng phụ mỗi lệnh được hiển thị trên một dòng riêng biệt và chúng ta có thể dễ dàng lọc đầu ra bằng cách sử dụng grep
  • -l - không cắt ngắn dòng dài
  • -s- hiển thị cho cha mẹ của quá trình đã chọn
    (tiếc là không được hỗ trợ trong các phiên bản cũ của pstree)
  • $$ - quá trình được chọn - PID của shell hiện tại

Vâng, tôi đã làm điều này khá nhiều. Tôi cũng có vài thứ để đếm "bash" và "vim", v.v. Tôi chỉ không muốn duy trì nó. Cũng không khả thi khi có nhiều chức năng tùy chỉnh khi bạn phải chuyển qua nhiều VM và đôi khi phát triển chúng.
Cầu nguyện

3

Điều này không trả lời đúng câu hỏi nhưng trong nhiều trường hợp có thể khiến bạn không cần thiết phải làm như vậy:

Khi bạn lần đầu tiên khởi động vỏ của bạn, chạy set -o ignoreeof. Đừng đặt nó trong của bạn ~/.bashrc.

Tạo thói quen gõ Ctrl-D khi bạn nghĩ rằng bạn đang ở trong lớp vỏ cao nhất và muốn chắc chắn.

Nếu bạn không ở trình bao cấp cao nhất, Ctrl-D sẽ báo hiệu "kết thúc đầu vào" cho trình bao hiện tại và bạn sẽ giảm trở lại một cấp.

Nếu bạn đang trong vỏ cấp cao nhất, bạn sẽ nhận được một thông điệp:

Use "logout" to leave the shell.

Tôi sử dụng tất cả thời gian cho các phiên SSH được xâu chuỗi, để giúp dễ dàng quay trở lại một cấp độ cụ thể của chuỗi SSH. Nó hoạt động cho vỏ lồng nhau là tốt.


1
Điều này chắc chắn sẽ giúp và có nó sẽ loại bỏ rất nhiều biến chứng :). Tôi chỉ có thể kết hợp điều này với câu trả lời được chấp nhận :)). Đặt điều kiện, vì vậy tôi có thể không phải nhìn vào lời nhắc của mình mọi lúc.
Cầu nguyện
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.