Bashはどのストリームにプロンプ​​トを書きますか?

Bashはどのストリームにプロンプ​​トを書きますか?

bash(プロンプト、ユーザー入力、結果)のすべての出力をファイルにリダイレクトしようとしています。

例:

/bin/bash > file.txt 2>&1

私はこれがうまくいくと思いましたが、プロンプトを得ませんでした。誰かが私が間違っていることを教えてもらえますか?

答え1

Bashはインタラクティブモードでのみプロンプトを出力します。つまり、通常はターミナル(Linuxの場合/ dev / tty)に出力されます。 /dev/stdoutでも/dev/stdinでもありません:)

今はわかりませんが、完全な機能を備えたttyが利用できない場合、bashは制限された対話モードを許可すると想像できます。それから私は予想される標準出力に書き込むメッセージを表示します。まだテストしていません。

優れた概念証明:

(for a in some set of words; do echo $a > /dev/tty; done) 2>&1 > /dev/null

リダイレクトがないかのように1..10のみ出力されます。プロンプトと同様に、出力を端末に直接送信します(ターミナルがないと失敗します)。

Tip: みんな集めてみたいなら確認してみてください。

答え2

最も簡単な方法は

bash -i >/tmp/logfile 2>&1

コマンドを入力すると、Bashはすべてを書き、/tmp/logfileコマンドを実行し続けますが、端末には何も表示されません。終了時にCtrl+を押すDか、を入力してターミナルセッションを終了できますexit

リダイレクトなしで同じタスクを実行すると、stderrhelloメッセージのみがファイルに書き込まれ、他のすべての内容は現在の端末で実行されます。したがって、bashがプロンプトストリーム(および次のコマンドの両方)を出力することに関する質問に対する答えは次のとおりです。標準エラー

ああ、そうです。この-iパラメータは、bashが対話型モードで実行されるように強制します。その人の言葉を聞かないでください。これを行うために魔法は必要ありません。

答え3

プロンプトはtruss(Solarisの場合)に示すようにstderrに書き込まれます。

$ truss -ft write -p 10501
10501:  write(2, " d", 1)               = 1
10501:  write(2, " a", 1)               = 1
10501:  write(2, " t", 1)               = 1
10501:  write(2, " e", 1)               = 1
10501:  write(2, "\n", 1)               = 1
10521:  write(1, " S a t u r d a y ,   S e".., 46)  = 46
10501:      Received signal #18, SIGCLD [caught]
10501:        siginfo: SIGCLD CLD_EXITED pid=10521 status=0x0000
10501:  write(2, " $  ", 2)             = 2

答え4

対話型モードでは、bashはプロンプトをstderrに出力します。次のstraceコマンドはこれを示しています(--norc別のプロンプトを設定する〜/ .bashrcのオプションが私のカスタムプロンプトをオーバーライドすると解釈されないことを確認してください)。

$ export PS1="MYPROMPT> "
MYPROMPT> strace -f -- bash --norc 
execve("/usr/bin/bash", ["bash"], 0x7ffcd83805b0 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> )              = 10 <----- prompt written into stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}

stdoutとstderrをファイルにリダイレクトするのと同じコマンドは、プロンプトは印刷されませんが、シェルがコマンドを受け入れることを示します。

MYPROMPT> strace -f -- bash --norc > /tmp/shout 2> /tmp/sherr
date

別の端末でファイルを表示します。内容は呼ぶ例:

$ cat /tmp/shout
dim. 04 sept. 2022 16:19:24 CEST  <------- Result of the date command entered above

内容はシェル例:

$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc"], 0x7ffe73ea5da8 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, 0x7ffc43496b30)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]
read(0,  <-------- No prompt is displayed

しかし、-ibashコマンドラインにオプションを追加するのと同じことをする場合:

MYPROMPT> strace -f -- bash --norc -i > /tmp/shout 2> /tmp/sherr

stderrにプロンプ​​トを表示する:

$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc", "-i"], 0x7ffd8c1fa520 /* 59 vars */) = 0
[...]
ioctl(2, TCGETS, 0x7ffe1a8465f0)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]
ioctl(0, TIOCGWINSZ, {ws_row=37, ws_col=155, ws_xpixel=0, ws_ypixel=0}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> )              = 10  <------ Prompt displayed on stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}

関連情報