標準出力が端末に送信されているか、別のプロセスにパイプされているかをシェルスクリプト内から検出するにはどうすればよいですか?
要点:出力を色分けするためにエスケープコードを追加したいのですが、対話的に実行した場合のみで、パイプで実行した場合ls --color
はそうではありません。
標準出力が端末に送信されているか、別のプロセスにパイプされているかをシェルスクリプト内から検出するにはどうすればよいですか?
要点:出力を色分けするためにエスケープコードを追加したいのですが、対話的に実行した場合のみで、パイプで実行した場合ls --color
はそうではありません。
回答:
純粋な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
は通常のファイル記述子割り当ての1つです。
0: stdin
1: stdout
2: stderr
-t
フラグはPOSIXで指定されているため、すべてのPOSIX互換シェルで機能するはずです(つまり、bash拡張ではありません)。 pubs.opengroup.org/onlinepubs/009695399/utilities/test.html
fish
シェルの答えを探していました。使用test
はきちんとしていますが、括弧で囲まれた例はサポートされていないため、試すことはできません。類似のものでラップしようとしましたbegin; ...; end
が、それはうまくいかなかったようで、ポジティブコードブロックをもう一度実行しました。使用する必要status
があるかもしれませんが、それは配管をチェックしていないようです。これらの明確な回答のおかげで、私は基本的に、先行するコマンド/スクリプトのSTDOUTが端末に設定されていないかどうかを確認したいと思います。
主にのようなプログラムが原因で、STDIN、STDOUT、またはSTDERRがスクリプトとの間でパイプ接続されているかどうかを判別するための確実な方法はありません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'
ただし、このコマンドを非TTYコマンドとして実行するとssh
、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 ${?}'
tty以外のssh
コマンドがパイプ処理されているかどうかをbashスクリプトで確認する方法がないことを意味するため、これはかなり大きな問題です。この不幸な動作は、の最近のバージョンがssh
非TTY STDIOのパイプの使用を開始したときに導入されたことに注意してください。以前のバージョンではソケットを使用していましたが、これを使用してbash内から区別できます[[ -S ]]
。
通常、この制限により、などのコンパイルされたユーティリティと同様の動作をするbashスクリプトを作成するときに問題が発生しますcat
。たとえば、cat
さまざまな入力ソースを同時に処理する次の柔軟な動作を可能にし、非TTYまたは強制TTY ssh
が使用されているかどうかに関係なく、パイプ入力を受信しているかどうかを判断するのに十分なほどスマートです。
ssh -t localhost 'echo piped | cat - <( echo substituted )'
ssh -T localhost 'echo piped | cat - <( echo substituted )'
パイプが関係しているかどうかを確実に判断できる場合にのみ、そのようなことを行うことができます。それ以外の場合、パイプまたはリダイレクトからの入力が利用できないときにSTDINを読み取るコマンドを実行すると、スクリプトがハングし、STDIN入力を待機します。
この問題を解決するために、問題を解決できないいくつかの手法を調べました。
stat
は/ dev / STDINファイル記述子に[[ "${-}" =~ 'i' ]]
tty
およびを介したttyステータスの調査tty -s
ssh
経由でステータスを調べる[[ "$(ps -o comm= -p $PPID)" =~ 'sshd' ]]
/proc
仮想ファイルシステムをサポートするOSを使用している場合は、STDIOのシンボリックリンクをたどってパイプが使用されているかどうかを判断できる場合があります。ただし、/proc
クロスプラットフォームのPOSIX互換ソリューションではありません。
この問題を解決することは非常に興味深いので、他の有効な手法、できればLinuxとBSDの両方で機能するPOSIXベースのソリューションについて考えた場合はお知らせください。
stat
、/ dev / stdinの呼び出しの出力に違いはありません。そして、なぜない"${-}"
かtty -s
動作しませんか?私はまた、ソースコードを調べましたcat
が、POSIXシェルでは実行できない魔法を実行している部分を確認できませんでした。拡大していただけますか?
コマンドtest
(に組み込まれbash
ています)には、ファイル記述子がttyであるかどうかをチェックするオプションがあります。
if [ -t 1 ]; then
# stdout is a tty
fi
「を参照してくださいman test
」または「man bash
」と「で検索-t
」
help test
(とhelp help
、より多くのために)、info bash
より詳細な情報については。これらのコマンドは、オフラインでスクリプトを作成する場合や、より広い理解を得たい場合に最適です。
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
:#