プログラムは、stdoutが端末またはパイプに接続されているかどうかをどうやって知ることができますか?

プログラムは、stdoutが端末またはパイプに接続されているかどうかをどうやって知ることができますか?

segfaultより前の出力が正確に必要なので、segfaultプログラムのデバッグに問題があります。ただし、出力をファイルにパイプすると失われます。この答えによると:https://unix.stackexchange.com/a/17339/22615これは、プログラムの出力バッファが端末に接続されるとすぐにフラッシュされますが、パイプに接続されている場合は特定のポイントでのみフラッシュされます。ここにはいくつかの質問があります。

  • プログラムは、標準出力が何に接続されているかをどのように決定しますか?

  • 「script」コマンドは、プログラムが端末に書き込むときと同じ動作をどのように生成しますか?

  • スクリプトコマンドなしでこれを実行できますか?

答え1

ファイル記述子が端末装置を指していることを確認する

プログラムは、次のコマンドを使用して、ファイル記述子がttyデバイスに関連付けられているかどうかを確認できます。isatty()ioctl()標準C機能(通常、以下ではfdがttyデバイスを指していないときにエラーを返す無害なtty関連のシステムコールを実行します)。

[/utilityはtest演算子を介してこれを実行できます-t

if [ -t 1 ]; then
  echo stdout is open to a terminal
fi

GNU/Linux システムでの libc 関数呼び出しの追跡:

$ ltrace [ -t 1 ] | cat
[...]
isatty(1)                                      = 0
[...]

システムコール追跡:

$ strace [ -t 1 ] | cat
[...]
ioctl(1, TCGETS, 0x7fffd9fb3010)        = -1 ENOTTY (Inappropriate ioctl for device)
[...]

パイプを指していることを確認

fd がパイプ/FIFO に関連付けられていることを確認するには、次を使用できます。fstat()システムコールst_mode、このfdで開かれたファイルの種類と権限がフィールドに含まれている構造を返します。これS_ISFIFO()標準Cマクロst_modeこのフィールドでは、fdがパイプ/ FIFOであることを確認するために使用できます。

これを実行できる標準ユーティリティはありませんが、それを実行できる互換性のないコマンド実装がいくつかfstat()あります。組み込み関数はパターンを文字列表現として返し、最初の文字は型(パイプの場合)を表します。 GNUは同じことができますが、報告する必要があります。statzshstatstat -sf "$fd" +modepstatstat -c %A - <&"$fd"stat -c %F - <&"$fd"タイプ一人で。 BSDの場合statstat -f %St <&"$fd"またはstat -f %HT <&"$fd"

検索可能かどうかを確認

アプリケーションは通常、stdoutがパイプかどうかを気にしません。彼らは検索可能かどうかに興味があるかもしれません(通常はバッファリングするかどうかを決定しませんが)。

fdが検索可能かどうかをテストするには(パイプ、ソケット、ttyデバイスは検索できず、通常のファイルとほとんどのブロックデバイスは通常検索可能)、相手を試すことができます。lseek()システムコールオフセットは0です(それで無害です)。dd標準ユーティリティであり、インターフェイスですが、オフセット0を要求しても実装はまったく呼び出さないlseek()ため、このテストでは機能しません。lseek()

zshそしてシェルにはksh93検索演算子が組み込まれています。

$ strace -e lseek ksh -c ': 1>#((CUR))' | cat
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
ksh: 1: not seekable
$ strace -e lseek zsh -c 'zmodload zsh/system; sysseek -w current -u 1 0 || syserror'
lseek(1, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
Illegal seek

バッファリングの無効化

このscriptコマンドは擬似端末ペアを使用してプログラムの出力をキャプチャするため、プログラムのstdout(stdinおよびstderrも含む)は疑似端末装置になります。

stdoutが端末デバイスの場合、通常はわずかなバッファリングがありますが、ラインベースです。printf/putsとcoは、改行文字が出力されるまで何も書きません。他の種類のファイルでは、バッファリングはブロック(キロバイト)単位で行われます。

バッファリングを無効にするためのいくつかのオプションがあります。これは多くのQ&Aで説明されています(検索バッファリング解除または標準バッファカット出力をリダイレクトできません。いくつかの方法は、疑似端末(たとえば、// socat(script)/)を使用するか、実行可能ファイルにコードを挿入してバッファリングを無効にする(GNUまたはFreeBSDで実行される)いずれかです。scriptexpectunbufferexpectzshzptystdbuf

関連情報