1. 概要

このチュートリアルでは、プロセスの出力をファイルとstdoutstderrなどの標準ストリームに同時にリダイレクトするためのいくつかの一般的な戦略について説明します。

2. teeコマンド

tee コマンドは、プロセスの出力をリダイレクトするために使用できる最も一般的なLinuxコマンドの1つです。

2.1. stdoutをリダイレクトします

lsコマンドの出力をstdoutと一時ファイル/tmp/out.logにリダイレクトする簡単な使用例を見てみましょう。

$ ls -C | tee /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr

ファイルの内容が、実行されたコマンドから生成された出力と同じであることを確認できます。

$ cat /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr

注意すべきもう1つの重要な点は、teeコマンドのデフォルトの動作は、ファイルの内容を上書きすることです。 ただし、必要に応じて、を選択できます。これは、ファイルの既存のコンテンツの後に新しいコンテンツを追加するオプションです。

2.2. stdoutstderrを同じファイルにリダイレクトします

内部的には、teeコマンドが着信stdin のTスプリッターとして機能し、データをstdoutと1つにリダイレクトできることを理解する必要があります。以上のファイル。 この理解を利用して、プロセスのstderrstdoutとファイルにリダイレクトしましょう。

$ (ls -C; cmd_with_err) 2>&1 | tee /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

cmd_with_err は不明なコマンドであるため、エラーメッセージが生成されます。 これをteeコマンドで使用できるようにするには、 stderr ファイル記述子( fd = 2 )をstdoutにリダイレクトします。 ファイル記述子( fd = 1 )。

または、 2>&1 |の省略表記として|&を使用します同じ結果を得るには:

$ (ls -C; cmd_with_err) |& tee /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

それでは、/tmp/out.logファイルの内容を確認しましょう。

$ cat /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

2.3. stdoutstderrを別々のファイルにリダイレクトします

シナリオによっては、プロセスのstdoutstderrを別々のファイルにリダイレクトする必要がある場合があります。 これを行うには、teeコマンドを呼び出しながらプロセス置換を使用します。 その前に、 teeコマンドが特定のファイル記述子をリッスンし、同じファイル記述子ストリームとファイルに書き戻すことを可能にするテンプレートコードスニペットを見てみましょう。

fd> >(tee file_name fd>&fd)

fd はファイル記述子の単なるプレースホルダーであり、実際の値は stdout の場合は1、 stderr の場合は2、[ X160X]stdin。

ここで、この理解を利用して、プロセスのstdoutおよびstderr出力を/tmp/out.logおよび/tmp/errにリダイレクトしましょう。 log 、それぞれ:

$ ((ls -C; cmd_with_err) 1> >(tee /tmp/out.log)) 2> >(tee /tmp/err.log 2>&2)
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

/tmp/out.logに有効なstdoutメッセージが含まれているのに対し、/tmp/err.logにはstderrからのエラーメッセージが含まれていることを確認できます。

$ cat /tmp/out.log
bin   dev  home  lib32	libx32	mnt  proc  run	 srv  tmp  var
boot  etc  lib	 lib64	media	opt  root  sbin  sys  usr
$ cat /tmp/err.log
bash: cmd_with_err: command not found

3. リダイレクトの遅延

いくつかのシナリオでは、プロセスの出力をファイルとstdoutにリダイレクトするために teeコマンドを呼び出すと、delayが発生する可能性があります。 このセクションでは、そのようなシナリオを調査し、それを軽減する方法を学びます。

3.1. シナリオ

1秒ごとに現在の時刻を出力する簡単なPythonスクリプトを見てみましょう。

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
    sys.stdout.write(datetime.today().strftime("%H:%M:%S %p\n"))
    time.sleep(1)

このスクリプトを実行すると、stdoutに書き込まれた2つの連続するタイムスタンプの間に1秒の遅延が発生します。

$ ./time.py
 6:49:48 PM
 6:49:49 PM
 6:49:50 PM
 6:49:51 PM
 6:49:52 PM
 6:49:53 PM

3.2. 遅延リダイレクト

次に、 tee コマンドを使用して、このプロセスの出力をstdoutおよびtime.outファイルにリダイレクトします。

$ ./time.py | tee time.out

以前とは異なり、 stdout に長時間書き込まれた出力がないことに気付くでしょう。その後、大量の出力がstdoutに一度にダンプされます。

遅延は、Pythonが内部で使用するシステムライブラリであるglibcのLinuxのstdioバッファリングポリシーのために導入されました。 バッファリングポリシーにより、stdoutへの書き込みは4096バイトバッファを通過するため、ストリームへの書き込みに必要なI/O呼び出しの数が削減されます。 。

インタラクティブなアプリケーションの場合、このようなリダイレクトの遅延は許容されません。 それでは、リダイレクト遅延の問題を軽減する方法を見つけましょう。

3.3. 緩和

この問題の根本的な原因は、 stdout へのデータのフラッシュの遅延に関連しているため、この問題を解決する1つの方法は、アプリケーションコードでストリーミングするデータのタイムリーなフラッシュを確保することです。 :

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
    sys.stdout.write(datetime.today().strftime("%H:%M:%S %p\n"))
    sys.stdout.flush()
    time.sleep(1)

遅延が実際になくなったことを確認しましょう。

$ ./time.py | tee time.out
19:29:12 PM
19:29:13 PM

このシナリオでは、アプリケーションコードに直接アクセスできるため、変更することができました。

ただし、多くの場合、プログラムは実行可能バイナリである可能性があり、プログラムを変更するためのアクセス権がない可能性があります。 このようなシナリオでは、Linuxで unbufferコマンドを使用して、stdoutへのバッファリングされた書き込みによって引き起こされる遅延を解決できます。

スクリプトからsys.stdout.flush()メソッド呼び出しを削除し、unbufferコマンドを使用してリダイレクトコマンドを再実行してみましょう。

$ unbuffer ./time.py | tee time.out
19:34:22 PM
19:34:23 PM

現在、stdoutの書き込みに予期しない遅延がないことがわかります。

4. 結論

この記事では、プログラムの出力をstdoutstderrなどのストリームに同時にリダイレクトするいくつかのユースケースについて説明しました。 さらに、バッファリングによって引き起こされるリダイレクトの遅延の問題を解決するためのいくつかの戦略を学びました。