web-dev-qa-db-ja.com

bashrcが現在のシェルがインタラクティブかどうかを確認するのはなぜですか?

Archのインストールでは、/etc/bash.bashrc/etc/skel/.bashrcに次の行が含まれています。

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

Debianでは、/etc/bash.bashrcには以下が含まれます。

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

そして/etc/skel/.bashrc

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

ただし、man bashによると、非インタラクティブシェルはこれらのファイルを読み取ることさえしません。

   When  bash  is  started  non-interactively,  to run a Shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com‐
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file‐
   name.

私が正しく理解していれば、*.bashrcがそれらを指すように設定されている場合にのみ、BASH_ENVファイルが読み取られます。これは偶然起こり得ないことであり、誰かがそれに応じて変数を明示的に設定した場合にのみ発生します。

これは、スクリプトがユーザーの.bashrcBASH_ENVに設定することによって自動的にソースする可能性を壊すようです。これは便利なものです。明示的に指示されない限り、bashが非対話的に実行されたときにこれらのファイルを決して読み取らないことを考えると、なぜデフォルトの*bashrcファイルがそれを許可しないのですか?

64
terdon

これは、数週間前にここに投稿する予定だった質問です。 terdon のように、_.bashrc_はインタラクティブなBashシェル用にのみ供給されるため、_.bashrc_が実行されているかどうかを確認する必要がないはずです。インタラクティブシェル。混乱して、all私が使用するディストリビューション(Ubuntu、RHEL、およびCygwin)には、ある種のチェック(テスト_$-_または_$PS1_)がありました)現在のシェルがインタラクティブであることを確認します。 貨物カルトプログラミング が嫌いなので、このコードの目的を_.bashrc_で理解することにしました。

Bashにはリモートシェル用の特別なケースがあります

この問題を調査した結果、リモートシェルの扱いが異なることがわかりました。非インタラクティブなBashシェルは通常、起動時に_~/.bashrc_コマンドを実行しませんが、シェルが リモートシェルデーモンによって呼び出される の場合は特別なケースになります。

Bashは、リモートシェルデーモン(通常はrshd、またはセキュアシェルデーモンsshd)によって実行されるときのように、標準入力がネットワーク接続に接続されて実行されているかどうかを判断しようとします。この方法で実行されているとBashが判断した場合、そのファイルが存在して読み取り可能であれば、〜/ .bashrcからコマンドを読み取って実行します。 shとして呼び出された場合、これは行われません。 _--norc_オプションを使用してこの動作を禁止し、_--rcfile_オプションを使用して別のファイルを強制的に読み取ることができますが、rshdsshdも通常、これらのオプションを使用してシェルを呼び出したり、指定する。

リモート_.bashrc_の先頭に以下を挿入します。 (_.bashrc_が_.profile_または_.bash_profile_から供給されている場合、テスト中はこれを一時的に無効にします):

_echo bashrc
fun()
{
    echo functions work
}
_

次のコマンドをローカルで実行します。

_$ ssh remote_Host 'echo $- $0'
bashrc
hBc bash
_
  • _$-_にiがない場合は、シェルが non-interactive であることを示します。
  • _-_の先頭に_$0_がない場合は、シェルが login Shell ではないことを示します。

リモート_.bashrc_で定義されたシェル関数も実行できます。

_$ ssh remote_Host fun
bashrc
functions work
_

sshの引数としてコマンドが指定されている場合、_~/.bashrc_が only をソースとしていることに気付きました。これは理にかなっています。sshを使用して通常のログインシェルを開始すると、_.profile_または_.bash_profile_が実行されます(および_.bashrc_は、これらのファイルのいずれかによって明示的に実行された場合にのみ供給されます)。

(非インタラクティブ)リモートコマンドを実行するときに_.bashrc_ソースを取得することで得られる主な利点は、シェル関数を実行できることです。ただし、一般的な_.bashrc_のほとんどのコマンドはインタラクティブシェルにのみ関連しています。たとえば、シェルがインタラクティブでない限り、エイリアスは展開されません。

リモートファイル転送が失敗することがある

rshまたはsshを使用してインタラクティブログインシェルを起動する場合、または非インタラクティブシェルを使用してコマンドを実行する場合、これは通常問題ではありません。ただし、データを転送するためにリモートシェルを使用するrcpscpsftpなどのプログラムでは、問題になる可能性があります

scpコマンドを使用すると、リモートユーザーのデフォルトのシェル(Bashなど)が暗黙的に開始されることがわかります。マンページにはこれについての言及はありません。scpがデータ転送にsshを使用しているという言及のみです。これにより、_.bashrc_に標準出力に出力するコマンドが含まれている場合、ファイル転送が失敗します、たとえば scpはエラーなしで失敗します

15年前のこの関連するRed Hatバグレポートも参照してください scpは、/ etc/bashrcにechoコマンドがあると壊れます (最終的にWONTFIXとして閉じられました)。

