web-dev-qa-db-ja.com

プログラムはstdoutがターミナルまたはパイプに接続されているかどうかをどのようにして知るのですか?

Segfaultの直前の出力が必要なため、segfaultingプログラムのデバッグに問題がありますが、出力をファイルにパイプすると、これは失われます。この回答によると: https://unix.stackexchange.com/a/17339/22615 、これは、プログラムの出力バッファがターミナルに接続されたときにすぐにフラッシュするためですが、特定の時点でのみパイプに接続されています。ここでいくつかの質問:

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

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

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

12
mowwwalker

ファイル記述子が端末デバイスを指しているかどうかの確認

プログラムは isatty() 標準C関数を使用して、ファイル記述子がttyデバイスに関連付けられているかどうかを確認できます(これは通常、無害なtty固有のioctl()システムを実行します) fdがttyデバイスを指さない場合、エラーで戻る呼び出し。

[/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がpipe/fifoに関連付けられているかどうかを判別するには、 fstat()システムコール を使用できます。これは、st_modeフィールドに次のタイプと権限が含まれる構造体を返しますそのfdで開かれたファイル。 S_ISFIFO()標準Cマクロ をそのst_modeフィールドで使用して、fdがpipe/fifoであるかどうかを判別できます。

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

シーク可能かどうかを伝える

ただし、アプリケーションは通常、stdoutがパイプであるかどうかを気にしません。彼らはそれがシーク可能であることを気にするかもしれません(ただし、一般的にバッファするかどうかを決定することはありません)。

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

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で説明されています( nbuffer または stdbuf を検索して、 カット出力をリダイレクトできません =いくつかのアプローチを提供します)socat/script/expect/unbuffer(an expect script)/ zsh 's zptyまたは、GNUまたはFreeBSDのstdbufのように、実行可能ファイルにコードを挿入してバッファリングを無効にします。

23