Release: 2006/03/18
Update: 2012/05/17
コマンドの実行結果は通常、標準出力であるディスプレイに出力される。 この実行結果はリダイレクション(>、>>)やパイプ( | )を使用することにより、ディスプレイではなくテキストファイルやコマンドに対して出力するように切り替えることができる。 また、リダイレクションはコマンドの出力先をテキストファイルに切り替える以外にも、これとは逆にコマンドへの入力元をテキストファイルに切り替えることもできる。
なお、リダイレクションとパイプは次のように使い分ける。
# コマンドの出力を file へ上書きする command >file # コマンドの出力を file へ追記する command >>file
→ コマンドの実行結果の出力先を、ディスプレイからファイルへ切り替えたい場合は、リダイレクションを使用する。
コマンドに続けてリダイレクション記号(「>」、「>>」)と出力先ファイルを指定することで、コマンドの実行結果をファイルへ書き込むことができる。 ファイルへの上書きでの出力は「>」(既存の内容は全て消去される)、追記での出力は「>>」(既存の内容の最下行に追加される)を使用する。 上書き(>)も、追記(>>)もファイルが存在しなかった場合は、新規にファイルが作成された上で出力される。
$ cat test.txt hoge hoge ※↑出力先ファイル内容。 $ date >test.txt ※↑ date コマンドの実行結果をファイルへ上書きする。 $ cat test.txt 2007年 5月 27日 日曜日 01:58:32 JST ※↑コマンドの実行結果で「hoge hoge」が上書きされている。 $ date >>test.txt ※↑ date コマンドの実行結果をファイルへ追記する。 $ cat test.txt 2007年 5月 27日 日曜日 01:58:32 JST 2007年 5月 27日 日曜日 01:58:54 JST ※↑先に実行された date コマンドの実行結果に追記される形で出力されている。
リダイレクション記号(>、>>)の前後のスペースはあっても無くてもよいが、前方にのみスペースを入れるのが筆者の推奨。
command >file command >>file
ファイルの中身を全てクリアし、0バイトのファイルにするにはリダイレクションを利用する。
>file : >file cat /dev/null >file
実際の実行結果は以下の通り。
$ ls -l file* -rw-rw-r-- 1 sunone sunone 122 5月 27 02:05 file1 -rw-rw-r-- 1 sunone sunone 122 5月 27 02:05 file2 -rw-rw-r-- 1 sunone sunone 122 5月 27 02:05 file3 ※↑file1 ~ file3 はいずれも122バイト。 $ >file1 $ : >file2 $ cat /dev/null >file3 ※↑各コマンドを file1 ~ file3 に対して実行する。 $ ls -l file* -rw-rw-r-- 1 sunone sunone 0 5月 27 02:05 file1 -rw-rw-r-- 1 sunone sunone 0 5月 27 02:06 file2 -rw-rw-r-- 1 sunone sunone 0 5月 27 02:06 file3 ※↑実行後には全て0バイトになっている。
いずれの方法でも、実行後に file は0バイトになっていることが分かる。
リダイレクションはコマンドの出力先をファイルへ変更するのみならず、その逆も可能である。 つまり、コマンドへの入力元をファイルに切り替えることもできる。
# ファイル内容をコマンドへ渡す command <file
→ 入力先コマンドに対して「<」を使用して、入力元ファイルを指定する。
コマンドに続けてリダイレクション記号(「<」)と入力元ファイルを指定することで、ファイル内容に対してコマンドを実行することができる。 「<」はコマンドへのリダイレクションではなく、 ファイルディスクリプタ 0番へのリダイレクションである。
$ cat test.txt 11111 22222 33333 44444 55555 ※↑入力元となるファイルの内容。 $ wc -l <test.txt 5 ※↑ファイルの行数を wc コマンドでカウントする(別にリダイレクションを使う必要は無いが)。
リダイレクションでファイルを入力元として使用する場合に、最もよく使用されるコマンドは while 文であろう。 下にリダイレクションと while 文を使用した例を示す。
redirection.sh
#!/bin/bash echo "while文にリダイレクション" # readコマンドで標準入力から1行読み込む while read line do echo "$line" done <$1 echo "カレントシェルにリダイレクション" # exec コマンドを使用しカレントシェルの標準入力へリダイレクトする exec <$1 # readコマンドで標準入力から1行読み込む while read line do echo "$line" done exit 0
このシェルスクリプト redirection.sh の実行結果は、以下の通りとなる。
$ ./redirection.sh test.txt while文にリダイレクション 11111 22222 33333 44444 55555 カレントシェルにリダイレクション 11111 22222 33333 44444 55555
command1 | command2 command1 | command2 | command3 command1 | command2 | … | commandN
→ コマンドの実行結果を、他のコマンドへ引き渡したい場合はパイプを使用する。
コマンドをパイプで結合すると、コマンドは左から実行され、コマンドの実行結果はパイプで結合された隣のコマンドへと引き渡される。 実行結果を引き渡されたコマンドは、その引き渡された実行結果に対して処理を行う。 3つ以上のコマンドがパイプで結合されている場合も同様にして左から順に実行され、実行結果を隣のコマンドへと次々に引き渡していく。 引き渡されるのは標準出力のみで、標準エラー出力は引き渡されない。
$ ls | wc -l 5 ※↑ ls コマンドの実行結果を wc コマンドに引き渡して行数をカウント。 $ ls hogehoge | wc -l ls: hogehoge: そのようなファイルやディレクトリはありません 0 ※↑標準エラー出力は wc コマンドには引き渡されていない。 $ ls hogehoge 2>&1 | wc -l 1 ※↑標準エラー出力も引き渡すにはパイプの前に2番を1番にリダイレクトする。
リダイレクションと同様にパイプ記号(「 | 」)の前後のスペースはあっても無くてもよい。 しかし、シェルスクリプトとして記述する場合には、可読性の面から前後にスペースを入れたほうがよいだろう。
command1 | command2 | command3command1|command2|command3command <<終了文字 hogehoge fugafuga foobar 終了文字
→ コマンドの標準入力に対して複数行にわたる任意の文字列を与えるにはヒアドキュメントを使用する。
ヒアドキュメントを使用すると、終了文字が出現するまでの文字列を、コマンドへの標準入力として与えることができる。 ヒアドキュメント中では「``」や「$()」によるコマンド置換や、変数も使用可能である。
ヒアドキュメント内でのコマンド置換や変数を完全に無効にしたい場合は、終了文字を「'終了文字'」といったようにシングルクオートで囲むか、 もしくは「\終了文字」といったようにバックスラッシュでエスケープする。
※注意! バックスラッシュは日本語環境だと「¥」、英語環境だと「\」と表示される。 当サイト内ではフォントの関係で両方の表記が混在している可能性があるが、その場合は適宜読みかえてください。
$ cat <<_EOT_ > hoge hoge > fuga fuga > foo foo _EOT_ > _EOT_ bar bar > _EOT_ hoge hoge fuga fuga foo foo _EOT_ _EOT_ bar bar ※↑「>」記号の部分がヒアドキュメントとしてキーボードから入力した部分。そのしたの部分は cat コマンドがヒアドキュメントからの入力を出力した部分。 $ cat <<_EOT_ `echo "hoge"` $(echo "fuga") > $PATH > $PWD > _EOT_ hoge fuga /usr/kerberos/bin:/usr/local/bin:/bin:/usr/bin:/home/sunone/bin /home/sunone ※↑ヒアドキュメント中ではコマンド置換や変数も使用可能。
上記の実行例のように、終了文字は行中にあっても無視される。 ヒアドキュメントを終了するには、終了文字を1行でかつ余計な文字を付けずに記述する必要がある。
ちなみに、上記の実行例で終了文字として使用されている「_EOT_」は「End Of Text」の略です。 前後の「 _ 」は終了位置を強調するために付加している。終了文字は任意の文字列でかまわないが、筆者は「_EOT_」か「__EOT__」を推奨する。
たまに終了文字を「EOF」としている人を見かけるが、これだと「End Of File」になるので間違いだと思う。
以下はヒアドキュメントを使用したシェルスクリプトの例。
heredocument.sh
#!/bin/bash # cat の出力結果を標準エラー出力へ if [ $# -ne 1 ]; then cat <<_EOT_ 1>&2 引数を指定してください。 Usage: $0 param _EOT_ exit 1 fi #「$」を表示したいときは「\$」のようにエスケープする cat <<_EOT_ ヒアドキュメント中では変数も使用できます。 \$1 は $1 です。 _EOT_ # 終了文字をエスケープするとヒアドキュメント中の変数は展開されない cat <<'_EOT_' シングルクオートで終了文字を囲むと変数は無視されます。 \$1 は $1 です。 `echo "コマンド置換も無視されます。"` _EOT_ cat <<\_EOT_ バックスラッシュでも同様です。 \$1 は $1 です。 `echo "コマンド置換も無視されます。"` _EOT_ # 「<<-」とすると、ヒアドキュメント中の先頭にあるタブは無視される(スペースは無視されない) cat <<-_EOT_ 終了文字の前に「-」を指定すると、ヒアドキュメント中の先頭のタブは無視されます。 ←タブ ←タブ _EOT_ exit 0
このシェルスクリプト heredocument.sh の実行結果は、以下の通りとなる。
$ ./heredocument.sh HEREDOC ヒアドキュメント中では変数も使用できます。 $1 は HEREDOC です。 シングルクオートで終了文字を囲むと変数は無視されます。 \$1 は $1 です。 `echo "コマンド置換も無視されます。"` バックスラッシュでも同様です。 \$1 は $1 です。 `echo "コマンド置換も無視されます。"` 終了文字の前に「-」をしてすると、ヒアドキュメント中の先頭のタブは無視されます。 ←タブ ←タブ
複数行の文字列を出力する場合は、このヒアドキュメントを使用することを推奨する。 echo や printf で複数行出力する場合は、「'」や「"」で囲む必要があるため、 出力する文字列にそれら自体が含まれているとエスケープする必要が出てくるが、 ヒアドキュメントであれば「'」や「"」のエスケープを意識する必要はない。
ヒアドキュメント使用方法まとめ
| 記号 | 効果 |
|---|---|
<<_EOT_ |
|
<<'_EOT_'<<\_EOT_ |
|
<<-_EOT_ |
|
<<-'_EOT_'<<-\_EOT_ |
|
プロセス置換とは、簡潔に説明すると「コマンドの出力結果をファイルとして扱う機能」である。 例えば diff コマンドは、引数にファイル以外を指定することができないが、このプロセス置換を使うとコマンドの実行結果を diff コマンドに渡すことができる。
diff <(command) <(commandA; commandB)
→ 実行結果をファイルとして使用したいコマンドを「<()」で指定する。
このプロセス置換は次の例のように、一時ファイルを作らずにファイルの置換結果を確認したい場合などに有効である。
$ cat text.txt hoge fuga foo bar ※↑置換対象のファイル内容。 $ diff text.txt <(sed -e 's/hoge/HOGE HOGE/' text.txt) 1c1 < hoge --- > HOGE HOGE ※↑置換前と置換後の diff 結果。
プロセス置換に使用するコマンドは、単一のコマンドのみではなく、セミコロン区切りの複数コマンド、パイプやネストしたプロセス置換も指定可能である。
$ diff <(echo "aaa"; echo "bbb") <(cat <(echo "aaa") | cat <(echo "BBB"); echo "ccc") 1,2c1,2 < aaa < bbb --- > BBB > ccc
標準入力とは、通常はキーボードからの入力を意味する。 この標準入力はファイルディスクリプタの0番で表され、この0番にリダイレクション(「>」)を使用することにより、 標準入力をテキストファイルの内容に切り替えることも可能である。
read 変数名
→ 標準入力からデータを読み取るには read コマンドを使用する。
read コマンドは標準入力からデータを読み取り、引数に指定した変数に読み取ったデータを設定する。 キーボードからもしくは、リダイレクションによりテキストファイルなどからデータを読み取る場合には read コマンドを使用する。
$ read DATA Today is a fine day. ※↑キーボードから「Today is a fine day.」と入力する。 $ echo $DATA Today is a fine day. ※↑read コマンドに指定した変数には、入力した値が設定されている。
read コマンドを実行すると、キーボードからの入力待ち状態になり、Enter キーが入力されるまでの値が指定した変数に設定される。 データを読み取る以外にも、入力待ち状態になることを利用して、シェルスクリプトの処理を一時的に止める、といった使い方もできる。
データの読み取りと、読み取り後に read コマンドで一時停止するシェルスクリプトを作成してみる。
input_test.sh
#!/bin/bash echo "キーボードから文字を入力してください..." read DATA echo "" echo "入力されたデータ: $DATA" echo "" echo "<Enter>で終了します。" # readコマンドをスクリプトの一時停止に利用する read DUMMY echo "終了します..." exit 0
このシェルスクリプト input_test.sh の実行結果は、以下の通りとなる。
$ ./input_test.sh
キーボードから文字を入力してください...
My name is SUNONE.
入力されたデータ: My name is SUNONE.
<Enter>で終了します。
※Enterキーを入力
終了します...
$
1回目の read コマンド実行時にキーボードから「My name is SUNONE.」を入力した。 その入力値が「入力されたデータ: 」の後ろに正しく表示されているのが分かる。 2回目の read コマンドは入力された値はまったく使用せず、コマンドの入力待ちによる一時停止状態のみを利用している。 つまり、Enter キーが入力されるまでスクリプトは一時停止状態となっている。
read 変数名 0<ファイル名 # 「0」は省略可能 read 変数名 <ファイル名
→ read コマンドにリダイレクションを使用することで、テキストファイルからもデータを入力することができる。
リダイレクションを使用すると、入力元や出力先を切り替えることが可能になる。 この場合は、ファイルの出力をファイルディスクリプタ0番に、つまり標準入力に切り替えることにより、 ファイルからのデータ入力を実現している。切り替え先として指定している「0」は省略可能である。
$ cat input.txt Today is a fine day. ※↑入力に使用するファイル。 $ read DATA <input.txt ※↑ファイルを標準入力にリダイレクトする。 $ echo $DATA Today is a fine day. ※↑キーボードからの入力ではなく、ファイルからの入力でも値が設定されている。
テキストファイルを標準入力にリダイレクトすることで、キーボードからの入力なしでも read コマンドが実行される。 このリダイレクトと while 文と組み合わせることで、ファイル内容の解析といった処理に応用できる。
リダイレクトと while 文を使用した、テキストファイル内各行の文字数をカウントするファイルを作成してみる。
input_from_text.sh
#!/bin/bash echo "$1の文字数をカウント..." # 行数カウンタを初期化 line_no=1 # read コマンドで読み取れなくなるまでループ while read LINE do count=`echo "$LINE" | wc -c` echo "$line_no行目: $count文字 : $LINE" line_no=`expr $line_no + 1` done <$1 exit 0
このシェルスクリプト input_from_text.sh の実行結果は、以下の通りとなる。
$ cat input.txt
a
aa
bbb
bbbbbbbbbbbbbbbb
c c
ccccccccc cc ccccccccc
c cccccccc ccccc
ccc cccccccccccc c c c cccccc
※↑入力に使用するテキストファイル。
$ ./input_from_text.sh input.txt
input.txtの文字数をカウント...
1行目: 2文字 : a
2行目: 3文字 : aa
3行目: 1文字 :
4行目: 4文字 : bbb
5行目: 17文字 : bbbbbbbbbbbbbbbb
6行目: 1文字 :
7行目: 27文字 : c c
8行目: 23文字 : ccccccccc cc ccccccccc
9行目: 38文字 : c cccccccc ccccc
10行目: 42文字 : ccc cccccccccccc c c c cccccc
※ 行頭のスペースは無視される。これを回避するには環境変数 IFS を変更する必要がある。
read コマンドに while 文とリダイレクトを組み合わせることにより、テキストファイルの各行に対して wc コマンド(文字数カウント)を実行することが可能となっている。
文字数には改行も含まれている。
上記のような処理を応用することにより、ログファイルを解析するといったようなシェルスクリプトも作成可能であるが、
while 文にテキストファイルをリダイレクトするシェルスクリプトは、実行時間が非常に長くなる傾向がある。
したがって10行程度のテキストファイルならば問題ないが、膨大な行数のテキストファイルを処理対象とする場合は、
awk スクリプトもしくは Perl スクリプトなどを使用した方がよい。
実際に同様の処理を awk スクリプトを使用して行うシェルスクリプトを作成し、上記の非 awk スクリプト版と実行時間を比較してみる。 比較に使用する awk スクリプト版は次の通り。
#!/bin/bash
echo "$1の文字数をカウント..."
awk '{
count = length();
printf("%d行目: %d文字 : %s\n", NR, count, $0);
}' $1
exit 0
非 awk 版(input_from_text.sh)と awk 版(input_from_text_awk.sh)の実行速度比較は以下の通り。
$ time ./input_from_text.sh input.txt input.txtの文字数をカウント... 1行目: 2文字 : a 2行目: 3文字 : aa 3行目: 1文字 : 4行目: 4文字 : bbb 5行目: 17文字 : bbbbbbbbbbbbbbbb 6行目: 1文字 : 7行目: 27文字 : c c 8行目: 23文字 : ccccccccc cc ccccccccc 9行目: 38文字 : c cccccccc ccccc 10行目: 42文字 : ccc cccccccccccc c c c cccccc real 0m1.125s user 0m1.106s sys 0m0.588s $ time ./input_from_text_awk.sh input.txt input.txtの文字数をカウント... 1行目: 1文字 : a 2行目: 2文字 : aa 3行目: 0文字 : 4行目: 3文字 : bbb 5行目: 16文字 : bbbbbbbbbbbbbbbb 6行目: 0文字 : 7行目: 26文字 : c c 8行目: 22文字 : ccccccccc cc ccccccccc 9行目: 37文字 : c cccccccc ccccc 10行目: 44文字 : ccc cccccccccccc c c c cccccc real 0m0.149s user 0m0.153s sys 0m0.046s
※awk 版は改行を1文字とカウントしないため、非 awk 版よりも文字数が1文字少なく表示されている。また、非 awk 版は行頭のスペースも無視される。
awk 版の方が非 awk 版よりも1秒ほど早いのが分かる。 10行程度のテキストファイルが対象なので、それほど大きな違いは出ないが、対象となるテキストファイルが1000行以上となるとかなりの差になってくる。
次は1000行のテキストファイルを使用した場合の実行速度比較。
$ wc -l input.txt
1000 input.txt
※↑1000行のテキストファイルを使用する。
$ time ./input_from_text.sh input.txt >/dev/null
real 2m5.945s
user 1m50.517s
sys 0m52.983s
$ time ./input_from_text_awk.sh input.txt >/dev/null
real 0m0.142s
user 0m0.122s
sys 0m0.077s
awk 版は一瞬で終了しているが、非 awk 版は終了まで2分もかかっている。 リダイレクトは便利な機能ではあるが、while 文と組み合わせて使用する場合には、実行スピードに注意する必要がある。
標準出力とは、通常はディスプレイを意味する。この標準出力はファイルディスクリプタの1番で表される。
echo メッセージ # メッセージ出力後に改行しない echo -n メッセージ
→ ディスプレイにメッセージを出力するには echo コマンドを使用する。
echo コマンドのパラメータに表示したいメッセージを指定して実行すると、標準出力(通常はディスプレイ)に指定したメッセージが表示される。 通常はメッセージ出力後に自動的に改行されるが、-n オプションを指定するとメッセージ出力後に改行されない。
$ echo "Hello world." Hello world. $ ※↑引数に指定したメッセージと改行が出力される。 $ echo -n "Hello world." Hello world.$ ※↑ -n オプションをしてすると改行されないので、メッセージの直後にプロンプトが表示されている。
printf "%d %nd %s %ns" 数値1 数値2 文字列1 文字列2
→ メッセージを整形して表示するには printf コマンドを使用する。
シェルスクリプトでも C 言語で使用される printf が使用可能である。 この printf コマンドを使用することにより、echo コマンドでは不可能な、メッセージを整形して表示する機能を実現できる。 printf コマンドは C 言語の printf 関数と同じように使用することができる。 処理結果を表形式で出力したいといった場合には、桁数を指定して出力できる printf コマンドを使用するとよい。
printf コマンドでメッセージを整形して出力するシェルスクリプトを作成してみる。
printf.sh
#!/bin/bash # 桁数を指定しないで、そのまま出力する。 printf "%d %d %d\n" 10 200 3000 printf "%d %d %d\n" 4000 500 60 printf "%d %d %d\n" 700 8000 9 # 文字数を指定しないで、そのまま出力する。 printf "%s %s %s\n" aa bbb cccc printf "%s %s %s\n" dddd eee ff printf "%s %s %s\n" ggg hhhh i echo "" # 桁数を指定し、整形して出力する。 printf "%5d %5d %5d\n" 10 200 3000 printf "%5d %5d %5d\n" 4000 500 60 printf "%5d %5d %5d\n" 700 8000 9 # 文字数を指定し、整形して出力する。 printf "%5s %5s %5s\n" aa bbb cccc printf "%5s %5s %5s\n" dddd eee ff printf "%5s %5s %5s\n" ggg hhhh i echo "" # 桁数・文字数を指定し、左詰で整形して出力する。 printf "%-5d %-5d %-5d\n" 10 200 3000 printf "%-5s %-5s %-5s\n" aa bbb cccc echo "" # 桁数分の0埋で出力する。 printf "%05d %05d %05d\n" 700 8000 9 exit 0
このシェルスクリプト printf.sh の実行結果は、以下の通りとなる。
$ ./printf.sh 10 200 3000 4000 500 60 700 8000 9 aa bbb cccc dddd eee ff ggg hhhh i 10 200 3000 4000 500 60 700 8000 9 aa bbb cccc dddd eee ff ggg hhhh i 10 200 3000 aa bbb cccc 00700 08000 00009
桁数を指定して実行すると、右揃えできれいに出力される。データを表形式で出力したい場合などは、このように printf コマンドのフォーマットに桁数を指定して実行する。 また、デフォルトで改行される echo コマンドとは異なり、printf コマンドはフォーマットに「\n」を指定しないと改行されないので注意が必要だ。
printf コマンドでは単純な数値や文字列だけではなく、コマンドの実行結果や変数も指定可能だ。
printf "%d %nd %s %ns" `command` printf "%d %nd %s %ns" $変数 printf "%d %nd %s %ns" `command` $変数
→ printf コマンドには単純な文字列だけではなく、コマンドや変数も指定可能。
コマンドを指定する場合は、バッククオートで囲んだ状態で指定する。 バッククオートを使用することで、指定したコマンドの実行結果がそのまま printf コマンドのフォーマット部分へ渡される。
以下はコマンドや変数を指定した場合の実行結果。
$ printf "%10s %10s %10s %10s %10s %10s\n" `date` 2007年 5月 27日 日曜日 04:16:58 JST $ printf "%-10s %-10s %-10s %-10s %-10s %-10s\n" `date` 2007年 5月 27日 日曜日 04:17:09 JST $ DATE=`date` $ printf "%10s %10s %10s %10s %10s %10s\n" $DATE 2007年 5月 27日 日曜日 04:17:16 JST
コマンドや変数を指定した場合の詳細は以下の通りとなる。まず、
printf "%5d %5d %5d\n" `date '+%Y %m %d'`
と指定した場合は、バッククオートの部分が展開されて、
printf "%5d %5d %5d\n" 2007 05 27
という状態で、printf コマンドが実行される。
変数を指定した場合も同様に変数が展開された状態で printf コマンドが実行される。
例えば、
DATE="2007 05 27" printf "%5d %5d %5d\n" $DATE
と指定した場合は、変数が展開され、
printf "%5d %5d %5d\n" 2007 05 27
という状態で、printf コマンドが実行される。
標準エラー出力とは、標準出力と同様に通常はディスプレイを意味する。だが、標準出力とは異なり、ファイルディスクリプタは2番で表される。 これによって、通常のメッセージとエラーメッセージを区別することが可能になっている。 つまり、通常のメッセージのみを表示したい、またはエラーメッセージのみを表示したい、といった処理が実現できる。
通常、コマンドが失敗した場合に表示されるエラーメッセージは標準エラー出力に送られている。 シェルスクリプトにおいても、リダイレクションを使用することにより、標準エラー出力にエラーメッセージを出力することが可能である。 シェルスクリプトを作成する場合は、通常メッセージは標準出力へ、エラーメッセージは標準エラー出力へ、というようにメッセージの出力先を振り分けることが望ましい。
echo エラーメッセージ 1>&2 printf エラーメッセージ 1>&2 任意のコマンド 1>&2
→ エラーメッセージを出力するには、標準出力を標準エラー出力にリダイレクトする。
echo コマンドなどにより出力されるメッセージを、標準エラー出力にリダイレクトすることで、エラーメッセージとして出力することができる。 「1>&2」はファイルディスクリプタ1番(標準出力)を2番(標準エラー出力)に丸め込んで出力するという意味になる。 結果的には、標準出力が標準エラー出力として出力される、ということになる。
通常のメッセージとエラーメッセージを出力するシェルスクリプトを作成してみる。
stderr.sh
#!/bin/bash # 標準出力に出力する。 echo "これは通常メッセージです。" # 標準エラー出力に出力する。 echo "これはエラーメッセージです。" 1>&2 exit 0
このシェルスクリプト stderr.sh の実行結果は、以下の通りとなる。
$ ./stderr.sh これは通常メッセージです。 これはエラーメッセージです。 $ ./stderr.sh 1>/dev/null これはエラーメッセージです。 ※↑標準エラー出力のみを表示。 $ ./stderr.sh 2>/dev/null これは通常メッセージです。 ※↑標準出力のみを表示。
※ NULL デバイス(/dev/null)へリダイレクトするとメッセージは破棄される。NULL デバイスはゴミ箱のような物と考えるとよい。
標準出力(1番)を NULL デバイスへリダイレクトすると、エラーメッセージのみが表示される。 逆に標準エラー出力(2番)を NULL デバイスへリダイレクトすると、通常メッセージのみが表示される。 このことから、メッセージが標準出力と標準エラー出力に振り分けられていることがわかる。
複数コマンドの出力をまとめて一つのファイルに、または for 文や while 文の出力をまとめて一つのファイルに リダイレクトしたいときに有効なテクニックを紹介する。
こういった場合の処理でよく見られる悪い例として、次のようなものがある。
×悪い例
echo "1. hogehoge" >>file echo "2. fugafuga" >>file echo "3. foofoo" >>file echo "4. barbar" >>file
文法的には全く問題ないのだが、リダイレクトが連続していてスクリプトがすっきりせず、 リダイレクト対象が変わった場合の修正が面倒になる(置換すればいいことは確かだが)。
スマートに複数コマンドの出力をリダイレクトするには "{}" によるコマンドのグルーピング機能を使用する。
○良い例 その1 ~ "{}" のグループをリダイレクト
{ echo "1. hogehoge" echo "2. fugafuga" echo "3. foofoo" echo "4. barbar" } >>file
もしくは cat コマンドとヒアドキュメントを使用してもよい。
○良い例 その2 ~ ヒアドキュメントをリダイレクト
cat <<__END_OF_MESSAGE__ >>file
1. hogehoge
2. fugafuga
3. foofoo
4. barbar
__END_OF_MESSAGE__
グルーピング機能と同様に、for 文の do ~ done ブロックの出力をまとめてリダイレクトすることも可能だ。
○良い例 その3 ~ for文のブロックをリダイレクト
for i in `seq 1 10`
do
echo "number $i"
done >>file
$ for i in `seq 1 10` > do > echo "number $i" > done >file $ ※↑何も表示されない。 $ cat file number 1 number 2 number 3 number 4 number 5 number 6 number 7 number 8 number 9 number 10 ※↑ブロック内の全ての出力はリダイレクト先のファイルに出力されている。
他の制御文も同様にリダイレクト可能である。
○良い例 その4 ~ 制御文のブロックをリダイレクト
while [ $i -lt 10 ] do echo "number $i" i=`expr $i + 1` done >>file if [ -f "$str" ]; then ls "fugafuga" else echo "FUGAFUGA" fi >>file case "$str" in "hoge" ) ls "hogehoge" ;; "fuga" ) cat "fugafuga" ;; * ) echo "NG..." ;; esac >>file
一切の出力を行わない関数を作りたい場合は次のようにする。
○良い例 その5 ~ 関数全体をリダイレクト
func()
{
[ $#- ne 3 ] && return 1
echo "NO MESSAGE..."
echo "NO ERROR MESSAGE..." 1>&2
grep -v "$1" $2 >$3
} >/dev/null 2>&1
複数コマンドの出力をまとめてパイプに流し込みたいときは、 "{}" によるコマンドのグルーピング機能を利用する。
{ echo "hogehoge"; echo "fugafuga"; } | wc -l
→ "{}" で複数コマンドをグループ化することにより出力を一つにまとめる。
コマンドの数が多い場合は、次のように記述すると可読性が高くなる。
{ date echo "hoge" echo "HOGE" echo "fuga" echo "FUGA" } | tee -a file

© 2006 SUNONE