我有多少深弹?


73

问题:查找我有多少壳深。

详细信息:我从vim打开了很多外壳。构建并运行并退出。有时我忘记了,打开另一个Vim,然后打开另一个shell。:(

我想知道我有多少深的贝壳,也许一直都在我的贝壳屏幕上。(我可以管理这部分)。

我的解决方案:解析进程树并查找vim和bash / zsh并找出其中的当前进程深度。

这样的东西已经存在了吗?我什么都找不到。


27
$SHLVL您要查找的变量(由多个shell维护)是什么?
斯特凡Chazelas

1
需要澄清的是,您对SHLVL指示的多少个(直接嵌套的)shell并不是很感兴趣,但是您当前的shell是否是vim的后代?
杰夫·谢勒

14
这似乎是一个XY问题-我的工作流程是^ Z,它从vim实例逃逸到父shell中并fg返回,而没有这个问题。
门把手

2
@Doorknob,我也是。但我更喜欢这一点,因为那样的话我就必须继续检查“工作”。而且有时我的机器上可能有很多运行。现在,将TMUX与等式相加。它变得复杂而泛滥。如果我在vim内生成shell,那就不会那么分散了。(但是我最终弄得一团糟,因此产生了问题)。
Pranay

3
@Doorknob:在所有应得的尊重下,这似乎是在回答“我要如何从B点从A点开车?”的问题,并建议“不要开车;不要开车;不要开车。如果用户的工作流程涉及同时编辑多个文件,那么与并行vim嵌套流程堆栈相比,拥有多个并行停止的作业可能会更加混乱。顺便说一句,我更喜欢具有多个窗口,因此我可以轻松地快速来回跳转,但是我不会因为仅仅喜欢其他工作流程而将其称为XY问题。
斯科特(Scott)

Answers:


45

当我读到你的问题时,我的第一个念头是$SHLVL。然后,我看到你想算vim的水平 ,除了外壳的水平。一种简单的方法是定义一个shell函数:

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

SHLVL 每次键入vim命令时,它都会自动无提示地增加。您将需要对所使用的vi/的每个变体进行此操作vim;例如,

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

括号的外部集会创建一个子外壳,因此手动更改其值SHLVL 不会污染当前(父)外壳环境。当然,存在command关键字可以防止函数调用自身(这将导致无限递归循环)。当然,您应该将这些定义放入您的.bashrc或其他Shell初始化文件中。


上面的代码效率低下。如果您说,在某些壳中(bash是一个)

cmd 1 ;  cmd 2 ;;  cmd n

在外部可执行程序(即不是内置命令)的地方,shell保留了一个额外的进程,只是为了等待终止。(可以说)这是没有必要的;优点和缺点是有争议的。如果您不介意占用一点内存和一个进程插槽(并在执行时看到一个比您需要的shell进程更多的进程),请执行上述操作并跳到下一部分。如果您使用的Shell不能保持多余的进程不变,则同上。但是,如果您想避免多余的过程,首先要尝试的是cmdncmdnps

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

exec命令可以防止多余的Shell进程留下来。

但是,有一个陷阱。Shell的处理SHLVL有些直观:Shell启动时,会检查是否SHLVL设置了。如果未设置(或设置为数字以外的值),则外壳程序将其设置为1。如果将其设置为(设置为数字),则外壳程序将其加1。

但是,按照这种逻辑,如果您说exec sh,您SHLVL应该上升。但这是不可取的,因为您的实际外壳级别没有增加。外壳由处理这一减去一个 SHLVL 当你这样做的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

所以

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

是洗;它SHLVL只会递增以再次递减。您可能只是说而已vim,而没有功能的好处。

注意:
根据StéphaneChazelas(谁都知道)的说法,如果shell在子shell中,那么一些shell足够聪明以至于无法执行此操作exec

要解决此问题,您需要

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

然后,我看到你想算vim水平 独立的外壳水平。好吧,完全相同的技巧也起作用(好,做了一些小修改):

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

(等对viview等)的export,因为有必要VILVL不被定义为默认的环境变量。但这并不需要成为功能的一部分。您可以说export VILVL成是单独的命令(在中.bashrc)。而且,如上所述,如果多余的shell程序对您来说不是问题,则您可以command vim代替exec vim,而SHLVL不必理会:

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

个人偏好:
您可能想重命名VILVLVIM_LEVEL。当我看着“ VILVL”时,我的眼睛受伤了。他们无法分辨这是“乙烯基”的拼写错误还是罗马数字格式错误。


如果您使用的外壳程序不支持SHLVL(例如破折号),则只要外壳程序实现启动文件,就可以自己实现。只是做类似的事情

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

在您.profile或适用的文件中。(您可能不应该使用该名称SHLVL,因为如果您开始使用支持的外壳,这将导致混乱SHLVL。)


其他答案已经解决了将环境变量值嵌入到您的shell提示中的问题,因此我不再重复,特别是您说您已经知道如何做。


1
当您可以使用shell内置函数执行外部可执行程序(例如pspstree)时,有这么多答案建议执行该命令,我对此感到有些困惑。
斯科特(Scott)

这个答案是完美的。我已将其标记为解决方案(不幸的是,它还没有那么多选票)。
Pranay

您的方法是惊人的,并且仅使用原语,这意味着将其包含在我的.profile / .shellrc中不会破坏任何内容。我将它们拉到我工作的任何计算机上。
Pranay

1
注意,dash具有算术扩展。SHELL_LEVEL=$((SHELL_LEVEL + 1))即使$ SHELL_LEVEL先前未设置或为空也应足够。这只是如果你有可以移植到Bourne shell的,你就需要诉诸expr,但是你想也需要更换$(...)`..`SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
斯特凡Chazelas

2
@Pranay,这不太可能是一个问题。如果攻击者可以注入任何随意的环境变量,然后之类的东西PATH / LD_PRELOAD较为明显的选择,但如果没有问题的变量打通,就像使用sudo没有reset_env配置(以及一个可以强制bash脚本读取的〜/ .bashrc例如,将stdin设为套接字),则可能会成为问题。这是一个很大的“如果” S的事,但保持在一个人的脑海里(在算术方面unsanitized数据是危险的)
斯特凡Chazelas

37

在找到会话负责人之前,您可能需要花费很多时间才能上到流程树。像zsh在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
}

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