scpおよびsftpが失敗する理由

SCP(セキュアコピー) および SFTP(セキュアファイル転送プロトコル) 転送されるファイルに関する情報を交換するためのローカルおよびリモートエンド用の独自のプロトコルがあります。リモートエンドからの予期しないテキストは(誤って)プロトコルの一部として解釈され、転送は失敗します。によると カタツムリの本からのFAQ

しかし、頻繁に発生するのは、サーバー上のシステムまたはユーザーごとのシェルスタートアップファイル(_.bashrc_、_.profile_、_/etc/csh.cshrc_、_.login_、ログイン時にテキストメッセージを出力し、人間が読むことを目的としています(fortune、_echo "Hi there!"_など)。

このようなコードは、標準入力にttyが接続されている場合にのみ、インタラクティブログインで出力を生成する必要があります。このテストを行わない場合は、これらのテキストメッセージが属していない場所に挿入されます。この場合は、プロトコルストリームが_scp2_/sftpと_sftp-server_の間で汚染されます。

シェルスタートアップファイルがまったく関連する理由は、sshdがユーザーの代わりにプログラムを起動するときにユーザーのシェルを使用するためです(たとえば/ bin /を使用) sh -c "コマンド")。これはUnixの伝統であり、利点があります。

  • ユーザーの通常の設定(コマンドエイリアス、環境変数、umaskなど)は、リモートコマンドが実行されたときに有効になります。
  • アカウントのシェルを/ bin/falseに設定して無効にする一般的な方法は、何らかの理由で認証が誤って成功した場合でも、所有者がコマンドを実行できないようにすることです。

SCPプロトコルの詳細

SCPの動作の詳細に興味がある人のために、私は SCPプロトコルの動作 に興味深い情報を見つけました。これには、リモートでのしゃべるシェルプロファイルでのscpの実行に関する詳細が含まれています側?

たとえば、これをリモートシステムのシェルプロファイルに追加した場合に発生する可能性があります。

エコー ""

なぜハングするのですか?これは、sourceモードのscpが最初のプロトコルメッセージの確認を待機する方法に由来します。バイナリ0でない場合は、リモートの問題の通知であると想定し、新しい行が到着するまで、さらに多くの文字がエラーメッセージを形成するのを待ちます。最初の行の後に別の新しい行を印刷しなかったので、ローカルのscpはループにとどまり、read(2)でブロックされます。その間、シェルプロファイルがリモート側で処理された後、シンクモードのscpが開始されました。これもread(2)をブロックし、データ転送の開始を示すバイナリゼロを待機しています。

結論/ TLDR

一般的な_.bashrc_のステートメントのほとんどは、インタラクティブシェルでのみ役立ちます。rshまたはsshを使用してリモートコマンドを実行する場合は役に立ちません。このような状況のほとんどでは、シェル変数、エイリアス、関数の定義は望ましくありません。また、scpsftpなどのプログラムを使用してファイルを転送する場合、 any text を標準出力に出力することは非常に危険です。現在のシェルが非インタラクティブであることを確認した後に終了するのが_.bashrc_の最も安全な動作です。

75

マニュアルページでは、bashも非インタラクティブリモートシェルの.bashrcをソースとしていることを無視しています。

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/Shell.c#n101

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for Shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/Shell.c#n105

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level Shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && Shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);
16
Mikel

慣例により、.bashrcは、ユーザーがシェルのカスタマイズ構成を格納する場所です。

これらのカスタマイズ構成は、環境変数、エイリアス、派手なプロンプトにすることができます。非インタラクティブシェルでは、これらの不足分は意味がありません。さらに、非対話型シェルは多くのコンテキストで呼び出される可能性があり、これらの環境変数が偽陰性のケースを引き起こしたり、セキュリティが脆弱になったりする可能性があるかどうかはわかりません。

最も近い例は次のようなエイリアスです。

alias cp='cp -i'

次に、非インタラクティブシェルを永久にハングさせます。

したがって、チェックは.bashrcの上部で実行され、問題が発生しないことを確認します。


シェルは非インタラクティブloginシェルとして呼び出すことができるため、明示的に*bashrcのソーシングをブロックしても意味がありません。

シェルが 非対話型ログインシェルと呼ばれる の場合、/etc/profileをソースにし、~/.bash_profile~/.bash_login、および~/.profileで最初に見つかったシェルをソースします。

Bashがインタラクティブログインシェルまたは--loginオプションの非インタラクティブシェルとして呼び出されると、最初に/ etc/profileファイル(存在する場合)。そのファイルを読み取った後、〜/ .bash_profile、〜/ .bash_login、および〜/ .profileをこの順序で検索し、存在し、読み取り可能な最初のコマンドからコマンドを読み取って実行します。

これらのファイルが.bashrc自体をソースすることを妨げるものは何もないため、.bashrc内でチェックを行う方が安全であり、物事を単純化します。

5
cuonglm