问题:找出我有多少贝壳深。
细节: 我经常从 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 不保留额外的进程,则同上。但是,如果您想避免额外的过程,首先要尝试的是cmdn
cmdn
ps
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 vim
SHLVL
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(zsh
、ksh93
、fish
至少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 深度级别