나는 얼마나 많은 조개 껍질입니까?


73

문제점 : 내가 얼마나 많은 조개 껍질을 찾으십시오.

세부 정보 : vim에서 쉘을 많이 엽니 다. 빌드하고 실행하고 종료하십시오. 때때로 나는 다른 vim을 잊어 버린 다음 또 다른 쉘을 엽니 다. :(

나는 얼마나 많은 껍질을 가지고 있는지 알고 싶습니다. 아마도 항상 껍질 화면에 있습니다. (나는 그 부분을 관리 할 수있다).

내 솔루션 : 프로세스 트리를 구문 분석하고 vim 및 bash / zsh를 찾아 현재 프로세스의 깊이를 파악하십시오.

이와 같은 것이 이미 존재합니까? 나는 아무것도 찾을 수 없었다.


27
$SHLVL찾고 있는 변수 (여러 쉘에서 유지 관리)입니까?
Stéphane Chazelas

1
명확히하기 위해 SHLVL로 표시된 얼마나 많은 (직접 중첩 된) 쉘에 관심이 없지만 현재 쉘이 vim의 자손인지 여부에 관심이 있습니까?
Jeff Schaller

14
이것은 약간의 XY 문제 인 것처럼 보입니다. 제 워크 플로우는 ^ Z로 vim 인스턴스에서 상위 쉘로 빠져 나와서 fg다시 돌아 오는 것입니다.이 문제는 없습니다.
Doorknob

2
@Doorknob, 나도 그래. 하지만 "작업"을 계속 확인해야하기 때문에이 방법을 선호합니다. 내 컴퓨터에는 때때로 많은 실행이있을 수 있습니다. 이제 방정식과 함께 TMUX를 추가하십시오. 복잡하고 넘쳐납니다. vim 내부에 쉘을 생성하면 분산이 적습니다. (그러나 나는 엉망이되어 질문을합니다.)
Pranay 2016 년

3
@Doorknob : 모든면에서, "B 지점에서 A 지점에서 어떻게 운전합니까?"라는 질문에 대한 답변, "운전하지 마십시오; 사용자에게 여러 파일을 동시에 편집하는 워크 플로우가있는 경우 여러 병렬 중지 된 vim작업이 중첩 된 프로세스 스택보다 더 혼란 스러울 수 있습니다. 나는 여러 개의 창을 선호 하므로 쉽게 빠르게 앞뒤로 이동할 수 있지만 다른 워크 플로를 선호하기 때문에 XY 문제라고 부르지는 않습니다.
Scott

답변:


45

당신의 질문을 읽을 때, 나의 첫 생각은 $SHLVL입니다. 그런 다음 쉘 레벨 외에도vim 레벨 을 계산하려고한다는 것을 알았습니다 . 이를 수행하는 간단한 방법은 쉘 함수를 정의하는 것입니다.

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

명령 SHLVL 을 입력 할 때마다 자동으로 자동 증가 합니다 vim. 지금까지 사용했던 vi/의 각 변형에 대해이 작업을 수행해야 vim합니다. 예를 들어

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

외부 괄호 세트는 서브 쉘을 작성하므로 값을 수동으로 변경해도 SHLVL 현재 (상위) 쉘 환경이 오염되지 않습니다. 물론 command키워드는 함수가 자신을 호출하지 못하게하기 위해 있습니다 (무한한 재귀 루프가 발생합니다). 물론 이러한 정의를 사용자 .bashrc또는 다른 쉘 초기화 파일에 넣어야 합니다.


위의 내용에는 약간의 비 효율성이 있습니다. 당신이 말하면 일부 껍질 (bash는 하나임)

( cmd 1 ;  cmd 2 ;;  cmd n )

여기서 외부, 실행 프로그램 (즉,하지 내장 명령), 쉘 그냥 기다릴, 주위에 거짓말을 별도의 프로세스를 유지하다 종료하는. 이것은 (반드시) 필요하지 않습니다. 장점과 단점은 논쟁의 여지가 있습니다. 약간의 메모리와 프로세스 슬롯을 묶지 않고 (및 할 때 필요한 것보다 하나 이상의 쉘 프로세스를 보려는 경우 ) 위의 작업을 수행하고 다음 섹션으로 건너 뜁니다. 여분의 프로세스를 막지 않는 쉘을 사용하는 경우 Ditto. 그러나 추가 프로세스를 피하려면 먼저 시도해보십시오.cmdncmdnps

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

exec명령은 느린에서 여분의 쉘 프로세스를 방지 할 수있다.

그러나 문제가 있습니다. 쉘의 처리 SHLVL는 다소 직관적입니다. 쉘이 시작되면 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éphane Chazelas (모든 것을 아는 사람)에 따르면 일부 쉘은 하위 쉘에있는 경우이 작업을 수행하지 않을 만큼 똑똑 exec합니다.

이 문제를 해결하려면

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


다른 답변은 환경 변수 값을 쉘 프롬프트에 포함시키는 문제를 해결 했으므로 반복하지는 않겠습니다. 특히 이미 그 방법을 알고 있다고 말하고 싶습니다.


1
쉘 내장 으로이 작업을 수행 할 수있을 때 많은 답변이 psor 과 같은 외부 실행 프로그램을 실행하는 것이 좋습니다 pstree.
Scott

이 답변은 완벽합니다. 나는 이것을 해결책으로 표시했습니다 (불행히도 아직 많은 표를 얻지 못했습니다).
Pranay 2016 년

귀하의 접근 방식은 놀랍고 프리미티브 만 사용하고 있습니다.이를 내 .profile / .shellrc에 포함하면 아무것도 깨지지 않습니다. 나는 내가 작업하는 모든 기계에서 그것들을 당깁니다.
Pranay

1
참고 dash산술 확장이있다. SHELL_LEVEL=$((SHELL_LEVEL + 1))$ SHELL_LEVEL이 이전에 설정되지 않았거나 비어 있어도 충분합니다. 당신은 당신이에 의지해야 할 것 Bourne 쉘에 이식했다 경우에만입니다 expr,하지만 당신은 또한 교체해야 할 것 $(...)와 함께 `..`. SHELL_LEVEL=`expr "${SHELL_LEVEL:-0}" + 1`
Stéphane Chazelas

2
@Pranay, 문제가 될 것 같지 않습니다. 공격자가 삽입 할 수있는 경우 어떤 임의의 ENV의 VAR을 다음 PATH / LD_PRELOAD 같은 일이 더 분명 선택하지만, 비 문제가 변수 sudo를 같이 통과하면 reset_env없이 구성 (하나는 강제 bash로 ~ / .bashrc에를 읽을 스크립트를 예를 들어 stdin을 소켓으로 만들면 문제가 될 수 있습니다. 그것은 많은 "if"이지만, 자신의 마음에 뒤 쳐야 할 것입니다 (산술적 맥락에서 비위생 화 된 데이터는 위험합니다)
Stéphane Chazelas

37

세션 리더를 찾을 때까지 프로세스 트리를 올라 가야하는 시간을 계산할 수 있습니다. zshLinux에서 와 같이 :

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여러 다른 쉘 ( zsh, ksh93, fishbash이상)를 유지하기 $SHLVL가 시작할 때 증가 변수 (또 다른 명령을 실행하기 전에 및 감소를 exec(즉,하지 않는 한 exec그들은 버그가 아니에요 (하지만 많은 경우 서브 쉘에))). 그것은 프로세스 중첩이 아니라 중첩 의 양만 추적합니다 . 또한 레벨 0은 세션 리더가 될 수 없습니다.


예 .. 이거 또는 비슷합니다. 나는 이것을 내 자신의 것으로 쓰고 싶지 않았으며, 누군가 나를 위해 이것을 쓰라는 의도는 아니었다. :(. vim 또는 shell 또는 정기적으로 관리되는 플러그인의 일부 기능을 찾고 있었지만 검색했지만 찾지 못했습니다.
Pranay

31

사용하십시오 echo $SHLVL. KISS 원칙을 사용하십시오 . 프로그램의 복잡성에 따라 이것으로 충분할 수 있습니다.


2
작동 bash하지만 작동 하지 않습니다 dash.
agc

SHLVL은 나를 도와주지 않습니다. 나는 그것에 대해 알고 있었고 검색 할 때도 검색에 나타났습니다. :) 질문에 더 자세한 내용이 있습니다.
Pranay 2016 년

@Pranay 특정 vim 자체가이 정보를 제공하지 않습니까?
user2497 2016 년

@ user2497, 나는 다소입니다. 이것이 질문의 전제입니다. 나는 어디에서나 검색했으며 SHLVL도 알고있었습니다. 나는-> a) 그런 것이 없다는 것을 확신하고 싶었다. b) 최소한의 의존성 / 유지 보수로 수행하십시오.
Pranay 2016 년

