Bashでは、ファイル記述子255は何のために使用できますか?


10

私はファイル記述子(またはファイルハンドラー)がLinuxシステムのファイルIO手法であることを理解しています

また、各プロセスには3つの標準ストリーム(つまり、stdin、stdout、stderr)があり、記述子が0〜3のファイルで表されることも知っています。

しかし、私が調べたすべてのプロセスにlsof -p <pid>は、255読み取り権限を持つ追加のファイル記述子があることに気づきました。

この回答から、この機能はBashシェルに固有であることがわかりましたが、回答と参照元の両方で、このファイル記述子の目的が実際に説明されていませんでした。

私の質問:

  1. 255ファイル記述子とは何ですか?
  2. 私のBashスクリプトでそれを利用できますか、それとも手動での使用/操作が想定されていない内部動作メカニズムだけですか?

私の意見では、あなたの質問はリンクされたページで答えられました。
Cyrus

もう一度答えを調べて、私の質問に答えるかどうかを確認します。私が気づいたのは、あなたがその答えを出したのはあなただけだったということだけです;)
Tran Triet

2
@Cyrusは、「小さなトリック」は何であるかを説明せずに「それは小さなトリックだ」と言っているのは適切な答えではありません。
モスビー2018年

リンクされた回答の最初のコメントは、より良い議論があるようです... 最後の返信は、おそらくあなたが探しているものです...
RubberStamp

回答:


12

あなたの質問の最後の部分について:

使えますか?

からman bash

9より大きいファイル記述子を使用するリダイレクトは、シェルが内部で使用するファイル記述子と競合する可能性があるため、注意して使用する必要があります。

したがって、その番号を使用して新しいfdを作成する場合は、答えは「いいえ」です。

次のように使用する場合:「そのfdに書き込む」:

$ echo hello >/dev/fd/255"

またはそれから読むには:

$ read a </dev/fd/255
abc
$ echo "$a"
abc

答えはイエスです。
しかし、おそらく、/dev/ttyへのアクセスに使用する方が(シェルとは関係なく)優れているはずttyです。

ファイル記述子255とは何ですか?

fd 1(/dev/stdout)およびfd 0(/dev/stdin)がブロックされた場合のttyへの代替接続として。

詳細

他のシェルは異なる番号を使用する場合があります(zshの10など)

