298

標準出力が端末に送信されているか、別のプロセスにパイプされているかをシェルスクリプト内から検出するにはどうすればよいですか?

適切な例: 出力を色付けするためにエスケープ コードを追加したいのですが、対話的に実行する場合のみであり、パイプされた場合はそうでls --colorはありません。

4

6 に答える 6

461

純粋な POSIX シェルでは、

if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi

出力が端末に送信されるため、「端末」を返しますが、

(if [ -t 1 ] ; then echo terminal; else echo "not a terminal"; fi) | cat

は、括弧要素の出力が にパイプされるため、「端末ではありません」を返しますcat


-tフラグは、man ページで次のように説明されています。

-t fd ファイル記述子 fd が開いていて、端末を参照している場合は真です。

...fd通常のファイル記述子の割り当てのいずれかになります。

于 2009-05-26T15:13:32.313 に答える
142

主に. _ssh

「通常」機能するもの

たとえば、次の bash ソリューションは対話型シェルで正しく機能します。

[[ -t 1 ]] && \
    echo 'STDOUT is attached to TTY'

[[ -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a pipe'

[[ ! -t 1 && ! -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a redirection'

しかし、それらは常に機能するとは限りません

ただし、このコマンドを非 TTYsshコマンドとして実行すると、STD ストリームは常にパイプされているように見えます。これを実証するには、より簡単な STDIN を使用します。

# CORRECT: Forced-tty mode correctly reports '1', which represents
# no pipe.
ssh -t localhost '[[ -p /dev/stdin ]]; echo ${?}'

# CORRECT: Issuing a piped command in forced-tty mode correctly
# reports '0', which represents a pipe.
ssh -t localhost 'echo hi | [[ -p /dev/stdin ]]; echo ${?}'

# INCORRECT: Non-tty mode reports '0', which represents a pipe,
# even though one isn't specified here.
ssh -T localhost '[[ -p /dev/stdin ]]; echo ${?}'

重要な理由

sshこれは、非 ttyコマンドがパイプ処理されているかどうかを bash スクリプトが判断する方法がないことを意味するため、非常に重要です。この残念な動作は、最近のバージョンのsshが非 TTY STDIO にパイプを使用し始めたときに導入されたことに注意してください。以前のバージョンではソケットを使用していましたが、 [[ -S ]].

重要な場合

などのコンパイル済みユーティリティと同様の動作をする bash スクリプトを作成する場合、通常、この制限により問題が発生しますcat。たとえば、catさまざまな入力ソースを同時に処理する際に次の柔軟な動作を可能にし、非 TTY または強制 TTYsshが使用されているかどうかに関係なく、パイプ入力を受信して​​いるかどうかを判断するのに十分スマートです。

ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'

パイプが関与しているかどうかを確実に判断できる場合にのみ、そのようなことを行うことができます。そうしないと、パイプまたはリダイレクトからの入力が利用できないときに STDIN を読み取るコマンドを実行すると、スクリプトがハングし、STDIN 入力を待機します。

その他動作しないもの

この問題を解決しようとして、問題の解決に失敗するいくつかの手法を調べました。

  • SSH 環境変数の調査
  • stat/dev/stdin ファイル記述子での使用
  • インタラクティブモードを調べる[[ "${-}" =~ 'i' ]]
  • ttyおよびを介して tty ステータスを調べるtty -s
  • ssh経由でステータスを調べる[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]

仮想ファイルシステムをサポートする OS を使用している場合は/proc、STDIO のシンボリック リンクをたどって、パイプが使用されているかどうかを判断できる場合があります。ただし、/procクロスプラットフォームの POSIX 互換ソリューションではありません。

私はこの問題を解決することに非常に興味を持っているので、Linux と BSD の両方で動作する POSIX ベースのソリューションが望ましい他の手法があれば教えてください。

于 2015-05-29T02:48:25.000 に答える
33

このコマンドtest(Bash に組み込まれています) には、ファイル記述子が tty かどうかをチェックするオプションがあります。

if [ -t 1 ]; then
    # Standard output is a tty
fi

man test「 」または「 」を参照して、「 man bash」を検索してください-t

于 2009-05-26T15:14:57.333 に答える
13

使用しているシェルについては言及していませんが、Bash では次のことができます。

#!/bin/bash

if [[ -t 1 ]]; then
    # stdout is a terminal
else
    # stdout is not a terminal
fi
于 2009-05-26T15:21:07.697 に答える
8

Solarisでは、 Dejay Clayton からの提案がほとんど機能します。-pが希望どおりに応答しません。

ファイルbash_redir_test.shは次のようになります。

[[ -t 1 ]] && \
    echo 'STDOUT is attached to TTY'

[[ -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a pipe'

[[ ! -t 1 && ! -p /dev/stdout ]] && \
    echo 'STDOUT is attached to a redirection'

Linuxでは、うまく機能します:

:$ ./bash_redir_test.sh
STDOUT is attached to TTY

:$ ./bash_redir_test.sh | xargs echo
STDOUT is attached to a pipe

:$ rm bash_redir_test.log
:$ ./bash_redir_test.sh >> bash_redir_test.log

:$ tail bash_redir_test.log
STDOUT is attached to a redirection

Solaris の場合:

:# ./bash_redir_test.sh
STDOUT is attached to TTY

:# ./bash_redir_test.sh | xargs echo
STDOUT is attached to a redirection

:# rm bash_redir_test.log
bash_redir_test.log: No such file or directory

:# ./bash_redir_test.sh >> bash_redir_test.log
:# tail bash_redir_test.log
STDOUT is attached to a redirection

:#
于 2015-06-12T20:41:57.933 に答える
1

次のコード (Linux Bash 4.4 でのみテスト済み)は、移植可能または推奨されていると見なされるべきではありませんが、完全を期すためにここに記載されています。

ls /proc/$$/fdinfo/* >/dev/null 2>&1 || grep -q 'flags:    00$' /proc/$$/fdinfo/0 && echo "pipe detected"

理由はわかりませんが、Bash 関数で標準入力がパイプ処理されていると、ファイル記述子「3」が何らかの形で作成されるようです。

于 2019-02-13T11:13:22.763 に答える