テキストファイルの末尾を見るだけなら tail -n 50 app.log、追記を継続表示するなら tail -f app.log を使います。ログローテーションでファイル名は同じまま実体が置き換わる環境では、GNU tail の tail -F app.log が適します。-F は名前を追跡し、ファイルが一時的に消えても再試行します。監視は通常 Ctrl+C で止めます。自動テストや既知の生成プロセスだけを待つ場合は --pid、確認間隔の調整には -s を組み合わせられます。 確認ポイント:tail -Fは名前を追って再openし、tail -fはdescriptorを追うというrotation時の差があります。
tail -nで開始行を決めてから追跡する
tail -n 20 -- /var/log/myapp/app.log
tail -n 1 -- ./result.txt
tail -n +101 -- ./records.txt
-n 20 は末尾20行、-n 1 は最終1行を表示して終了します。-n +101 の先頭プラスは「101行目から末尾まで」という別の意味です。ファイル名がハイフンで始まる可能性があるときは、オプション終端の -- を置きます。ファイルが20行未満なら存在する全行だけが表示されます。
追記をリアルタイムに近い形で見る
tail -f -- /var/log/myapp/app.log
実行直後に既定の末尾部分が表示され、その後に別プロセスが行を追記すると新しい行が続きます。停止は Ctrl+C です。端末を閉じるまで放置する前提にせず、調査終了時に明示的に止めます。最初の履歴を表示せず今後の追記だけ見たいなら tail -n 0 -f -- ファイル とします。
期待する出力が出ない場合は、まず別端末で ls -l -- ファイル と stat -- ファイル を確認し、対象パス、所有者、更新時刻、サイズが変化しているかを見ます。tail が開けないときの Permission denied は読み取り権限の問題です。権限を広げる前に、ログ閲覧用グループ、sudo運用、サービスの正式なログ参照方法を管理方針で確認します。
-fと-Fをローテーション方式で選ぶ
-f は通常、開いたファイル記述子を追跡します。ログが別名へ rename され、新しい同名ファイルが作られるローテーションでは、古い実体を追い続けて新しい app.log の行が見えないことがあります。-F は --follow=name --retry に相当し、名前を定期的に開き直すので、この方式に向きます。
tail -n 100 -F -- /var/log/myapp/app.log
一方、copytruncate方式では同じファイルをコピー後にサイズ0へ切り詰めます。GNU tail は切り詰めを検出すると、先頭側から新しいデータを読み直します。端末に「file truncated」相当の診断が出ることがあります。rename+create と copytruncate を同じ現象と考えず、実際のローテーション設定とinodeの変化を確認します。
-F はファイルがまだない場合にも再試行するため、タイプミスしたパスを永久に待ち続ける可能性があります。「has become accessible」などの診断が出た後に追跡が始まるのが正常です。意図したログ名か、親ディレクトリを検索できる権限があるか、ローテーション後の新規ファイルを読めるかを確認します。
監視間隔と生成プロセスの終了を指定する
tail --pid=4321 -s 0.5 -f -- ./worker.log
GNU tail の --pid=4321 は、指定PIDが存在しなくなった後に tail も終了させるための指定です。-s 0.5 は反復のスリープ間隔を0.5秒にします。PIDは別プロセスへ再利用され得るため、長時間後に番号だけを手入力する使い方ではなく、自分が今起動した生成プロセスの $! を直ちに渡すテストに向きます。
--pid は「そのPIDの出力だけを選別する」機能ではありません。tail は指定ファイルの追記を読み、PIDは終了条件にだけ使われます。また実装によって利用できないオプションがあるため、GNU Coreutils以外では tail --help とOSのmanページを確認します。
再現できる一時ファイルテスト
次は現在のシェルで一時ファイルを一つ作り、初期行を書き、バックグラウンドの生成側が2行を追記します。tail は生成側の正確なPIDを --pid に受け取り、その終了後に止まります。最後に作成した一時ファイルだけを削除します。業務ログを使わず、tail -f の動きを再現できます。
tmp_file=$(mktemp)
printf "%s\n" "start" >"$tmp_file"
( sleep 1; printf "%s\n" "step-1" >>"$tmp_file"; sleep 1; printf "%s\n" "done" >>"$tmp_file" ) &
writer_pid=$!
tail --pid="$writer_pid" -s 0.2 -f -- "$tmp_file"
wait "$writer_pid"
printf "writer exit=%s\n" "$?"
rm -- "$tmp_file"
期待出力は start、約1秒後の step-1、さらに約1秒後の done、最後の writer exit=0 です。tail のポーリング都合で生成側終了からtail終了まで最大で指定間隔程度の遅れがあり得ます。途中で Ctrl+C を押した場合は一時ファイルが残るので、printf "%s
" "$tmp_file" でパスを確認し、その正確な一件だけを片付けます。
grepなどのパイプを挟むときの注意
tail -n 0 -F -- /var/log/myapp/app.log | grep --line-buffered -F -- "ERROR"
この例は追記行のうち文字列ERRORを含む行だけ表示します。パイプの途中に awk、sed、独自プログラムを加えると、そのプログラムの標準出力が端末ではなくパイプになり、ブロックバッファリングで表示が遅れることがあります。tailが遅いとは限りません。GNU grep では --line-buffered が使えますが、処理性能が下がる場合があり、他実装にはないことがあります。
切り分けは、まずパイプなしのtailで追記が届くことを確認し、次にgrep一段だけを加えます。grepの条件に一致しないだけならtailは動作しています。標準エラーを標準出力へ混ぜるとローテーション診断までフィルター対象になるため、必要性を決めずに 2>&1 を加えません。パイプ全体は Ctrl+C で止め、残存プロセスがないか jobs -l で確認します。
監視できないときの切り分け
No such file or directory:パス、大小文字、マウント、ローテーション中の一時的不在を確認する。Permission denied:ファイルの読み取り権限と親ディレクトリの検索権限を確認する。- 既存行だけ出て追記が出ない:書き込み先が別ファイル、別コンテナ、別inodeでないか確認し、rename方式なら
-Fを試す。 - 行がまとめて遅れて出る:生成側のflushとパイプ途中のバッファリングを調べる。
- 同じ行が再表示される:切り詰め検出、tail再起動、複数監視プロセスの有無を確認する。
一時監視fileとprocessを片付ける
tail自体は対象ファイルを読み取るだけで、ログを変更しません。そのため実ログ監視にロールバックはありません。一時テストでは一時ファイルの作成と追記が変更に当たり、最後の rm -- "$tmp_file" がその一件だけを片付けます。実ログの権限やローテーション設定をtailのために変更する手順は含めません。監視が終わったら Ctrl+C、jobs -l、必要なら正確なPIDの確認で終了状態を確かめます。
tail監視を終了する前の照合
- 過去の確認は
-n、継続追跡は-f、名前の再オープンが必要なら-Fと選んだ。 - rename+create と copytruncate の差を確認した。
--pidを終了条件、-sを確認間隔として理解した。- パイプなしで追記を確認してからフィルターのバッファリングを調べた。
- 監視は Ctrl+C で止め、作成した一時ファイル以外を変更していない。

コメント