通常、サーバー上での作業時には端末を開いて作業を行います。実行されたコマンドの出力は概ねコンソール画面上に表示されますが、運用上の要件により、作業内容のログや過去の出力内容などを取っておきたいことがあります。そんな時に役立つのがリダイレクトです。
リダイレクトとは
Unix系のOSにおいては、コマンドの実行結果やエラー結果の出力先、あるいはデータ入力元をデフォルトから変更することを指します。
リダイレクトで出力先を変えてファイルなどに結果を残しておくと、何か問題が起こった時の調査に役立つ為、このリダイレクトはよく用いられます。そして、この「入力元」や「出力先」を変えることをリダイレクションといいます。
リダイレクトとは、方向を変える、宛先を変える、という意味を持つ言葉なのですが、うまく日本語訳として定着したものはなくカタカナで呼ぶのが一般的です。
ファイルディスクリプタとは
UNIX系OSには、「ファイルデイスクリプタ」というものがあり、「プロセス」と「使用するファイル」とを結び付けています。
何かのプロセスがファイルを作成して書き込みを行う場合など、必ずプロセスはファイルディスクリプタを使って対象となるファイルヘアクセスしています。
ファイルディスクリプタは、ファイルディスクリプタ【n】番というように、数値で表現されます。下記に代表的な3つのファイルディスクリプタを記載します。
ディスクリプタ | 意味 | 動作 |
---|---|---|
0 | 標準入力 | キーボードからの入力 |
1 | 標準出力 | 端末画面への出力 |
2 | 標準エラー | 端末画面への出力 |
このファイルディスクリプタのうち、0番、1番、2番の3つは予約されているもので、何かプロセスが動作する時は、必ずこの3つのファイルデイスクリプタが利用可能な状態になります。
リダイレクトの方法
リダイレクトをする場合、コマンドを記述した行の末尾に、以下のような記号を用いることで変更が可能です。
- < (標準入力の変更)
- << (標準入力の変更)
- > (標準出力の変更)
- >> (標準出力の変更、アペンドモードで)
- 2> (標準エラー出力の変更)
また、これらの他に「&(アンパサンド)」を用いた記述方法もあります。
それぞれ見ていきましょう。
標準入力のリダイレクト 「<」
通常、コマンド実行時の標準入力は、キーボードが一般的です。そこで「コマンド < ファイル名」のように「<(小なり)」を使って記述することにより、標準入力をファイルへリダイレクトすることが可能です。
つまりキーボードから入力する代わりに、ファイルの内容を標準入力として扱うと言うことです。下記の「stdin.txt」を使って説明します。
stdin.txt
aaa
bbb
「標準入力」をファイルへリダイレクトして、ファイル中身の行数を「wc -l」コマンドを使用してコンソールへ出力させます。※「wc -l」コマンドは、行数をカウントするコマンドです。
# stdin.txtの行数を数える
$ wc -l < stdin.txt
1 2 3 |
[root@CentOS7 ~]# wc -l < stdin.txt 2 [root@CentOS7 ~]# |
「stdin.txt」には、2行の文字列があることが分かります。
重要なのは矢印の方向(向き)ではなく「<(小なり)」とは、「標準入力の意」と言う事です。「wc -l < stdin.txt」は、「wc -l」コマンドへファイルの中身を標準入力として(キーボードに代わって)入力(渡す)しています。矢印の向きで理解してしまうと、後述の標準エラーでパニクリます!
標準入力のリダイレクト・ヒアドキュメント 「<<」
基本的な用法は「<」と同じですが、次のように「<<」を使うことで、ファイルを使わずに「そこに書いてあるテキスト」をそのまま入力とすることができます。
$ cat << EOF
echo "実行しているマシンのホスト名は$(hostname -s)です。"
EOF
1 2 3 4 |
[root@CentOS7 ~]# cat << EOF > echo "実行しているマシンのホスト名は$(hostname -s)です。" > EOF echo "実行しているマシンのホスト名はCentOS7です。" |
この用法を「ヒア・ドキュメント(Here Document)」と言います。「<<」のあとに任意のワードを書くと、次にその同じワードが出てくる行までに書かれたテキストを標準入力からのものだと解釈します。
「ヒア・ドキュメント(heredocument)」の詳細については、以前の記事でも取り上げているので、下記のリンクページを参照してください。
標準出力のリダイレクト 「>」
逆に、端末の画面上に出るスクリプトの実行結果をファイルに残す場合もあります。
そのような時にはどうすればよいでしょうか。
この場合は、「>(大なり)」記号を用いると、ファイルへの出力が可能になります。
# stdout.txtへ文字列を出力する
$ echo "hello" > stdout.txt
実行結果は、標準出力先が画面ではなく、「stdout.txt」というファイルになるため、画面には何も表示されませんが、「stdout.txt」と言うファイルが作成され、「stdout.txt」ファイルの中身へ"Hello"は記述されているはずです。見てみましょう。
1 2 3 4 5 6 |
[root@CentOS7 ~]# echo "hello" > stdout.txt [root@CentOS7 ~]# ls -l -rw-r--r--. 1 root root 8 4月 20 10:54 stdin.txt -rw-r--r--. 1 root root 6 4月 20 11:02 stdout.txt [root@CentOS7 ~]# cat stdout.txt hello 👈「>」を使って「stdout.txt」へ記述された文字列 |
きちんと「stdout.txt」の中に"hello"という文字列が記述されています。
標準出力のリダイレクト 「>(大なり)」を使って文字列を記述した場合、既に「stdout.txt」というファイルが存在していれば、上書きされ元の内容はなくなってしまいます。
逆にファイルが存在していない場合は、新規に作成します。
※ ファイルのアクセス権(パーミッションや所有者など)はそのまま引き継がれ変更しません。
何度実行されても、同じ結果(冪等性)が要求されるような各種設定ファイル等への記述に、この「>」が良く使用されます。
ファイルを上書きするのではなく、今ある内容の後ろに結果を追加したい場合には、標準出力のリダイレクト・アペンド(追記)モード 「>>」を使います。
標準出力のリダイレクト・アペンド(追記)モード 「>>」
標準出力のリダイレクト方法にはもう一つあり「>」記号を二つ重ね「>>」と表現します。
「>」一つのものと異なるのは、「>>」でリダイレクトした場合、すでに対象のファイルに何かが書かれていた場合、それらを上書きせずに最後の行に追記して出力する点です。
逆に「>」だと既にある内容は消去して一から書き直します。
先ほどの「>」の検証で使用したファイル「stdout.txt」へ文字列 "world" を追加してみます。
# stdout.txtへ文字列 "world" を追記する
$ echo "world" >> stdout.txt
1 2 3 4 |
[root@CentOS7 ~]# echo "world" >> stdout.txt [root@CentOS7 ~]# cat stdout.txt hello world 👈「>>」を使って「stdout.txt」へ追記された文字列 |
「stdout.txt」ファイルの文字列 "hello" の下部へ文字列"world"が追記されています。
時系列に出力されるログファイルの記述に、この「>>」が良く使用されます。
「>>」を使用した場合も「stdout.txt」ファイルが存在しなかった場合は、「>」のときと同じようにファイルが新規に作成します。
標準エラー(コマンドの出すエラーメッセージ)を同じようにファイルに書き込みたい場合は、標準エラー出力のリダイレクト 「2>」(その番号である2を付けてリダイレクト)を使用します。
標準エラー出力のリダイレクト 「2>」
普通、コマンドの文法エラー時には画面上にその旨が表示されます。
一見標準出力のリダイレクトでこちらもファイルに出力されるように見えますが、違います。
内部的には結果を出力する標準出力とエラー内容を出力する標準エラー出力が分けられており、その2つが画面という同じ出力先を指定しているために両方とも画面に出力されています。
標準エラー出力のみを別のファイル等に出力したい場合は「2>」の記号を用いて記述します。
# 存在しないファイル「nothing.txt」を使用した例
# 標準出力の向け先は「stdout.txt」
$ ls -l nothing.txt > stdout.txt
1 2 3 4 |
[root@CentOS7 ~]# ls -l nothing.txt > stdout.txt ls: nothing.txt にアクセスできません: そのようなファイルやディレクトリはありません [root@CentOS7 ~]# cat stdout.txt 👈 何も入力されていない |
「ls -l」コマンドで画面上へエラーが出力されてしまいました。
この場合、標準出力は何もない為「stdout.txt」へは何も記述されていません。
では次は、標準エラーとして記述してみます。
# 存在しないファイル「nothing.txt」を使用した例
# 標準エラーの向け先は「stdout.txt」
$ ls -l nothing.txt 2> stdout.txt
1 2 3 |
[root@CentOS7 ~]# ls -l nothing.txt 2> stdout.txt [root@CentOS7 ~]# cat stdout.txt ls: nothing.txt にアクセスできません: そのようなファイルやディレクトリはありません |
今度は、標準エラーが「stdout.txt」へ記述されています。
しかし、これですと標準出力と標準エラーは別々に処理する必要が出てきて非常に面倒くさいですよね?
そんな場合「2>&1」のように、ファイルディスクリプタの番号を明示的に指定すれば、そのファイルデイスクリプタが向いている出力先を別のファイルに変更することが可能です。
ファイル記述子「&」を用いた表記
標準エラーと標準出力の出力先を同じにしたい場合は、「&」(アンパサンド)を用いて以下のように記述します。
# 「&」(アンパサンド)を用いて標準エラーと標準出力の出力先を一緒にする
$ ls -l nothing.txt stdout.txt > stdout.txt 2>&1
1 2 3 4 |
[root@CentOS7 ~]# ls -l nothing.txt stdout.txt > stdout.txt 2>&1 [root@CentOS7 ~]# cat stdout.txt ls: nothing.txt にアクセスできません: そのようなファイルやディレクトリはありません -rw-r--r--. 1 root root 115 4月 20 12:32 stdout.txt |
「&」を用いて標準エラーと標準出力の出力先が「stdout.txt」へ統一されました。
まとめ
本記事をもって【Shellの基礎知識】は終了です。
これまでの内容で、簡単なシェルスクリプトなら記述できるようになっているかと思いますが、シェルは非常に奥の深いスクリプト言語です。
これまでに説明しきれなかった用法も沢山ありますので、これを機会にぜひシェルマスターを目指してください。シェルを極めてるにつれて、サーバー構築の生産性は想像以上に向上していくのを実感します。
現場で一目置かれる存在になることは間違いありません。
お疲れさまでした!
Shellスクリプト基礎知識(全11記事+1)
├─シェルスクリプトの基本事項!
├─変数と特殊変数について!
├─演算子「算術演算子」「比較演算子」について!
├─条件分岐「if」「case」について!
├─ループ処理「for」「while」について!
├─文字列置換「bash」「sed」について!
├─複数行のテキスト出力!ヒアドキュメントについて!
├─書式?戻り値?シェルスクリプト内の関数について!
├─シェルの組み込みコマンドについて!
├─クォートとは?コマンド置換とは?実現方法と内容の違いについて!
└─リダイレクトとは?標準入力・出力、標準エラー出力等について!
(補足)シェルスクリプトの設計書とは?必要な項目や書き方等を解説!