LPIC Level1 復習ラジオ 18:リダイレクトとパイプ
音声
目次
標準入力・標準出力・標準エラー
Linuxのコマンドは、通常、入力を受け取り、処理結果を出力します。この入出力には、標準入力、標準出力、標準エラーという三つの標準ストリームがあります。
| ストリーム | ファイルディスクリプタ | 通常の接続先 |
|---|---|---|
| 標準入力 | 0 |
キーボードなどの入力 |
| 標準出力 | 1 |
端末画面への通常の処理結果 |
| 標準エラー | 2 |
端末画面へのエラーメッセージ |
ファイルディスクリプタは、プロセスが開いているファイルや入出力先を識別するための番号です。標準入力は0、標準出力は1、標準エラーは2として扱われます。
コマンド
├─ 標準入力 0
├─ 標準出力 1
└─ 標準エラー 2
標準出力と標準エラーは、どちらも通常は端末へ表示されるため、見た目だけでは区別しにくいことがあります。しかし、内部では別の流れです。
ls /etc
ls /存在しない場所
最初の結果は主に標準出力、二つ目のエラーメッセージは標準エラーへ送られます。
リダイレクトは、この標準ストリームの接続先を変更するシェルの機能です。パイプは、あるコマンドの標準出力を、次のコマンドの標準入力へ接続します。
>による上書き出力
>は、コマンドの標準出力をファイルへリダイレクトします。通常、対象ファイルが存在しなければ作成し、存在する場合は内容を空にしてから書き込みます。
date > date.txt
ls -l > files.txt
printf '%s\n' "hello" > message.txt
画面へ表示されるはずだった標準出力が、指定したファイルへ書き込まれます。
標準出力の番号を明示する
date 1> date.txt
>は、ファイルディスクリプタを省略すると標準出力、つまり1を対象にします。そのため、>と1>は同じ意味です。
空のファイルを作る
> empty.txt
コマンドを書かずにリダイレクトだけを行うと、ファイルを作成したり、既存ファイルの内容を空にしたりできます。
上書き防止
bashでは、noclobberを有効にすると、通常の>で既存の通常ファイルを上書きしにくくできます。
set -o noclobber
echo test > existing.txt
この状態で強制的に上書きする場合は、bashで>|を使います。
echo test >| existing.txt
set +o noclobber
>>による追記出力
>>は、標準出力をファイルの末尾へ追加します。ファイルが存在しない場合は、新しく作成します。
date >> history.log
echo "start" >> app.log
df -h >> disk.log
>は上書き、>>は追記です。
| 記号 | 既存ファイルがある場合 | ファイルがない場合 |
|---|---|---|
> |
内容を空にして上書き | 新規作成 |
>> |
末尾へ追記 | 新規作成 |
複数行を追記する
printf '%s\n' \
"server=web01" \
"status=ok" >> result.log
ログや履歴を残す処理では追記が便利ですが、同じ処理を何度も実行すると同じ行が増えることがあります。設定ファイルの更新では、単純な追記だけでよいかを確認します。
標準エラーを追記する
command 2>> error.log
2>>は、標準エラーをファイルの末尾へ追記します。
<による入力
<は、コマンドの標準入力をファイルから受け取るように変更します。
wc -l < users.txt
sort < names.txt
tr 'a-z' 'A-Z' < message.txt
例えば、wc -l < users.txtでは、users.txtの内容がwcの標準入力へ渡されます。
ファイルを引数にする場合との違い
wc -l users.txt
wc -l < users.txt
どちらも行数を数えますが、出力が異なる場合があります。
$ wc -l users.txt
12 users.txt
$ wc -l < users.txt
12
一つ目は、users.txtをコマンドの引数として渡しています。二つ目は、シェルがファイルを開き、内容を標準入力として渡しています。そのため、wcは入力元のファイル名を引数として受け取りません。
標準入力の番号を明示する
sort 0< names.txt
<は、ファイルディスクリプタを省略すると標準入力、つまり0を対象にします。通常は0を省略します。
コマンドによっては、ファイル名を引数にできず、標準入力だけを受け取るものがあります。その場合、<やパイプを使います。
2>によるエラー出力
2>は、標準エラーをファイルへリダイレクトします。先頭の2は標準エラーのファイルディスクリプタです。
ls /etc /存在しない場所 2> error.txt
この例では、/etcの一覧は標準出力として画面へ表示され、存在しない場所に対するエラーだけがerror.txtへ書き込まれます。
標準出力と標準エラーを別々に保存する
ls /etc /存在しない場所 \
> output.txt \
2> error.txt
通常の結果はoutput.txt、エラーメッセージはerror.txtへ保存されます。
エラーを表示しない
find / -name '*.conf' 2> /dev/null
/dev/nullは、書き込まれたデータを破棄する特殊なデバイスです。不要なエラー表示を捨てる場合に使われます。
ただし、エラーをすべて捨てると、権限不足やファイルシステムの問題など、重要な情報も見えなくなります。調査中は、エラーをファイルへ保存して確認する方が安全です。
標準エラーを追記する
find /tmp -type f 2>> find-error.log
2>は上書き、2>>は追記です。
標準出力と標準エラーをまとめる
標準出力と標準エラーを同じファイルへ保存する場合は、ファイルディスクリプタの複製を使います。
command > all.log 2>&1
処理の流れは次のとおりです。
> all.logで、標準出力1をall.logへ向ける2>&1で、標準エラー2を、現在の標準出力1と同じ接続先へ向ける
&1は、名前が1のファイルへ書くという意味ではなく、ファイルディスクリプタ1を複製するという意味です。
順番が重要
command > all.log 2>&1
command 2>&1 > all.log
この二つは同じではありません。リダイレクトは左から順に処理されます。
command > all.log 2>&1
- 最初に標準出力を
all.logへ向ける - 次に標準エラーを、その時点の標準出力と同じ場所へ向ける
- 標準出力と標準エラーの両方が
all.logへ入る
command 2>&1 > all.log
- 最初に標準エラーを、その時点の標準出力、通常は端末と同じ場所へ向ける
- 次に標準出力だけを
all.logへ向ける - 標準出力はファイル、標準エラーは端末に残る
bashの短縮形式
command &> all.log
command &>> all.log
&>は標準出力と標準エラーをまとめて上書きし、&>>はまとめて追記します。これらはbashの構文です。移植性を重視するシェルスクリプトでは、> file 2>&1を使うことがあります。
両方を破棄する
command > /dev/null 2>&1
通常の結果とエラーの両方を破棄します。必要な診断情報まで消えるため、用途を限定します。
パイプ
パイプ記号|は、左側のコマンドの標準出力を、右側のコマンドの標準入力へ接続します。
左のコマンド | 右のコマンド
ps aux | grep ssh
cat access.log | grep '404'
sort names.txt | uniq
du -h /var/* 2>/dev/null | sort -h
パイプを使うと、一つの大きなコマンドですべてを処理するのではなく、小さなコマンドを組み合わせられます。
入力
↓
コマンド1
↓ 標準出力
パイプ
↓ 標準入力
コマンド2
↓ 標準出力
画面または次のパイプ
標準エラーは通常パイプへ流れない
find / -name '*.log' | grep nginx
通常の|が接続するのは、左側の標準出力だけです。左側の標準エラーは、通常はそのまま端末へ表示されます。
標準エラーもパイプへ流す場合は、次のようにします。
command 2>&1 | grep pattern
bashでは、次の短縮形式も利用できます。
command |& grep pattern
|&は、bashでは概ね2>&1 |の短縮です。
不要なcat
cat access.log | grep '404'
grep '404' access.log
どちらも動作しますが、grepがファイルを直接読める場合は、二つ目の方が単純です。ただし、パイプの仕組みを学ぶ例としてcatを使うことはあります。
パイプラインの終了ステータス
bashでは、通常、パイプライン全体の終了ステータスは最後のコマンドの終了ステータスです。
false | true
echo "$?"
左側のfalseが失敗しても、最後のtrueが成功するため、通常は0になります。
途中の失敗も検出したい場合は、bashでpipefailを有効にします。
set -o pipefail
false | true
echo "$?"
set +o pipefail
pipefailが有効な場合、パイプ内のコマンドが失敗すると、パイプライン全体も失敗として扱いやすくなります。
tee
teeは、標準入力を標準出力へ流しながら、一つ以上のファイルにもコピーします。画面で確認しながらファイルへ保存したい場合に使います。
command | tee result.txt
通常の>では、出力をファイルへ向けると画面には表示されません。teeでは、出力を画面へ流しながらファイルにも保存できます。
基本例
df -h | tee disk.txt
ip address | tee network.txt
grep 'ERROR' app.log | tee errors.txt
追記する
date | tee -a operation.log
teeは標準では出力ファイルを上書きします。-aを付けると末尾へ追記します。
複数ファイルへ保存する
command | tee file1.txt file2.txt
同じ標準入力を、標準出力と複数のファイルへコピーできます。
標準エラーも保存する
command 2>&1 | tee all.log
標準出力と標準エラーをまとめてパイプへ送り、teeで画面とファイルへ出力します。
sudoとリダイレクト
sudo echo "setting" > /root/example.conf
この書き方では、echoだけがsudoの対象です。>によるファイルのオープンは現在のシェルが行うため、一般利用者の権限では書き込めないことがあります。
標準入力から一行を書き込む例は次のとおりです。
printf '%s\n' "setting" | sudo tee /root/example.conf > /dev/null
tee自体をsudoで実行するため、管理者権限でファイルを開きます。画面への重複表示が不要な場合は、teeの標準出力を/dev/nullへ向けます。
実践的な組み合わせ
検索結果を保存する
grep -Rni 'error' /var/log \
> matches.txt \
2> search-errors.txt
検索結果と、権限不足などのエラーを別々に保存します。
画面で確認しながら保存する
journalctl -u ssh 2>&1 | tee ssh-check.log
標準出力と標準エラーをまとめて、画面とファイルへ流します。
行数を数える
grep -v '^#' /etc/ssh/sshd_config | wc -l
コメント行以外を抽出し、その出力をwc -lへ渡します。
重複を数える
cut -d' ' -f1 access.log \
| sort \
| uniq -c \
| sort -nr \
| head
左から順に、列の抽出、並べ替え、重複数の集計、件数の降順並べ替え、上位の表示を行います。
一時結果も残す
sort names.txt \
| tee sorted-names.txt \
| uniq \
> unique-names.txt
teeで並べ替え直後の内容を保存しながら、後続のuniqへも渡します。
成功時だけ次へ進める
set -o pipefail
generate_data | process_data | tee result.log
status=$?
set +o pipefail
if [ "$status" -eq 0 ]; then
echo "pipeline success"
fi
パイプライン途中の失敗も確認したい場合は、pipefailを使います。シェルスクリプトでは、各コマンドの失敗をどのように扱うかを明確にします。
よくある間違いと確認方法
1. >と>>を取り違える
echo "new line" > important.log
追記するつもりで>を使うと、元の内容が失われます。実行前に、上書きか追記かを確認します。
2. エラーがファイルへ入らない
command > output.log
>が対象にするのは標準出力だけです。標準エラーも保存する場合は、2>または2>&1を使います。
3. パイプへエラーが流れない
command | grep error
通常のパイプが受け取るのは標準出力です。標準エラーも対象にする場合は、command 2>&1 | grep errorなどとします。
4. リダイレクトの順番を間違える
command 2>&1 > all.log
両方をファイルへ入れたい場合、この順番では標準エラーが端末へ残ることがあります。左から処理されることを意識します。
5. sudoを付けたのに書き込めない
sudo echo value > /protected/file
リダイレクトはsudoではなく、現在のシェルが処理します。必要に応じてsudo teeや、管理者権限で起動したエディタを使います。
6. パイプ途中の失敗を見落とす
failed_command | successful_command
echo "$?"
通常は最後のコマンドの終了ステータスが返ります。途中の失敗を検出したい場合は、bashのpipefailを検討します。
7. ファイルディスクリプタを確認する
ls -l /proc/$$/fd
Linuxでは、現在のシェルが開いているファイルディスクリプタを/proc/プロセスID/fdで確認できます。0、1、2がどこへ接続されているかを調べる手掛かりになります。
症状別の確認
| 症状 | 主な確認点 |
|---|---|
| ファイルが空になった | >で既存内容を上書きしていないか |
| エラーだけ画面に残る | 2>または2>&1の有無 |
grepがエラー文を拾わない |
標準エラーをパイプへ流しているか |
| 両方を保存したつもりなのに片方だけ | リダイレクトの記述順 |
sudoでもPermission denied |
ファイルを開くリダイレクト側の権限 |
| パイプ途中の失敗に気付かない | 終了ステータスとpipefail |
30秒チェック
- 標準入力、標準出力、標準エラーのファイルディスクリプタ番号は、それぞれ何番でしょうか。
>と>>は、既存ファイルに対してどのように動作が違うでしょうか。wc -l file.txtとwc -l < file.txtでは、ファイルはそれぞれどのように渡されるでしょうか。- 標準エラーだけを
error.logへ上書き保存する書き方は何でしょうか。 - 標準出力と標準エラーの両方を
all.logへ保存する書き方は何でしょうか。 - 通常のパイプ
|は、左側のどのストリームを右側へ渡すでしょうか。 - 画面へ表示しながら
result.txtにも保存するコマンドは何でしょうか。 - パイプ途中の失敗も終了ステータスへ反映しやすくするbashの設定は何でしょうか。
答えを表示
- 標準入力は0、標準出力は1、標準エラーは2
>は上書き、>>は末尾へ追記- 前者はファイル名を引数として渡し、後者はファイル内容を標準入力として渡す
command 2> error.logcommand > all.log 2>&1。bashではcommand &> all.logも利用できる- 標準出力
command | tee result.txtset -o pipefail
要約
- 標準入力は0、標準出力は1、標準エラーは2のファイルディスクリプタで扱われます。
- リダイレクトは、コマンドの標準ストリームの接続先を変更するシェルの機能です。
>は標準出力をファイルへ上書きし、>>は末尾へ追記します。<は、ファイルの内容をコマンドの標準入力へ渡します。2>は標準エラーを上書きし、2>>は標準エラーを追記します。/dev/nullへ出力するとデータを破棄できますが、重要なエラーまで隠さないよう注意します。command > file 2>&1は、標準出力と標準エラーを同じファイルへ送ります。- リダイレクトは左から順に処理されるため、
> file 2>&1と2>&1 > fileは同じではありません。 - パイプ
|は、左側の標準出力を右側の標準入力へ接続します。 - 標準エラーもパイプへ渡す場合は、
2>&1 |またはbashの|&を使います。 teeは標準入力を標準出力へ流しながら、ファイルにもコピーします。tee -aは、ファイルを上書きせず末尾へ追記します。sudo echo ... > fileでは、リダイレクト自体は現在のシェルが行います。必要に応じてsudo teeを使います。- bashの通常設定では、パイプラインの終了ステータスは最後のコマンドのものです。
set -o pipefailを使うと、パイプ途中の失敗を検出しやすくなります。
参考資料
- GNU Bash Reference Manual
- GNU Bash Manual:Redirections
- GNU Bash Manual:Redirecting Input
- GNU Bash Manual:Redirecting Output
- GNU Bash Manual:Appending Redirected Output
- GNU Bash Manual:Redirecting Standard Output and Standard Error
- GNU Bash Manual:Duplicating File Descriptors
- GNU Bash Manual:Pipelines
- GNU Bash Manual:The Set Builtin
- GNU Coreutils Manual:tee invocation
- bash(1) — Linux manual page
- tee(1) — Linux manual page
- pipe(7) — Linux manual page
- null(4) — Linux manual page
- POSIX.1-2024:Shell Command Language