$ zsh
mail% ls -l /proc/self/fd /proc/$$/fd/* &
[1] 3345
mail% lrwx------ 1 isaac isaac 64 Oct 14 09:46 /proc/3250/fd/0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/10 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 /proc/3250/fd/2 -> /dev/pts/2

/proc/self/fd:
total 0
lrwx------ 1 isaac isaac 64 Oct 14 09:50 0 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 1 -> /dev/pts/2
lrwx------ 1 isaac isaac 64 Oct 14 09:50 2 -> /dev/pts/2
lr-x------ 1 isaac isaac 64 Oct 14 09:50 3 -> /proc/3345/fd

[1]  + done       ls -l /proc/self/fd /proc/$$/fd/*
mail% 

メールリストから:

Fd 255は、ttyへの接続として内部的に使用されるため、execを使用してfdを再配置することを妨げません。同じ理由で、bashはプロセス置換 `<(foo) 'を処理するときに高いfdsも割り当てます。
アンドレアスシュワブ


fd 255は「fd 1とfd 0のコピーを保持する」ために使用されません -で簡単に確認できますdd bs=1 | bash -i -c 'sleep .1; ls -l /proc/$$/fd' 2>/tmp/err | tee /tmp/out。また、メーリングリストからのコメントは、いつbash実行されるかbash scriptfile255その場合はオープンハンドルscriptfile-であり、その場合はls -l /proc/pid/fd非常に説得力のある方法で出力されます255 -> scriptfile;-))に関するものであり、対話的に実行されるときに関するものではありません。
2018年

私の回答からのソースコードスニペットと分析があなたを納得させなかったのは残念ですが、明確にするために:a)それはttyへの「代替」接続ではなく、すべてに使用されるttyへのメイン接続ですtty関連の目的b)/dev/ttyfd 0(fder 1)からではなく、fd 2(stderr)からコピーされるか、から直接開かれるc)fds 0、1、または2が「ブロック」される場合、bashはその255 fdを代替として使用しませんユーザーからの入力の読み取り、またはコマンド出力、プロンプト、エラーメッセージなどの
書き込み

8

その255ファイル記述子は、制御ttyへのオープンハンドルであり、bashがインタラクティブモードで実行されている場合にのみ使用されます。

stderrメインシェルでをリダイレクトしながら、ジョブ制御を機能させます(つまり、^ Cでプロセスを強制終了したり、^ Zでプロセスを中断したりすることができます)。

例:

$ exec 2> >(tee /tmp/err); ls /nosuchfile; sleep 1000

ksh93制御ターミナルへの参照としてファイル記述子2を使用しているだけのようなシェルでそれを試すと、sleepプロセスは^ Cおよび^ Zの影響を受けなくなり、別のウィンドウ/セッションから強制終了する必要があります。これは、ファイル記述子2がもはやターミナルを指していないため、シェルはのプロセスグループをsleepターミナルでフォアグラウンドのプロセスグループとして設定できないtcsetgrp()ためです。

これはbash特定のものではなく、dashおよびでも使用されzshます。記述子がそれほど高く移動されないことだけです(通常は10です)。

zsh また、そのfdを使用してプロンプトとユーザー入力をエコーするので、次のように機能します。

$ exec 2>/tmp/err
$ 

他の回答やコメントで提案されてbashいるように、スクリプトを読み取ったりパイプを設定したりするときに使用するファイルハンドル(同じ機能で邪魔にならない-)とは関係ありませんmove_to_high_fd()

bash9シェル内リダイレクト(たとえばexec 87<filename)で使用されるよりも大きいfdsを許可するために、このような大きな数を使用しています。他のシェルではサポートされていません。

このファイルハンドルは自分で使用できますが、を使用すると、どのコマンドでも同じ制御端末へのハンドルを取得できるので、そうする意味がほとんどありません... < /dev/tty

bashのソースコード分析

ではbash、制御端末のファイル記述子がshell_tty変数に格納されます。シェルが対話型である場合、その変数は、中(起動時または失敗EXECの後に)初期化されるjobs.c:initialize_job_control()からそれをdup'ingによってstderr(場合stderr、端末に装着される)または直接開いて/dev/tty、その後より高いFDに再度dup'edれますとgeneral.c:move_to_high_fd()

int
initialize_job_control (force)
     int force;
{
  ...
  if (interactive == 0 && force == 0)
    {
      ...
    }
  else
    {
      shell_tty = -1;

      /* If forced_interactive is set, we skip the normal check that stderr
         is attached to a tty, so we need to check here.  If it's not, we
         need to see whether we have a controlling tty by opening /dev/tty,
         since trying to use job control tty pgrp manipulations on a non-tty
         is going to fail. */
      if (forced_interactive && isatty (fileno (stderr)) == 0)
        shell_tty = open ("/dev/tty", O_RDWR|O_NONBLOCK);

      /* Get our controlling terminal.  If job_control is set, or
         interactive is set, then this is an interactive shell no
         matter where fd 2 is directed. */
      if (shell_tty == -1)
        shell_tty = dup (fileno (stderr));        /* fd 2 */

      if (shell_tty != -1)
        shell_tty = move_to_high_fd (shell_tty, 1, -1);
      ...
    }

shell_ttyが制御ttyでない場合は、次のように作成されます。

          /* If (and only if) we just set our process group to our pid,
             thereby becoming a process group leader, and the terminal
             is not in the same process group as our (new) process group,
             then set the terminal's process group to our (new) process
             group.  If that fails, set our process group back to what it
             was originally (so we can still read from the terminal) and
             turn off job control.  */
          if (shell_pgrp != original_pgrp && shell_pgrp != terminal_pgrp)
            {
              if (give_terminal_to (shell_pgrp, 0) < 0)

shell_tty 次に使用されます

  1. 取得してフォアグラウンドプロセスグループを設定tc[sg]etpgrpjobs.c:maybe_give_terminal_to()jobs.c:set_job_control()およびjobs.c:give_terminal_to()

  2. 取得および設定termios(3)中のparamsをjobs.c:get_tty_state()し、jobs.c:set_tty_state()

  3. ターミナルウィンドウのサイズを取得ioctl(TIOCGWINSZ)の中でlib/sh/winsize.c:get_new_window_size()

move_to_high_fd()通常は、bash(スクリプトファイル、パイプなど)が使用するすべての一時ファイル記述子で使用されます。そのため、Google検索で目立つように表示されるほとんどのコメントで混乱が生じます。

bashを含め、内部で使用されるファイル記述子shell_ttyはすべてclose-on-execに設定されているため、コマンドにリークされることはありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.