这将为您的终端仿真器或getty启动的外壳提供0,为每个后代提供一个。

您只需在启动时执行一次。例如:

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

在您~/.zshrc的提示下显示出来。

tcsh和其他几发炮弹(zshksh93fishbash至少)保持$SHLVL他们在启动时增加变量(运行另一个命令之前和递减exec(除非exec是在子shell,如果他们不是越野车(但很多)))。但这仅跟踪外壳嵌套的数量,而不跟踪进程嵌套。同样,不能保证级别0是会话领导者。


是的..这个或类似。我不想自己写这个,也不打算让任何人为我写这个。:(。我希望可以在vim或shell中获得某些功能,或者可以定期维护一些插件。我进行了搜索,但没有找到任何东西
。– Pranay

31

使用echo $SHLVL。使用KISS原则。根据您程序的复杂程度,这可能就足够了。


2
适用于bash,但不适用于dash
agc

SHLVL对我没有帮助。我知道它,当我搜索时它也出现在搜索中。:)问题中有更多详细信息。
Pranay

@Pranay您确定vim本身不提供此信息吗?
user2497

@ user2497,我有点。这是问题的前提。我到处搜索,我也知道SHLVL。我想要-> a)确定没有这种东西。b)做到最少的依赖/维护。
Pranay

16

一种可能的解决方案是查看的输出pstree。在内部生成的外壳中运行时vi,列出的树的一部分pstree应显示出您的深度。例如:

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

是的,这就是我建议的解决方案(在问题中)。我不希望解析pstree :(。这对于手动读取它很有用,我正在考虑编写一个程序为我做它,并让我知道。如果插件/,我不太愿意编写解析器。工具已经做到了:)。
Pranay

11

第一个变体-仅外壳深度。

一个简单的解决方案bash:添加到.bashrc下两行(或更改您的当前PS1值):

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

结果:

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

提示字符串开头的数字将表示外壳程序级别。

第二种变体,同时具有嵌套的vim和shell级别。

将此行添加到 .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-vim深度级别
s:3-外壳深度级别


这将使我重返巢穴。它不会给我vim嵌套。:)
Pranay

@Pranay检查新解决方案。它在做什么你想要的。
MiniMax

是的,这是一个很好的解决方案。我可以添加更多的外壳,它将起作用:)。
Pranay

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
  • $$ -所选过程-当前外壳的PID

是的,我几乎在做这个。我也有一些要数“ bash”和“ vim”等。我只是不想保留它。当您必须切换大量VM并有时在其上进行开发时,拥有大量自定义功能也是不可行的。
Pranay

3

这不能严格回答问题,但是在许多情况下可能没有必要这样做:

首次启动Shell时,运行set -o ignoreeof不要把它放在你的~/.bashrc

当您认为自己在顶级外壳程序中并希望确定时,养成键入Ctrl-D的习惯。

如果您不在顶层外壳中,则Ctrl-D将向当前外壳发出“输入结束”的信号,并且您将退回上一层。

如果你在顶级的外壳,你会得到一个消息:

Use "logout" to leave the shell.

一直将其用于链接的SSH会话,以便轻松地回退到SSH链的特定级别。它也适用于嵌套壳。


1
这绝对有帮助,是的,它将消除很多复杂的问题:)。我可以将其与可接受的答案结合起来:))。有条件设置,因此我可能不必一直查看提示。
Pranay
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.