問題:私の深さを数えます。
詳細:vimからシェルをたくさん開きます。ビルドして実行し、終了します。時々私は忘れて、別のvimを開いてから、さらに別のシェルを開きます。 :(
私はいくつのシェルの深さを知りたいと思っています。おそらく、シェルの画面に常に表示されていることでしょう。 (私はその部分を管理できます)。
私の解決策:プロセスツリーを解析してvimとbash/zshを探し、現在のプロセスの深さを把握します。
そのようなものはすでに存在していますか?何も見つかりませんでした。
あなたの質問を読んだとき、私の最初の考えは$SHLVL
でした。次に、vim
レベルに加えてシェルレベルをカウントする必要があることを確認しました。これを行う簡単な方法は、シェル関数を定義することです。
vim() { ( ((SHLVL++)); command vim "$@");}
これにより、SHLVL
コマンドを入力するたびに、自動的にvim
が自動的にインクリメントされます。これは、これまでに使用したvi
/vim
のバリアントごとに行う必要があります。例えば。、
vi() { ( ((SHLVL++)); command vi "$@");}
view() { ( ((SHLVL++)); command view "$@");}
括弧の外側のセットはサブシェルを作成するため、SHLVL
の値を手動で変更しても、現在の(親)シェル環境が汚染されることはありません。もちろん、command
キーワードは、関数が自分自身を呼び出すのを防ぐためにあります(無限再帰ループになります)。そしてもちろん、これらの定義を.bashrc
またはその他のシェル初期化ファイルに挿入する必要があります。
上記にはわずかな非効率性があります。一部のシェル(bashは1つ)では、
(cmd1;cmd2; …;cmdん)
ここで、cmdn
は外部の実行可能プログラム(つまり、組み込みコマンドではありません)であり、cmdn
が終了するのを待つだけのために、シェルは余分なプロセスを保持します。これは(おそらく)不要です。長所と短所は議論の余地があります。少しのメモリとプロセススロットを占有することを気にしない場合(およびps
を実行するときに必要以上のシェルプロセスを表示する場合)、上記を実行して、次のセクションにスキップしてください。追加のプロセスを維持しないシェルを使用している場合は、同上。ただし、余分なプロセスを回避したい場合は、最初に試すことです
vim() { ( ((SHLVL++)); exec vim "$@");}
exec
コマンドは、余分なシェルプロセスが残るのを防ぐためにあります。
しかし、落とし穴があります。シェルのSHLVL
の処理は少し直感的です。シェルが起動すると、SHLVL
が設定されているかどうかを確認します。設定されていない場合(または数値以外に設定されている場合)、シェルはそれを1に設定します。設定されている場合(数値に設定されている場合)、シェルは1を追加します。
しかし、このロジックにより、exec sh
と言うと、SHLVL
が上がるはずです。しかし、それは望ましくありません。実際のシェルレベルが増加していないからです。シェルはこれを1を引くfromSHLVL
で処理します。 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によれば 、いくつかのシェルは十分賢いnotexec
がサブシェルにある場合にこれを行うには。
これを修正するには、次のようにします
vim() { ( ((SHLVL+=2)); exec vim "$@");}
次に、vim
レベル独立してシェルレベルをカウントする必要があることを確認しました。まあ、まったく同じトリックが機能します(まあ、マイナーな変更を加えます):
vim() { ( ((SHLVL++, VILVL++)); export VILVL; exec vim "$@");}
(vi
、view
などについても同様です)。export
はデフォルトでは環境変数として定義されていないため、VILVL
が必要です。ただし、関数の一部である必要はありません。 export VILVL
を別のコマンドとして(.bashrc
で)言うことができます。また、上記のように、追加のシェルプロセスが問題でない場合は、command vim
の代わりにexec vim
を実行し、SHLVL
をそのままにすることができます。
vim() { ( ((VILVL++)); command vim "$@");}
個人の好み:
VILVL
をVIM_LEVEL
のような名前に変更したい場合があります。 「VILVL
」を見ると目が痛い。 「ビニール」のつづりが間違っているのか、ローマ数字の形式に誤りがあるのかはわかりません。
SHLVL
(ダッシュなど)をサポートしないシェルを使用している場合は、シェルがスタートアップファイルを実装している限り、自分で実装できます。ちょうど何かをする
if [ "$Shell_LEVEL" = "" ]
then
Shell_LEVEL=1
else
Shell_LEVEL=$(expr "$Shell_LEVEL" + 1)
fi
export Shell_LEVEL
.profile
または該当するファイル内。 (SHLVL
をサポートするシェルの使用を開始すると混乱を招くため、SHLVL
という名前は使用しないでください。)
他の回答では、環境変数値をシェルプロンプトに埋め込む問題に対処しているので、繰り返しはしません。特に、方法をすでに知っているとおっしゃっています。
セッションリーダーが見つかるまで、プロセスツリーを上るのに必要な時間を数えることができます。 Linuxのzsh
と同様:
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が、子孫にさらに1つ追加されます。
起動時に一度だけ実行する必要があります。たとえば:
PS1="[$(lvl)]$PS1"
あなたの~/.zshrc
または同等のものをプロンプトに表示します。
tcsh
と他のいくつかのシェル(zsh
、ksh93
、fish
およびbash
少なくとも)$SHLVL
変数は、起動時にインクリメントします(そして、exec
で別のコマンドを実行する前にデクリメントします(バグがなければ(ただし、多くの場合)exec
がサブシェルにある場合を除きます)))。ただし、Shellネストの量のみを追跡し、プロセスネストは追跡しません。また、レベル0がセッションリーダーであるとは限りません。
使用する echo $SHLVL
。 KISS原則 を使用します。プログラムの複雑さによっては、これで十分な場合があります。
解決策の1つは、pstree
の出力を確認することです。 vi
内から生成されたシェル内で実行すると、ツリーツリーのpstree
をリストする部分が、自分の深さを示しているはずです。例えば:
$ pstree <my-user-ID>
...
├─gnome-terminal-─┬─bash───vi───sh───vi───sh───pstree
...
bash
の簡単な解決策:.bashrc
の次の2行に追加(または現在のPS1
の値を変更):
PS1="${SHLVL} \w\$ "
export PS1
結果:
1 ~$ bash
2 ~$ bash
3 ~$ exit
exit
2 ~$ exit
exit
1 ~$
プロンプト文字列の先頭の数字はシェルレベルを示します。
この行を.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-シェルの深さレベル
質問では、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これは厳密には質問に答えませんが、多くの場合、そうする必要はないかもしれません:
シェルを初めて起動するときに、set -o ignoreeof
を実行します。 しないでください~/.bashrc
に入れてください。
最上位のシェルを使用していることを確認し、確認したい場合は、Ctrl-Dを入力することを習慣にしてください。
最上位のシェルでではない場合、Ctrl-Dは現在のシェルに「入力の終了」を通知し、1レベル下に戻ります。
トップレベルのシェルでareの場合、次のメッセージが表示されます。
Use "logout" to leave the Shell.
私はこれを常にチェーンSSHセッションに使用して、SSHチェーンの特定のレベルに簡単に戻すことができるようにしています。ネストされたシェルでも同様に機能します。