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


62

私の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、それらを指すように設定されている場合にのみファイルが読み込まれます。これは偶然に発生することはなく、誰かがそれに応じて明示的に変数を設定した場合にのみ発生します。

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


回答:


69

これは、数週間前にここに投稿しようとしていた質問です。同様terdon、私がいることを理解.bashrcは必要ないはずですので、唯一の対話型のBashシェルのために供給され.bashrc、それは対話型シェルで実行されているかどうかを確認します。紛らわしいことに、私が使用するすべてのディストリビューション(Ubuntu、RHEL、およびCygwin)には、現在のシェルが対話型であることを確認するための何らかのタイプのチェック(テスト$-または$PS1)がありました。私はカーゴカルトプログラミングが好きではないので、私のコードでこのコードの目的を理解し始めました.bashrc

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

この問題を調査した後、リモートシェルの扱いが異なることを発見しました。非対話型のBashシェルは通常~/.bashrc、起動時にコマンドを実行しませんが、シェルがリモートシェルデーモンによって呼び出されると特別なケースが作成されます

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

リモートの先頭に次を挿入します.bashrc。(またはから.bashrc供給されている.profile場合.bash_profile、テスト中に一時的にこれを無効にします):

echo bashrc
fun()
{
    echo functions work
}

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

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • No iin $-は、シェルが非対話型であることを示します。
  • リードなし-では$0シェルがないことを示していないログインシェル

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

$ ssh remote_host fun
bashrc
functions work

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

.bashrc(非対話型)リモートコマンドを実行するときにソースを取得することでわかる主な利点は、シェル関数を実行できることです。ただし、典型的なコマンドのほとんどは.bashrc対話型シェルにのみ関連しています。たとえば、シェルは対話型でない限りエイリアスは展開されません。

リモートファイル転送が失敗する可能性があります

これは通常、対話型ログインシェルを起動するために使用される場合、rshまたはsshコマンドを実行するために非対話型シェルが使用される場合は問題になりません。しかし、それは問題になる可能性がありますのようなプログラムのためrcpscpおよびsftpそれが使用するリモートシェルをデータを転送します。

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

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

なぜscpsftp失敗

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

何が頻繁に起こる、しかし、サーバー上のシステムやユーザごとのシェル起動ファイルのいずれかでのステートメント(があるということです.bashrc.profile/etc/csh.cshrc.login、など)は、ヒト(等によって読まれることを意図し、ログインに出力テキストメッセージは、fortuneecho "Hi there!"、等。)。

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

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

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

SCPプロトコルの詳細

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

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

エコー ""

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

結論/ TLDR

一般的なステートメントのほとんどは、.bashrc対話型シェルでのみ有用です。rshまたはでリモートコマンドを実行するときは有用ではありませんssh。ほとんどのこのような状況は、シェル変数、別名を設定し、所望されていない関数を定義するには-と印刷の任意のテキストを標準出力へののようなプログラムを使用してファイルを転送する場合は積極的に有害ですscpsftp。現在のシェルが非対話型であることを確認した後に終了することが、の最も安全な動作です.bashrc


素敵な記事。しかし、私はそれでやるべきですか?私はbashrcににチェックインが、それでも0コードでscpコマンドを終了し、リモートボックスには何もコピーしないでくださいていない
SES

@ses状況をより詳細に説明できる新しい質問をするのが最善です。
アンソニーG-モニカの正義

16

マニュアルページでは、次のように非対話型リモートシェルのbashソースも言及し.bashrcていません。

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 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#n1050

          /* 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);

.bash_profile(または.profile)から.bashrcを入手していますか?
グレンジャックマン16年

はい、しかしそれはポイントではありません。空の.bash_profile場合も同じ動作になります。
ミケル

それが私に何を示しているのか分かりません。rsh(私は一度も使ったことがない)ソースと言っています~/.bashrcか?そして、拡張機能により、Linuxディストリビューションは、bashだれかがスクリプトを実行しようとした場合に備えて、それをブロックしrshますか?とにかくログインシェルssh server.bashrcので、触れてはいけません。ないシステムがそのソース〜/ .bashrc`からのものの一つである場合を除き~/.profile。そして、そうするssh server commandべきではありません。確かに私のシステムにはありません。
テルドン

2
いいえ。bashソースではなくソースにリンクしましたrsh。:)コメントも同様に適用さsshれます、それは非常に長い時間前に書かれました。より多くのソースを含む更新された回答を参照してください。
ミケル

ああ、助かります、ありがとう。これをどのようにテストしますか?私は、追加しようとしたechoの一番上に/etc/bash.bashrcして~/.bashrc(対話型シェルのテストの前に)、その後でターゲットマシンにsshをssh server hostnameしている間はhostname、私は私のエコーのいずれかを見ていない、実行されました。私のshell_level(それが何であれ)そうではないの<2ですか?
テルドン

5

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

これらのカスタマイズ構成は、環境変数、エイリアス、空想プロンプトにすることができます。非対話型シェルでは、これらの短いものは無意味です。さらに、非対話型シェルは多くのコンテキストで呼び出すことができますが、これらの環境変数が偽陰性のケース、さらにはセキュリティの脆弱性につながるかどうかはわかりません。

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

alias cp='cp -i'

その後、非対話型シェルが永久にハングします。

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


シェルは非対話型ログインシェルとして呼び出すことができるため、明示的にブロックする*bashrcことは意味がありません。

シェルがされた場合には非対話型ログインシェルと呼ばれる、それはソース/etc/profile、その後、最初のものをソース内で見つかった~/.bash_profile~/.bash_login~/.profile

bashが対話型ログインシェルとしてまたは--loginオプションを使用した非対話型シェルとして呼び出されると、bashは最初にファイル/ etc / profileが存在する場合、そのファイルからコマンドを読み取って実行します。そのファイルを読み取った後、〜/ .bash_profile、〜/ .bash_login、および〜/ .profileをこの順序で探し、最初に存在して読み取り可能なコマンドを読み取って実行します。

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


はい、知っています。これが、非対話型シェルが*bashrcファイルをまったくソースしない理由です。私の質問は、これらのファイルが非インタラクティブシェルによって読み取られない場合、さまざまなLinuxベンダーが非インタラクティブシェルによるこれらのファイルの読み取りを明示的にブロックするという問題に直面する理由です。
テルドン

1
@terdon:シェルは非対話型ログインシェルとして呼び出すことができるため、ログインシェルはsourceになり.bash_profile、source になります.bashrc
クオングルム

いいえ、非対話型シェルでは、読み込まれるのはのみです$BASH_ENV*profile*bashrcは両方とも無視されます。または、少なくとも、それはマニュアルページに書かれていることです。ただし、Mikelが示したように、manページは嘘をついている可能性があります。
テルドン

@terdon:リンクが記載された最新の回答を読んでください。bash -l -c :非対話型ログインシェルを起動しようとしましたか?
クオングルム

ワオ。あなたは絶対に正しいです。私はその部分を約--login100回読みましたが、どうやら登録されていなかったようです。ありがとう!
テルドン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.