我有多少贝壳深?

我有多少贝壳深?

问题:找出我有多少贝壳深。

细节: 我经常从 vim 打开 shell。构建并运行并退出。有时我会忘记打开另一个 vim,然后打开另一个 shell。 :(

我想知道我的 shell 深度有多少,甚至可能一直在我的 shell 屏幕上。 (我可以管理那部分)。

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

类似的东西已经存在了吗?我找不到任何东西。

答案1

当我读到你的问题时,我的第一个想法是$SHLVL。然后我看到你想要计算vim等级 此外外壳级别。一个简单的方法是定义一个 shell 函数:

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

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

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

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


上面有一点效率低下。在某些 shell 中(bash 就是其中之一),如果你说

(命令1 命令2…… 命令n

其中是外部可执行程序(即不是内置命令),shell 会保留一个额外的进程,只是为了等待终止。这(可以说)是没有必要的;其优点和缺点值得商榷。如果您不介意占用一点内存和一个进程槽(并且在执行 a 时看到比您需要的多一个 shell 进程),那么请执行上述操作并跳到下一节。如果您使用的 shell 不保留额外的进程,则同上。但是,如果您想避免额外的过程,首先要尝试的是cmdncmdnps

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

exec命令的作用是防止额外的 shell 进程徘徊。

但是,有一个问题。 shell 的处理SHLVL有点直观:当 shell 启动时,它检查是否SHLVL已设置。如果未设置(或设置为数字以外的其他值),则 shell 将其设置为 1。如果设置了(设置为数字),则 shell 将其加 1。

但是,按照这个逻辑,如果你说exec sh,你SHLVL应该上涨。但这是不可取的,因为你的真实外壳等级并没有增加。 shell 通过以下方式处理这个问题减一 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éphane Chazelas)(他无所不知)的说法, 有些 shell 足够聪明不是exec如果位于子 shell 中,则执行此操作。

要解决这个问题,你可以这样做

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

然后我看到你想要计算vim等级 独立于外壳级别。好吧,完全相同的技巧也有效(好吧,只需稍加修改):

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

vi(对于、等,依此view类推)export是必需的,因为VILVL默认情况下没有定义为环境变量。但它不需要是函数的一部分;你可以只说export VILVL作为一个单独的命令(在你的.bashrc)。而且,如上所述,如果额外的 shell 进程对您来说不是问题,您可以执行, 而command vim不用管它:exec vimSHLVL

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

个人喜好:
您可能想重命名VILVL为类似的名称VIM_LEVEL。当我看“ VILVL”时,我的眼睛很痛;他们无法判断这是“vinyl”的拼写错误还是罗马数字的错误。


如果您使用的 shell 不支持SHLVL(例如 dash),您可以自己实现,只要 shell 实现启动文件即可。只需做类似的事情

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

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


其他答案已经解决了将环境变量值嵌入到 shell 提示符中的问题,所以我不会重复,特别是你说你已经知道如何做到这一点。

答案2

您可以计算沿进程树向上查找所需的次数,直到找到会话领导者。就像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 启动的 shell 提供 0,并为每个后代提供 1。

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

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

在您的~/.zshrc或等效的内容中将其包含在您的提示中。

tcsh和其他几个 shell(zshksh93fish至少bash)维护一个$SHLVL变量,它们在启动时递增(并在运行另一个命令之前递减exec(除非exec在子 shell 中,如果它们没有错误(但很多都是)))。这只追踪数量但嵌套,而不是进程嵌套。另外,级别 0 不保证是会话领导者。

答案3

使用echo $SHLVL。使用亲吻原则。根据程序的复杂性,这可能就足够了。

答案4

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

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

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

结果:

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

提示字符串开头的数字将表示 shell 级别。

第二种变体,同时具有嵌套的 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 - shell 深度级别

相关内容