標準出力と標準エラー出力を入れ替える方法@bash

標準出力と標準エラー出力を入れ替える方法を、結論だけを先に書くと

3>&2 2>&1 1>&3

というリダイレクトを行うと可能です。

 

以下は、あまりリダイレクトに詳しくない人向けに説明書いてみました。

ファイルディスクリプタは、0番が標準入力、1番が標準出力、2番が標準エラー出力です。例えば、index.html というファイルがあり、abc.htmlが無いディレクトリで以下のコマンドを実行したとすると、以下のように表示されます。

# ls -l index.html abc.html
ls: cannot access abc.html: そのようなファイルやディレクトリはありません
-rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

このとき、見た目はわかりませんが、結果1行目の”ls: cannot access abc.html: そのようなファイルやディレクトリはありません” というのが標準エラー出力、2行目の”-rw-r–r– 1 root root 0  12月 30 15:15 2012 index.html” が標準出力になります。

この二つの違いを知るには、単純に標準出力をリダイレクトしてみるとわかります。

# ls -l index.html abc.html > log.txt
ls: cannot access abc.html: そのようなファイルやディレクトリはありません

このように、”>” は標準出力だけをリダイレクトするため、標準出力だけがlog.txtに書き込まれ、標準エラー出力はそのまま画面に出力されます。

# ls -l index.html abc.html 2> log.txt
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

 

次は、パイプを使って結果を処理することを考えたいと思います。今回は、よく使われるgrepを使います。

# ls -l index.html abc.html | grep index
 ls: cannot access abc.html: そのようなファイルやディレクトリはありません
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

はい。知っている人には当たり前ですが、”index”という文字列をgrepしたのに、エラーの内容も表示されました。これは、パイプが標準出力だけをgrepの入力として処理し、標準エラー出力はパイプに渡されずにそのまま画面に出力されてるためです。

標準エラー出力もgrepで処理したい場合は、以下のように標準エラー出力を標準出力にリダイレクトしてあげることで解決します。

# ls -l index.html abc.html 2>&1 | grep index
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

 

では標準エラー出力だけをgrepし、標準出力はそのまま画面に出力させたい場合はどうすればよいのでしょうか?

言い換えれば、標準出力と標準エラー出力を入れ替えるにはどうすればよいでしょうか?

 

bashには、リダイレクトを入れ替えるという機能はありません。そこで登場してもらうのが、3番のファイルディスクリプタです。考え方は、標準出力(1番)を一時的に3番に退避し、標準エラー出力(2番)を1番にリダイレクト、退避していた3番を2番にリダイレクトするという方法です。これをコマンドで書くとこのようになります。
※説明の順番を以下のようにコマンドの後ろから書いていく必要があります。

# ls -l index.html abc.html 3>&2 2>&1 1>&3
 ls: cannot access abc.html: そのようなファイルやディレクトリはありません
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

見た目では分かりませんが、これで入れ替わりました。先ほどと同じようにgrepしてみましょう。

# ls -l index.html abc.html 3>&2 2>&1 1>&3 | grep index
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

はい。最初のgrepの例と異なり、”ls: cannot access abc.html: そのようなファイルやディレクトリはありません”だけがgrepの対象になり、、”-rw-r–r– 1 root root 0  4月 30 15:54 2012 index.html” は標準エラー出力として出力されているためそのまま画面に出力されました。

この例だと、grepでマッチしているように見えるので、違う文字列をgrepしてみます。

# ls -l index.html abc.html 3>&2 2>&1 1>&3 | grep def
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html
# ls -l index.html abc.html 3>&2 2>&1 1>&3 | grep abc
 ls: cannot access abc.html: そのようなファイルやディレクトリはありません
 -rw-r--r-- 1 root root 0  4月 30 15:54 2012 index.html

ちゃんと入れ替わってるでしょ?

タイトルとURLをコピーしました