로드된 rcfile을 사용하여 하위 쉘 명령 호출

로드된 rcfile을 사용하여 하위 쉘 명령 호출

이 작업을 수행하는 Python 코드가 있습니다. (간소화된 버전입니다.)

shell = os.environ['SHELL']
os.environ['PATH'] = ... # some new PATH value
if call(shell, '-c', 'program_that_checks_that_PATH_is_sane'): # hasn't been mangled by the rcfile
    sys.exit('fix your .bashrc|.zshrc|.config/fish')
call(shell) # drops the user in a shell

나는 어리석게도 이것이 $SHELL -c내 목적에 충분할 것이라고 생각했지만 최근 PATH 검사가 bash에서 작동하지 않는다는 것을 발견했습니다.

좀 보려고 하는데제안된 솔루션하지만 bash는 예상했던 것보다 훨씬 더 취약합니다.

기본적으로 소스 파일이 특정 오류를 유발할 수 있으면 BASH_ENV수동 소싱이 작동하지 않는다는 것을 알았습니다.bash -c "source ~/.bashrc; do_your_stuff"

bash가 대화형으로 호출되는 경우 동일한 오류는 문제가 되지 않습니다(즉, bash -i오류는 무시되고 bash -c자동으로 실패합니다). bash -i하지만실행 가능한 솔루션이 아님(이것은 bash -istdin이 "도용"되어 Python 프로세스 호출이 중지되었기 때문인 것 같습니다)

단순히 "손상된 ~/.bashrc를 사용하지 마십시오"라는 해결책도 없습니다.

  • 잘못 구성된 환경에서도 코드는 강력해야 합니다.
  • 이는 기본적으로 발생합니다 .bashrc.우분투에서 제공하는 것(이건 예전 버전입니다)

실제로 이 줄 내에서 오류가 발생했습니다 set -xe(소싱 전에 발견됨). (이것이 어떻게 실패할 수 있는지, 소싱 프로세스가 제 능력을 넘어서는 것입니다.)/usr/share/bash-completion/bash_completion[[ -f /etc/slackware-version ]] && sysvdirs=( /etc/rc.d )

많은 조정 끝에 소싱할 때 이러한 오류를 감지하는 방법을 알아냈습니다.

set -e && . ~/.bashrc & wait %% ; echo $?

이상하게도 다음은 실패합니다.

set -e ; . ~/.bashrc & wait %% ; echo $?

그래도 그것도 아니기 때문에 충분하지 않습니다.

bash -c "set -e && . ~/.bashrc & wait %% ; echo \$?"
call(['bash', '-c', 'set -e && . ~/.bashrc & wait %% ; echo $?'])

0이 아닌 종료 코드가 인쇄됩니다. 또한 이와 같은 쉘 특정 코드에 의존하는 것을 피하고 싶습니다.

호출 시 올바르게 로드되도록 하려면 어떻게 해야 합니까 bash -c?.bashrc

전체 문제에 대한 대안으로 PATH 문제를 인식하도록 .{bash,zsh}rc를 수정하는 것을 고려하고 있습니다. 이는 좋지 않은 솔루션이지만 90%의 사례를 다루고 추가 프로세스를 포크하는 것을 방지합니다.

import re
r = '^export PATH\s?=\s?([^:]+:)+(\$PATH|\${PATH})'
g = re.match(r, 'export PATH=/usr/bin:$PATH').groups()
any(check_collision(x[:-1]) for x in g[:-1])

편집하다:

bash -i플래그는 가장 간단하고 가장 유망한 세미 솔루션인 것처럼 보이지만 그 동작은 명확하지 않습니다. 몇 가지 예는 이해하는 데 도움이 될 수 있습니다.

이것은 ls를 실행하고 Python이 실행되는 Python 프롬프트로 이동합니다.

python3 -ic "import os; os.system(\"bash -c ls\")"

그러면 Python 프로세스가 중지됩니다.

python3 -ic "import os; os.system(\"bash -ic ls\")"

자식 프로세스에 다른 표준 입력을 제공하는 것만으로는 충분하지 않습니다.

python3 -ic "import subprocess;f=open('/dev/null');subprocess.call(['bash', '-ic', 'ls'], stdin=f)"

마지막으로 bash -ic실제로 표준 입력에서 추가 명령을 얻지 않고도 다음 두 명령은 정확히 동일한 동작을 갖습니다.

 echo echo sed | bash -c "sed 's/[^ ]*$/bash/'"
 echo echo sed | bash -ic "sed 's/[^ ]*$/bash/'"

답변1

SHELL스크립트에 환경 변수를 사용 하지 마세요 . 이는 사용자가 선호하는 대화형 셸입니다. 당신은 그것이 지원하는 구문에 대해 아무것도 모릅니다. tcsh, zsh, fish, rc 등이 ​​될 수 있습니다.

.bashrc스크립트에서 로드 하지 마세요 . 사용자는 터미널과 상호 작용하고 터미널을 갖기 위해 bash에 의존하는 무언가를 넣을 수 있습니다. 터미널이 정지되거나 터미널을 재구성하거나 스크립트 출력을 숨기는 등 여러 가지 나쁜 일이 발생할 수 있습니다. 을 실행하면 bash -c …로드 .bashrc되지 않습니다. 이는 의도적으로 설계된 것입니다.

괜찮은지 확인해야 하는 경우 PATH셸을 호출하지 않고 Python에서 이를 수행할 수 있습니다. "코드는 잘못 구성된 환경에서도 견고하게 유지되어야 합니다"라는 말은 Python 스크립트가 대화형 셸에서 시작되지 않았더라도 대화형 셸의 PATH를 복사하려고 시도해야 한다는 의미입니다. 그러면 그렇게 하지 마십시오. 심지어 가깝다. 사용자를 돕기보다는 이상한 방법(특히 사용자의 명시적 설정 무시)으로 문제를 일으킬 가능성이 더 높습니다.

어떤 이유로 스크립트가 사용자를 대화형 셸에 넣으면 run이 실행되거나 os.environ['SHELL']환경 변수가 설정되지 않은 경우 대체됩니다. 스크립트가 터미널에서 실행되는 경우 대화형 셸이 실행되므로 예상한 결과를 의미할 수도 있고 그렇지 않을 수도 있는 pwd.getpwuid(os.geteuid()).pw_shell추측이나 기타 옵션을 시도할 필요가 없습니다 . -i스크립트가 터미널에서 실행되고 있지 않으면 사용자와 상호 작용을 시도하지 않으며 대화형 셸을 시작해서는 안 됩니다.

관련 정보