ロードされた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を使用しないでください」という解決策はありません。

  • 誤って設定された環境でも、コードは強力でなければなりません。
  • これはデフォルトで発生します.bashrcUbuntuが提供するもの(これは旧バージョンです)

実際、この行内でエラーが発生しました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 $?'])

ゼロ以外の終了コードが印刷されます。また、このようなシェル固有のコードに依存することを避けたいと思います。

呼び出し時に正しくロードされるようにするにはどうすればよいですか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実際には標準入力から追加のコマンドを取得しなくても、次の2つのコマンドはまったく同じ動作をします。

 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スクリプトが端末で実行されていない場合は、ユーザーと対話しようとせず、対話型シェルを起動しないでください。

関連情報