LoginSignup
37
27

More than 5 years have passed since last update.

bashのリダイレクトの話

Last updated at Posted at 2016-10-22

はじめに

先日、>/dev/null 2>&1は順序が逆だとダメという投稿をしましたが、内容がダメダメだったので、再度まとめ直しました。

ざっくりした理解

ファイルディスクリプタ

入出力インターフェースを識別する識別番号。デフォルトでは下記の通りの設定となっている。

番号 インターフェース
0 標準入力
1 標準出力
2 標準エラー出力

出力リダイレクト

コマンドの実結果をファイルfileに書き出す。nにより、出力インターフェースを指定する。(n=1は標準出力、n=2は標準エラー出力)

cmd [n]>file

入力リダイレクト

コマンドへ渡す入力をファイルfileから読み込む。nにより、入力インターフェースを指定する。(n=1は標準入力)

cmd [n]<file

出力を別の出力へリダイレクトする

nの出力先をmと同じに設定する。

cmd [n]>&[m]

ちゃんと調べた結果

ファイルディスクリプタ(IT用語辞典より)

ファイルディスクリプタとは、プログラムがアクセスするファイルや標準入出力などをOSが識別するために用いる識別子。0から順番に整数の値が割り当てられる。OSによってはファイルディスクリプタにバッファ管理機能なども含めた「ファイルハンドル」と呼ばれる管理体系が存在する。

ファイルディスクリプタには、識別子とともにファイル名、ファイルサイズ、プログラムが操作中のファイル内の位置、ファイル作成、更新日時などの情報が含まれており、OSは識別子によってどのファイルを操作するかを判断する。

通常、0:標準入力(stdin)、1:標準出力(stdout)、2:標準エラー出力(stderr)の3つはOS(シェル)が最初に用意するため、プログラムがファイルをオープンすると「3」から順番にディスクリプタが割り当てられる。

プログラム(プロセス)がアクセスする標準入出力やファイルに関連する情報を保持した識別子。保持する情報には、入出力先のファイルパスが含まれる。それぞれの識別子に整数値の識別番号が割り振られる。(ただの識別番号ではなく、ファイルディスクリプタという識別子オブジェクトに識別番号がついているイメージ)

リダイレクト(ちゃんとman bashを読んでみる)

プロセスの各入出力に対応するファイルディスクリプタを新規作成/複製することにより、入出力先を変更すること。プロセスが生成された時点では、標準入力に対応するファイルディスクリプタが0に、標準出力に対応するファイルディスクリプタが1、標準エラー出力に対応するファイルディスクリプタが2となっている。また、3以上の番号にファイルディスクリプタを設定することもできる。

入出力リダイレクト

ファイルwordを入力元/出力先とする新規の入力ファイルディスクリプタを作成し、識別番号nをつける。

cmd [n]>word # Redirecting Output
cmd [n]<word # Redirecting Input

ファイルディスクリプタの複製

識別番号mのファイルディスクリプタを複製し、識別番号nをつける。

cmd [n]>&[m] # Duplicating Output File Descriptor
cmd [n]<&[m] # Duplicating Input File Descriptor

【注意】

標準エラーのリダイレクトによると、複製するといいつつ、内部処理としては、nを示すポインタにmのポインタを設定している処理のようです。

特別なファイルパス

上記のwordには、ファイルのパスだけでなく、下記の特別なパスが使用できる。

パス 内容
/dev/null 虚無(ここへ向けてリダイレクトすると出力されない)
/dev/fd/n 識別番号nで表されるファイルディスクリプタ
/dev/stdin 標準入力
/dev/stdout 標準出力
/dev/stderr 標準エラー出力

cmd /dev/null 2>&1の順序問題(再び)

>はファイルディスクリプタの新規作成、>&はファイルディスクリプタの複製、として再度cmd /dev/null 2>&1の順序問題を確認する。

元の設定

+-----+
|    1|-----/dev/stdout
|     |
|    2|-----/dev/stderr
+-----+

【誤】cmd 2>&1 >/dev/null

2>&1で、21に複製

+-----+
|    1|-----/dev/stdout
|     |       |
|    2|-------+  (/dev/stderr)
+-----+

>/dev/nullで、新規に1/dev/nullを設定

         +--/dev/null
+-----+  |
|    1|--+  /dev/stdout
|     |       |
|    2|-------+  (/dev/stderr)
+-----+

【正】cmd >/dev/null 2>&1

>/dev/nullで、新規に1/dev/nullを設定

         +--/dev/null
+-----+  |
|    1|--+  (/dev/stdout)
|     |
|    2|-----/dev/stderr
+-----+

2>&1で、21に複製

         +--/dev/null
+-----+  |    |
|    1|--+    |  (/dev/stdout)
|     |       |
|    2|-------+  (/dev/stderr)
+-----+

>a.txt 2>&1>a.txt 2>a.txtは違う問題

こちらも、再度確認する。

元の設定

+-----+
|    1|-----/dev/stdout
|     |
|    2|-----/dev/stderr
+-----+

cmd >a.txt 2>&1

>a.txtで、新規に1a.txtを設定

         +--./a.txt
+-----+  |
|    1|--+  (/dev/stdout)
|     |
|    2|-----/dev/stderr
+-----+

2>&1で、21に複製

         +--./a.txt
+-----+  |    |
|    1|--+    |  (/dev/stdout)
|     |       |
|    2|-------+  (/dev/stderr)
+-----+

cmd >a.txt 2>a.txt

>a.txtで、新規に1a.txtを設定

         +--./a.txt
+-----+  |
|    1|--+  (/dev/stdout)
|     |
|    2|-----/dev/stderr
+-----+

2>a.txtで、新規に2a.txtを設定(別々に新規のファイルディスクリプタを生成するため、お互いに同じファイルに上書き合ってしまう)

         +--./a.txt
+-----+  |
|    1|--+  (/dev/stdout)
|     |
|    2|--+  (/dev/stderr)
+-----+  |
         +--./a.txt

参考

man bashによれば、bashでは下記の構文が利用できるらしい。これは>word 2>&1のsemantically equivalentだそうなので、bashの場合はこれを使うと間違いがなさそう。

cmd &>word

また、>>&&>以外にも、>|<>など様々リダイレクトの種類があるらしい。
気になる方は、あなたの知らない>|と<>の使い方などを参考に。

37
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
37
27