서브 쉘에 있는지 어떻게 알 수 있습니까?


24

exit터미널에서 나가지 않도록 내장 기능을 대체하는 함수를 작성하려고합니다 .

SHLVL환경 변수 를 사용하려고 시도했지만 하위 쉘 내에서 변경되지 않는 것 같습니다.

$ echo $SHLVL
1
$ ( echo $SHLVL )
1
$ bash -c 'echo $SHLVL'
2

내 기능은 다음과 같습니다.

exit () {
    if [[ $SHLVL -eq 1 ]]; then
        printf '%s\n' "Nice try!" >&2
    else
        command exit
    fi
}

exit그래도 하위 쉘 내 에서 사용할 수는 없습니다 .

$ exit
Nice try!
$ (exit)
Nice try!

서브 쉘에 있는지 여부를 감지하는 좋은 방법은 무엇입니까?



1
그 때문이다 . 때문에 $ SHLVL은 1 당신이 에코 $ SHLVL 명령은 "서브 쉘"에서 실행 되더라도 쉘 레벨 1에 여전히. 이 게시물에 따르면 괄호로 생성 된 (...)하위 셸은 상위 프로세스의 모든 속성을 상속합니다. 제공되는 답변은 셸 수준을 결정하는보다 강력한 솔루션입니다.
kemotep


5
@mosvy 나는 그것이 다른 질문 인 것 같아. 예를 들어 BASH_SUBSHELL답은 (논쟁의 여지가 있지만) 해당 질문에 적용되지 않습니다.
Sparhawk

2
HNQ에서 제목을보고 이것이 양자 역학 질문이라고 생각했습니다 ...
Mehrdad

답변:


43

bash에서는 다음과 비교할 $BASHPID수 있습니다.$$

$ ( if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi )
subshell
$   if [ "$$" -eq "$BASHPID" ]; then echo not subshell; else echo subshell; fi
not subshell

배쉬가 아닌 경우 $$, 서브 쉘에서 동일하게 유지되어야하므로 실제 프로세스 ID를 얻는 다른 방법이 필요합니다.

실제 pid를 얻는 한 가지 방법은 sh -c 'echo $PPID'입니다. ( … )껍질을 평평 하게 넣으면 껍질이 포크를 최적화하여 작동하지 않는 것처럼 보일 수 있습니다. ( : ; sh -c 'echo $PPID'; : )서브 쉘이 너무 복잡하여 최적화 할 수 없다고 생각되도록 추가 no-op 명령 을 시도하십시오 . 이 접근법에 대한 크레딧은 스택 오버플 로에서 John1024 에게 전달됩니다 .


당신은에 있음을 변경할 수 있습니다  (sh -c 'echo $PPID'; : )참조 - 내 댓글John1024의 답변을 .
G-Man, 'Reinstate

@ G-Man 글쎄, 그것은 그것을 테스트하는 것이 었습니다 (실제로 사용하는 것이 더 복잡하기 때문에) ... 그렇습니다. 테스트가 모든 쉘에서 작동하면 가장 좋습니다. 그래서 나는 모든 것을 다룰 것입니다 앞뒤에 no-op를 넣었습니다.
derobert

38

어때요 BASH_SUBSHELL?

BASH_SUBSHELL
      쉘
      이 해당 환경에서 실행을 시작할 때 각 서브 쉘 또는 서브 쉘 환경 내에서 하나씩 증가 합니다. 초기 값은 0입니다.

$ echo $BASH_SUBSHELL
0
$ (echo $BASH_SUBSHELL)
1

16
영화 Inception에서 편리한 명령이었을 것입니다.
Eric Duminil

처음에는 아마도 $ SHLVL 일 것입니다
Granny Aching

19

[이것은 의견 이었음에 틀림 없다. 그러나 나의 의견은 중재자에 의해 삭제되는 경향이 있으므로, 삭제 된 경우에도이를 참조로 사용할 수있는 답변으로 유지 될 것이다]

사용은 BASH_SUBSHELL그것이 단지 1로 설정할 수로 완전히 신뢰할 수없는 몇 가지 모든 서브 쉘에서, 서브 쉘.

$ (echo $BASH_SUBSHELL)
1
$ echo $BASH_SUBSHELL | cat
0

파이프 라인 명령이 실행되는 하위 프로세스가 아니라고 주장하기 전에 정말 실제 서브 쉘이이 고려 man bash조각을 :

파이프 라인의 각 명령은 별도의 프로세스 (즉, 서브 쉘)로 실행됩니다.

그리고 실질적인 의미-스크립트 조각이 일부 프로세스 퀴즈가 아닌 하위 프로세스에서 실행되는지의 여부입니다.

질문 에 대한 답변에서 이미 설명한 바와 같이 유일한 해결책 은 $BASHPID동일 $$하거나 이식 가능하지만 효율성이 떨어지는 지 확인하는 것입니다.

if [ "$(exec sh -c 'echo "$PPID"')" != "$$" ]; then
    echo you\'re in a subshell
fi

11
Nit : BASH_SUBSHELL설정이 매우 확실하지만 값을 올바르게 얻는 것이 좋습니다. 무엇을 주 워드 프로세서는 말 : "증가됩니다 각 서브 쉘 또는 서브 쉘 환경에서 하나 . 쉘이 해당 환경에서 실행을 시작할 때이 "나는 변수가 확장 될 때 파이프의 예에서, 배쉬는 아직 서브 쉘에서 실행 시작되지 않았 음을 생각합니다. 당신은 비교할 수 echo $BASH_VERSIONdeclare -p BASH_VERSION- 후자해야 신뢰성 등 파이프, 백그라운드 작업과 출력 1
muru

6
심지어 실행이 시작된 후 변수가 확장되기 때문에에 대해 eval 'echo $BASH_SUBSHELL $BASHPID' | cat1을 출력 BASH_SUBSHELL합니다.
muru

4
이러한 모든 인수는 프로세스 및 명령 대체, bg 프로세스에도 적용되어야하지만 파이프 라인 만 다릅니다. 코드를 살펴보면 포 그라운드 파이프 라인 의 경우 증분이 subshell_level실제로 연기 됩니다. 아마도 이유가 있지만 만들 수는 없습니다. ;-)
mosvy

2
네가 옳아. Chet이 명시 적으로 그렇게 의도 한 것 같습니다. list.gnu.org/archive/html/bug-bash/2015-06/msg00050.html : "BASH_SUBSHELL은 파이프 라인 요소가 아닌 (...) 서브 쉘을 측정합니다." lists.gnu.org/archive/html/bug-bash/2015-06/msg00054.html : "현재 상태를 문서화해야하는지 또는 $ BASH_SUBSHELL이 반영하는`subshell '의 정의를 확장해야하는지 생각할 것입니다. "
muru

2
@JoL 당신이 틀 렸습니다. 확장은 별도의 프로세스에서도 발생합니다. 위의 토론에서 링크와 예제를 읽으십시오. 또는 그냥 사용해보십시오 echo $$ $BASHPID $BASH_SUBSHELL | cat.
mosvy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.