Linuxでコマンド出力を逆順にしたいときは、「現在の行順を後ろから前へ反転したい」のか、「値を比較して降順に並べ替えたい」のかを最初に区別します。GNU coreutilsでは前者にtac、後者にsort -rを使います。目的を取り違えると、ログの時系列や同順位データの順序が変わるため、入力形式と並びの意味を確認してから選びます。
元の行順を反転するならtac
tacは、既定では改行で区切られたレコードをファイル末尾側から出力します。catの逆順版と考えると分かりやすく、内容を比較してソートするわけではありません。入力がA、C、Bなら、出力はB、C、Aです。ファイルを変更せず標準出力へ書くため、まず画面や別の読み取り処理で結果を確認できます。
tac -- /var/log/myapp/events.log
printf '%s\n' first second third | tac
複数ファイルを指定すると、GNU tacは各ファイルを個別に反転して順番に連結します。すべてを一つの入力として全体反転したい場合とは結果が異なります。ファイル名に空白や先頭ハイフンがあっても一引数として扱えるよう引用と--を使います。読み取り権限エラーや破損した入力があれば、標準エラーと終了ステータスを確認します。
最近のN行だけを新しい順に見る
巨大ログ全体ではなく末尾の一定件数だけを逆順表示したい場合は、tail -nで範囲を絞ってからtacへ渡します。通常、ログ末尾が最新行なら、新しいイベントから古いイベントへ確認できます。ただし、複数行スタックトレースや継続行を一つのイベントとして扱うログでは、単純な行反転によりイベント内部も逆転します。
tail -n 200 -- /var/log/myapp/events.log | tac
tail -n 50 -- /var/log/myapp/events.log | tac | less
lessを使うときは端末制御文字や色コードを含む入力の扱いに注意します。信頼できないログを端末へ直接表示すると、制御シーケンスが見た目を変える可能性があります。ログ形式が一イベント一行であることを確認し、JSON Linesならパーサーで妥当性を確認してから必要なフィールドだけを表示する方法も検討します。
降順ソートはsort -rを使う
sort -rは入力行の大小を比較し、その比較結果を逆向きにします。元の行順を反転するtacとは目的が違います。数値の大きい順なら-n -r、人が読む容量表記なら-h -r、バージョン番号なら-V -rなど、データ型に合う比較方法を指定します。
printf '%s\n' 2 10 3 | sort -n -r
sort -t ',' -k 3,3nr -- report.csv
LC_ALL=C sort -r -- names.txt
CSVの例は、カンマや改行を引用フィールド内に含まない単純形式だけを想定しています。本格的なCSVは専用パーサーを使います。sortの照合順序はロケールの影響を受けるため、再現可能なバイト順が必要ならそのコマンドだけLC_ALL=Cを指定します。ただし日本語として自然な順序になるとは限らず、要件に合うロケールと文字コードを選びます。
同順位の元順を保つにはstableを指定する
キーが同じ行について元の相対順序を保ちたい場合は、GNU sort --stableを使います。通常のsortは指定キーが同じとき、最後の比較として行全体を使うため、同順位の順序が変わる場合があります。時刻の新しい順へ並べる前に、同じ時刻内の到着順を維持したい、といった要件では安定性を明記します。
sort --stable -t ',' -k 2,2r -- events.csv
逆順表示という要望が「最後に来たものを先に見る」ならtac、「最大値を先に見る」ならsortです。さらに「時刻フィールドで新しい順」なら、時刻文字列の形式が辞書順と時系列順で一致するかを確認します。タイムゾーンや可変桁の日付が混ざる場合は、日時を正規化してから比較します。
NUL区切りのファイル名を逆順にする
Linuxのファイル名には改行を含められるため、findの結果を行単位で反転すると名前の境界が壊れます。GNU tacでは空の区切り文字を指定するとASCII NULをレコード区切りとして扱えます。find -print0などNUL区切りを出す側と、NUL区切りを受け取る側を揃えます。
find /srv/reports -maxdepth 1 -type f -print0 | tac --separator='' | while IFS= read -r -d '' file; do
printf 'file=%q\n' "$file"
done
この例はBashとGNU tacを前提に、名前をシェル再利用向けの引用形式で表示するだけです。出力を別コマンドへ渡す場合も、文字列へ連結せず引用した"$file"として渡します。NUL区切りはテキスト表示には見えないため、途中のツールがNUL対応かを確認します。
tacがない環境ではawkで行を保持する
tacはGNU coreutilsで一般的ですが、最小構成OSや他のUnix系環境にない場合があります。POSIX系のawkで各行を配列へ保存し、ENDで逆向きに出す方法は簡潔です。ただし入力全体をメモリへ保持するため、大きなファイルには適しません。また、行区切りを前提にするので改行を含むレコードやバイナリデータには使えません。
awk '{ lines[NR] = $0 } END { for (i = NR; i >= 1; i--) print lines[i] }' input.txt
短いテキストで移植性が必要な場合に限定し、ファイルサイズと最大行長を確認します。巨大入力では、利用環境のtacまたはログ保管基盤の逆順検索機能を使います。sedで全行を保持するワンライナーも見かけますが、実装差やメモリ消費が分かりにくいため、用途と限界を説明できる方法を選びます。
パイプラインの失敗を見落とさない
Bashでは、既定のパイプライン終了ステータスは最後のコマンドの状態です。そのためproducer | tacでproducerが失敗しても、tacが正常終了すると全体が成功に見える場合があります。重要なスクリプトではset -o pipefailの影響を理解したうえで有効にするか、各処理の終了状態を明示的に取得します。
set -o pipefail
if generate_report | tac; then
printf '%s\n' 'reverse display completed'
else
status=$?
printf 'pipeline failed: status=%s\n' "$status" >&2
fi
pipefailは失敗を見つけやすくしますが、意図的に途中で読み取りを終えるコマンドとの組み合わせなどで既存スクリプトの判定を変える可能性があります。局所的に適用し、正常系と入力側失敗、出力側失敗をテストします。エラーメッセージを非表示にせず、空出力と取得失敗を区別します。
大きな入力と一時領域に注意する
逆順出力は入力の末尾を知る必要があります。GNU tacは、パイプなどシークできない入力を一時領域へバッファーします。既定ではTMPDIRまたは/tmpが関係するため、巨大なストリームでは一時容量、権限、I/O負荷、機密データの保管方針を確認します。終わりのないストリームをtacへ渡しても、通常は期待する逐次表示になりません。
継続監視で最新イベントを見たい場合は、逆順化よりtail -fやログ管理システムの降順クエリを使います。既存ファイルの最後の一部を確認するならtail -n N | tacで入力を限定できます。機密ログを一時ファイルへ置けない環境では、ツールの実装と保存先を確認してから実行します。
目的別の選び方
- 現在の行順を完全に反転する:
tac - 末尾N行だけを新しい順で読む:
tail -n N | tac - 数値が大きい順に並べる:
sort -n -r - 指定キーの降順で同順位の元順を保つ:
sort --stable -k ...r - 改行を含み得るファイル名を扱う: NUL区切り対応の生産側・tac・消費側を組み合わせる
- 終わりのないログを監視する: 逆順化せず
tail -fまたはログ基盤を使う
確認チェックリスト
- 逆転したいのが入力位置か、値による比較順かを決めた。
- 一行一レコードか、複数行イベントやNUL区切りかを確認した。
- sortでは数値・文字列・日時・バージョンに合う比較指定を選んだ。
- ロケールと同順位の安定性を明示した。
- パイプライン入力側の失敗と空出力を区別した。
- 巨大な非シーク入力で一時領域が必要になる点を確認した。
逆順表示は短いコマンドで実現できますが、意味の違いが結果を左右します。位置を反転するtac、比較結果を降順にするsort -r、対象範囲を絞るtailを使い分け、レコード区切り、ロケール、終了ステータス、一時領域まで確認すると、安全で再現可能な処理になります。

コメント