16

가능한 해결책 중 하나는의 출력을 보는 것입니다 pstree. within 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-쉘 깊이 레벨


이것은 나에게 bash 중첩을 줄 것이다. 그것은 vim 둥지를주지 않을 것입니다. :)
Pranay 2016 년

@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을 전환하고 때로는 VM을 개발해야 할 때 많은 사용자 정의 기능을 갖는 것이 불가능합니다.
Pranay

3

이것은 엄격하게 질문에 대답하지는 않지만 많은 경우에 그렇게하지 않아도 될 수 있습니다.

쉘을 처음 시작할 때을 실행하십시오 set -o ignoreeof. 넣지 마십시오~/.bashrc .

최상위 쉘에 있고 확실하게 생각할 때 Ctrl-D를 입력하는 습관을들이십시오.

최상위 쉘에 있지 않은 경우 Ctrl-D는 "입력 끝"을 현재 쉘에 알리고 한 레벨을 취소합니다.

당신이 경우 이다 최상위 쉘에서, 당신은 메시지가 표시됩니다 :

Use "logout" to leave the shell.

체인 된 SSH 세션에 항상 사용 하여 특정 수준의 SSH 체인으로 쉽게 되돌릴 수 있습니다. 중첩 쉘에서도 작동합니다.


1
이것은 확실히 도움이되고 그렇습니다. 많은 합병증을 제거 할 것입니다 :). 나는 이것을 허용 된 대답과 결합 할 수 있습니다 :)). 조건부로 설정되었으므로 항상 내 프롬프트를 볼 필요가 없습니다.
Pranay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.