- 技術 💻
teeコマンドの使い方|画面とファイル両方に出力・sudo teeで追記
teeは、受け取った入力を「画面(標準出力)」と「ファイル」の両方へ同時に書き出すコマンドです。 コマンド | tee ファイル と書くだけで、実行結果を目で確認しながらログとして残せます。名前は、水道管を2方向に分けるT字型の継ぎ手(T)に由来します。
この記事では、基本の使い方に加えて、sudo echo ... > ファイル が Permission denied になる様子を再現し、sudo tee と sudo tee -a で直す流れを出力つきで示します。さらに、標準エラーも一緒に保存する 2>&1 と |&、画面に出さない > /dev/null、複数ファイルへの同時出力、teeを挟むとコマンドの失敗が見えなくなる問題を pipefail と PIPESTATUS で直す例、-a の付け忘れによる上書きまで扱います。出力はすべて Ubuntu 24.04・GNU coreutils 9.4 の tee・bash 5.2.21 で実行したものです。
teeコマンドの早見表
コピーして使える形で並べました。cmd はご自身のコマンドに置き換えてください。
| やりたいこと | 書き方 | 補足 |
|---|---|---|
| 画面とファイルの両方に出力する | cmd | tee out.txt |
ファイルは上書きされる |
| ファイルの末尾に追記する | cmd | tee -a out.txt |
-a は append(追記) |
| 複数のファイルに同時に書く | cmd | tee a.txt b.txt c.txt |
ファイル名を並べるだけ |
| 標準エラーも一緒に保存する | cmd 2>&1 | tee out.txt |
どのシェルでも使える書き方 |
| 標準エラーも一緒に保存する(短縮形) | cmd |& tee out.txt |
bash 4 以降・zsh の書き方 |
| 画面には出さずファイルだけに書く | cmd | tee out.txt > /dev/null |
sudo tee と組み合わせることが多い |
| 管理者権限が必要なファイルに書く | echo "..." | sudo tee /etc/x.conf |
上書き |
| 管理者権限が必要なファイルに追記する | echo "..." | sudo tee -a /etc/x.conf |
-a を忘れると上書きになる |
| 保存しつつ次のコマンドへ渡す | cmd | tee out.txt | wc -l |
パイプの途中に挟める |
| 元のコマンドの失敗を検知する | set -o pipefail を先に実行 |
bash・zsh など |
| 元のコマンドの終了ステータスを取る | ${PIPESTATUS[0]} |
bash の配列変数 |
| 複数行をまとめて書き込む | sudo tee file > /dev/null <<'EOF' |
ヒアドキュメントと組み合わせる |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「teeコマンドの使い方|画面とファイル両方に出力・sudo teeで追記」
検証環境
| 項目 | 内容 |
|---|---|
| OS | Ubuntu 24.04.4 LTS |
| tee | GNU coreutils 9.4 |
| シェル | GNU bash 5.2.21(比較用に dash の sh) |
| sudo | 1.9.15p5 |
| 権限の検証に使ったファイル | /opt/t199demo/app.conf(所有者 root、権限 644) |
Permission denied の再現は一般ユーザーで実行しました。sudo tee を使った直し方は、検証用の一般ユーザーにsudoの権限を付けていないため、rootのシェルで実行した結果を載せています(sudoが使える一般ユーザーで実行した場合は、最初にパスワードを聞かれる点が加わります)。
teeの基本:画面とファイルの両方に出力する
書き方は コマンド | tee ファイル名 です。左のコマンドの出力がteeに渡り、teeがそれを画面とファイルの両方へ書き出します。
graph LR
A["コマンドの標準出力"] -->|"パイプ"| T["tee"]
T --> S["画面(標準出力)"]
T --> F["ファイル"]
echo "hello" | tee out.txt
cat out.txt
実行結果:
$ echo "hello" | tee out.txt
hello
$ cat out.txt
hello
1行目の hello はteeが画面に出したもの、cat out.txt の hello はファイルに保存されたものです。リダイレクト(>)はファイルにだけ書き、teeは画面とファイルの両方に書くという違いがあります。
| 書き方 | 画面に出る | ファイルに保存される |
|---|---|---|
cmd |
出る | されない |
cmd > out.txt |
出ない | される(上書き) |
cmd >> out.txt |
出ない | される(追記) |
cmd | tee out.txt |
出る | される(上書き) |
cmd | tee -a out.txt |
出る | される(追記) |
パイプの途中に挟んで、保存しながら次へ渡す
teeの標準出力はそのまま次のコマンドへ渡せます。途中経過をファイルに残しつつ、処理を続けたいときに便利です。
seq 3 | tee nums.txt | wc -l
cat nums.txt
実行結果:
$ seq 3 | tee nums.txt | wc -l
3
$ cat nums.txt
1
2
3
画面には wc -l の結果(3)だけが出て、途中の 1〜3 は nums.txt に残りました。
-a で追記する(付け忘れると上書きになる)
teeは、何も指定しないとファイルを上書きします。 追記したいときは -a を付けます。次の例で、付けない場合と付けた場合を比べます。
echo "1回目" | tee log.txt
echo "2回目" | tee log.txt
cat log.txt
echo "3回目" | tee -a log.txt
cat log.txt
実行結果:
$ echo "1回目" | tee log.txt
1回目
$ echo "2回目" | tee log.txt
2回目
$ cat log.txt
2回目
$ echo "3回目" | tee -a log.txt
3回目
$ cat log.txt
2回目
3回目
2回目のteeで「1回目」の行が消え、-a を付けた3回目は末尾に追加されました。
| 書き方 | 既存の内容 | リダイレクトで言うと |
|---|---|---|
tee file |
消えて、新しい内容に置き換わる | > と同じ |
tee -a file |
残り、末尾に追加される | >> と同じ |
設定ファイルやログのように「既存の内容を残したい」ファイルには、必ず -a を付けると覚えておくと安心です。大事なファイルを書き換える前には、後述のようにコピーを取っておくとさらに確実です。
sudo echo > ファイル が Permission denied になる理由と、sudo tee での直し方
teeがもっともよく使われるのがこの場面です。rootだけが書き込めるファイルに、1行追記したいとします。
$ ls -l /opt/t199demo/app.conf
-rw-r--r-- 1 root root 10 Oct 2 20:23 /opt/t199demo/app.conf
$ id -un
nobody
所有者は root で、権限は rw-r--r-- です。一般ユーザー(ここでは nobody)は読むことだけができます。
再現:sudo を付けても Permission denied になる
まず、sudoなしで追記してみます。
$ echo "debug=true" >> /opt/t199demo/app.conf; echo "status=$?"
bash: /opt/t199demo/app.conf: Permission denied
status=1
権限がないので当然の結果です。では、先頭に sudo を付けるとどうなるでしょうか。
$ sudo echo "debug=true" >> /opt/t199demo/app.conf; echo "status=$?"
bash: /opt/t199demo/app.conf: Permission denied
status=1
$ cat /opt/t199demo/app.conf
port=8080
sudoを付けても同じ Permission denied になり、ファイルは変わっていません。 エラーメッセージの先頭が bash: であることがヒントです。メッセージを出しているのは sudo でも echo でもなく、いま使っているシェル(bash)です。
なぜそうなるのか
> や >> のリダイレクトは、コマンドを実行する前にシェル自身がファイルを開く仕組みです。順番に並べると次のようになります。
| 順番 | だれが | 何をするか | 権限 |
|---|---|---|---|
| 1 | いまのシェル(一般ユーザー) | >> /opt/t199demo/app.conf のためにファイルを開こうとする |
一般ユーザーなので開けない → ここで Permission denied |
| 2 | sudo | echo を root として実行する | 1 で止まるので、ここまで進まない |
つまり、sudoで権限が上がるのは echo だけで、ファイルを開く役のシェルは一般ユーザーのままです。ファイルを開く役そのものを root で動かす必要があります。その役をteeに任せるのが sudo tee です。
graph LR
E["echo(一般ユーザー)"] -->|"パイプ"| T["sudo tee -a(root)"]
T --> F["/opt/t199demo/app.conf に追記"]
T --> S["画面"]
直し方1:sudo tee -a で追記する
echo "debug=true" | sudo tee -a /opt/t199demo/app.conf
cat /opt/t199demo/app.conf
実行結果:
$ echo "debug=true" | sudo tee -a /opt/t199demo/app.conf
debug=true
$ cat /opt/t199demo/app.conf
port=8080
debug=true
echo は一般ユーザーのままで構いません。ファイルを開いて書き込む tee だけを sudo で実行するのがポイントです。書き込んだ内容(debug=true)は画面にも表示されます。
直し方2:画面に出したくないときは > /dev/null を付ける
teeは書き込んだ内容を画面にも出します。表示が不要なときは、teeの標準出力を /dev/null(何も残らない出力先)へ送ります。
$ echo "log_level=info" | sudo tee -a /opt/t199demo/app.conf > /dev/null
$ cat /opt/t199demo/app.conf
port=8080
debug=true
log_level=info
> /dev/null はシェルが開きますが、/dev/null はだれでも書き込めるので問題ありません。
直し方3:上書きするなら sudo tee(-a なし)
ファイルの中身をまるごと置き換えたいときは、-a を付けずに実行します。
$ echo "port=9090" | sudo tee /opt/t199demo/app.conf
port=9090
$ cat /opt/t199demo/app.conf
port=9090
先ほど追記した debug=true と log_level=info が消え、1行だけになりました。 追記のつもりで -a を付け忘れると、この状態になります。設定ファイルを扱うときは、次のようにコピーを取ってから追記し、diffで差分を確かめる流れにすると安心です。
sudo cp -p /opt/t199demo/app.conf /opt/t199demo/app.conf.bak \
&& echo "retry=3" | sudo tee -a /opt/t199demo/app.conf > /dev/null \
&& diff /opt/t199demo/app.conf.bak /opt/t199demo/app.conf
実行結果(追加された行だけが差分として表示される):
2a3
> retry=3
別の直し方:sudo sh -c でシェルごと root で動かす
リダイレクトを使いたい場合は、シェル自体を sudo で起動し、その中でリダイレクトを行います。
$ sudo sh -c "echo timeout=30 >> /opt/t199demo/app.conf"
$ cat /opt/t199demo/app.conf
port=9090
timeout=30
| 方法 | 書き方 | 向いている場面 |
|---|---|---|
| sudo tee -a | echo "x" | sudo tee -a file |
1行〜数行の追記。引用符が単純で読みやすい |
| sudo tee | echo "x" | sudo tee file |
中身をまるごと置き換える |
| sudo tee + ヒアドキュメント | sudo tee file > /dev/null <<'EOF' |
複数行の設定ファイルを作る |
| sudo sh -c | sudo sh -c "echo x >> file" |
リダイレクトで書きたいとき。引用符が入れ子になる点に注意 |
| エディタ | sudoedit file や sudo vi file |
手で内容を確かめながら編集したいとき(この記事では未実行) |
sudo を置く位置で結果がどう変わるか
ここまでの結果を、sudo の位置ごとに並べ直します。「ファイルを開くのはだれか」を見ると、結果を予想できます。
| 書き方 | ファイルを開くのは | 結果 |
|---|---|---|
sudo echo "x" >> file |
一般ユーザーのシェル | bash: file: Permission denied |
echo "x" | tee file |
一般ユーザーの tee | tee: file: Permission denied(画面には x が出る) |
echo "x" | sudo tee -a file |
root の tee | 追記できる |
sudo sh -c "echo x >> file" |
root のシェル | 追記できる |
パイプの左側(echo)に sudo を付ける必要はありません。権限が必要なのは、ファイルに書き込む右側だけです。
複数行をまとめて書く(sudo tee + ヒアドキュメント)
設定ファイルを新しく作るときは、ヒアドキュメント(<<'EOF' から EOF までを入力として渡す書き方)と組み合わせます。
sudo tee /opt/t199demo/site.conf > /dev/null <<'END'
[server]
host = 127.0.0.1
port = 9090
END
cat /opt/t199demo/site.conf
実行結果:
[server]
host = 127.0.0.1
port = 9090
開始の目印を 'END' と引用符で囲むと、中の $HOME などは展開されずにそのまま書き込まれます。引用符なしの END にすると展開されます。次のスクリプトで違いを確かめました。
tee note2.txt > /dev/null <<'END'
name=demo
path=$HOME/data
END
cat note2.txt
tee note3.txt > /dev/null <<END
path=$HOME/data
END
cat note3.txt
実行結果:
name=demo
path=$HOME/data
path=/tmp/t199home/data
| 開始の目印 | 中の変数($HOME など) |
|---|---|
<<'END'(引用符あり) |
展開されず、文字のまま書かれる |
<<END(引用符なし) |
展開された値が書かれる |
1行だけなら、ヒアストリング(<<<)も使えます。
$ tee note.txt <<< "1行だけ保存"
1行だけ保存
標準エラーも一緒に保存する(2>&1 と |&)
コマンドの出力には、通常の結果が流れる「標準出力」と、エラーメッセージが流れる「標準エラー出力」の2本があります。パイプ(|)が次のコマンドへ渡すのは標準出力だけなので、何もしないとエラーメッセージはteeを通りません。
存在するファイル(out.txt)と存在しないファイル(nofile)を ls に渡して確かめます。
$ ls out.txt nofile | tee r1.txt
ls: cannot access 'nofile': No such file or directory
out.txt
$ cat r1.txt
out.txt
画面には2行出ていますが、r1.txt に保存されたのは out.txt の1行だけです。エラーメッセージはteeを通らず、直接画面に出ています。
2>&1 で標準エラーを標準出力に合流させる
2>&1 は「標準エラー出力(2番)を、標準出力(1番)と同じ行き先にする」という意味です。パイプの前に書きます。
$ ls out.txt nofile 2>&1 | tee r2.txt
ls: cannot access 'nofile': No such file or directory
out.txt
$ cat r2.txt
ls: cannot access 'nofile': No such file or directory
out.txt
今度はエラーメッセージも r2.txt に保存されました。
|& は 2>&1 | の短縮形
bash(4以降)と zsh では、|& と書くと 2>&1 | と同じ意味になります。
$ ls out.txt nofile |& tee r3.txt
ls: cannot access 'nofile': No such file or directory
out.txt
$ cat r3.txt
ls: cannot access 'nofile': No such file or directory
out.txt
| 書き方 | 意味 | 使えるシェル |
|---|---|---|
cmd | tee f |
標準出力だけを保存 | どのシェルでも |
cmd 2>&1 | tee f |
標準出力と標準エラーを保存 | どのシェルでも(sh でも可) |
cmd |& tee f |
上と同じ(短縮形) | bash 4 以降、zsh |
cmd 2>&1 >/dev/null | tee f |
標準エラーだけを保存 | どのシェルでも |
#!/bin/sh で始まるスクリプトには 2>&1 | を、bashと決まっている場面では |& を、という使い分けがわかりやすいです。
標準エラーだけを保存する
2>&1 >/dev/null の順で書くと、標準エラーだけがパイプに流れます。
$ ls out.txt nofile 2>&1 >/dev/null | tee erronly.txt
ls: cannot access 'nofile': No such file or directory
標準出力と標準エラーを別々のファイルに保存する
bashのプロセス置換(>(コマンド) と書くと、その出力先を別のコマンドの入力にできる機能)を使うと、2本を別々のファイルに分けつつ、どちらも画面に出せます。
ls out.txt nofile 2> >(tee err.txt >&2) | tee ok.txt
実行結果:
$ ls out.txt nofile 2> >(tee err.txt >&2) | tee ok.txt
out.txt
ls: cannot access 'nofile': No such file or directory
$ cat err.txt
ls: cannot access 'nofile': No such file or directory
$ cat ok.txt
out.txt
複数のファイルに同時に出力する
teeの後ろにファイル名を並べると、すべてに同じ内容が書かれます。
echo "2026-10-03 backup ok" | tee a.txt b.txt c.txt
head a.txt b.txt c.txt
実行結果:
$ echo "2026-10-03 backup ok" | tee a.txt b.txt c.txt
2026-10-03 backup ok
$ head a.txt b.txt c.txt
==> a.txt <==
2026-10-03 backup ok
==> b.txt <==
2026-10-03 backup ok
==> c.txt <==
2026-10-03 backup ok
-a は並べたすべてのファイルに効きます。> /dev/null と組み合わせれば、画面に出さずに複数ファイルへ追記できます。
$ echo "追記" | tee -a a.txt b.txt > /dev/null
$ cat a.txt
2026-10-03 backup ok
追記
ファイルではなく別のコマンドへ分岐させる
プロセス置換を使うと、1つの出力を複数のコマンドへ同時に渡せます。 次の例は、1〜5 の数字を「行数を数える処理」と「逆順に並べる処理」の両方へ流しています。
seq 5 | tee >(wc -l > count.txt) >(sort -rn > rev.txt) > /dev/null
cat count.txt; cat rev.txt
実行結果:
5
5
4
3
2
1
最初の 5 が行数(count.txt)、続く5行が逆順の結果(rev.txt)です。
teeを挟むと失敗が隠れる問題を、pipefail と PIPESTATUS で直す
teeを使うときに知っておきたい大事な点があります。パイプ全体の終了ステータスは、既定では「最後のコマンド」の結果になります。 cmd | tee log の最後のコマンドは tee なので、cmd が失敗しても全体は成功(0)として扱われます。
$ false | tee s.txt; echo "status=$?"
status=0
false は必ず失敗する(終了ステータス1を返す)コマンドですが、$? は 0 になりました。スクリプトで ビルド | tee build.log のように書いていると、ビルドが失敗しても次の処理へ進んでしまいます。
再現:失敗したのに終了ステータスが 0 になる
わざと失敗するスクリプト build.sh を用意します。
#!/bin/bash
echo "step1 ok"
echo "step2 failed" >&2
exit 3
これを tee 経由で実行するスクリプト run_ng.sh です。
#!/bin/bash
./build.sh 2>&1 | tee build.log
echo "終了ステータス: $?"
実行結果:
$ bash run_ng.sh
step1 ok
step2 failed
終了ステータス: 0
build.sh は 3 で終了しているのに、表示は 0 です。
直し方1:set -o pipefail を付ける
set -o pipefail を実行しておくと、パイプの中で1つでも失敗したコマンドがあれば、その終了ステータスがパイプ全体の結果になります。
#!/bin/bash
set -o pipefail
./build.sh 2>&1 | tee build.log
echo "終了ステータス: $?"
実行結果:
$ bash run_ok.sh
step1 ok
step2 failed
終了ステータス: 3
対話的に試した結果も載せます。|| で「失敗したときの処理」をつなげられるようになります。
$ set -o pipefail
$ false | tee s.txt; echo "status=$?"
status=1
$ ls nofile 2>&1 | tee s.txt || echo "失敗を検知しました"
ls: cannot access 'nofile': No such file or directory
失敗を検知しました
直し方2:PIPESTATUS で個別の終了ステータスを取る
bashには、直前のパイプに含まれる各コマンドの終了ステータスを順番に保持する配列 PIPESTATUS があります。
$ false | tee s.txt; echo "PIPESTATUS=${PIPESTATUS[@]}"
PIPESTATUS=1 0
$ ls nofile 2>&1 | tee s.txt; echo "ls=${PIPESTATUS[0]} tee=${PIPESTATUS[1]}"
ls: cannot access 'nofile': No such file or directory
ls=2 tee=0
${PIPESTATUS[0]} が1つ目のコマンド、${PIPESTATUS[1]} が2つ目(tee)の結果です。PIPESTATUSは次のコマンドを実行すると書き換わるので、パイプの直後に変数へ移しておきます。
#!/bin/bash
./build.sh 2>&1 | tee build.log
rc=${PIPESTATUS[0]}
echo "終了ステータス: $rc"
exit "$rc"
実行結果:
$ bash run_ok2.sh; echo "呼び出し側: $?"
step1 ok
step2 failed
終了ステータス: 3
呼び出し側: 3
pipefail と PIPESTATUS の使い分け
| 方法 | 書き方 | 向いている場面 | 使えるシェル |
|---|---|---|---|
| pipefail | スクリプト冒頭に set -o pipefail |
スクリプト全体で、パイプ内の失敗を見逃したくない | bash、zsh など |
| PIPESTATUS | rc=${PIPESTATUS[0]} |
特定のパイプだけ、どのコマンドが失敗したかを知りたい | bash(zsh は小文字の pipestatus で、番号は1から。未実行) |
Ubuntuの sh(dash)では、手元の版で pipefail が使えませんでした。
$ sh -c "false | tee s.txt; echo \$?; set -o pipefail; false | tee s.txt; echo \$?"
0
sh: 1: set: Illegal option -o pipefail
pipefail や PIPESTATUS を使うスクリプトは、1行目を #!/bin/bash にして bash で実行すると確実です。
tee自身が書き込めなかった場合
tee自身が失敗した場合(書き込み先を開けないなど)は、teeの終了ステータスが 1 になります。このとき、画面への出力と、ほかの書き込めるファイルへの出力は続きます。
$ echo x | tee /opt/t199demo/app.conf; echo "status=$?"
tee: /opt/t199demo/app.conf: Permission denied
x
status=1
$ echo x | tee /nodir/x.txt ok2.txt; echo "status=$?"
tee: /nodir/x.txt: No such file or directory
x
status=1
$ cat ok2.txt
x
| メッセージ | 原因 | 直し方 |
|---|---|---|
tee: ファイル: Permission denied |
そのファイルに書き込む権限がない | sudo tee を使う。または書き込める場所に保存する |
tee: ファイル: No such file or directory |
保存先のディレクトリがない | 先に mkdir -p ディレクトリ で作る |
bash: ファイル: Permission denied |
teeではなくシェルのリダイレクトが開けなかった | > をやめて | sudo tee に書き換える |
エラーメッセージの先頭が tee: か bash: かで、どちらが原因かを見分けられます。
知っておくと役立つ動き
後ろのコマンドが先に終わると、ファイルは途中までになる
teeの後ろに head のような「必要な分だけ読んで終わるコマンド」を置くと、teeもそこで止まります。
$ seq 100000 | tee big.txt | head -n 2; echo "PIPESTATUS=${PIPESTATUS[@]}"
1
2
PIPESTATUS=141 141 0
$ wc -l big.txt
12773 big.txt
10万行を流しましたが、big.txt に残ったのは手元では 12,773 行でした(この行数は実行のたびに変わりえます)。141 は、書き込み先のパイプが閉じられたことを知らせるシグナル(SIGPIPE)で終了したことを表します。ファイルに全体を残したいときは、teeの後ろに途中で終わるコマンドを置かないか、先にファイルへ保存してから head ファイル で見る形にします。
GNU の tee には、この動きを変える -p オプション(後ろのパイプが閉じても、ファイルへの書き込みを続ける)があります。この記事では -p を付けた実行結果は載せていません(未実行。後述の --help の説明にもとづいています)。
ファイル名に - を書くと「-」という名前のファイルができる
GNU coreutils 9.4 の tee では、tee - は「-」という名前のファイルに書き込みました。標準出力を2回出す意味にはなりません。
$ seq 3 | tee - | head -3; ls -- -; rm -- -
1
2
3
-
日付入りのログファイルに追記する
コマンド置換($(...))と組み合わせると、日付入りのファイル名にできます。
echo "$(date +%T) start" | tee -a "run_$(date +%Y%m%d).log"
毎回同じファイルに積み上げていくログは -a を付け、実行のたびに作り直すログは -a なしにする、と使い分けます。
teeのオプション一覧(GNU coreutils 9.4)
tee --help の出力をもとにまとめました。
| オプション | 意味 | この記事での確認 |
|---|---|---|
-a, --append |
上書きせず、ファイルの末尾に追記する | 実行して確認 |
-i, --ignore-interrupts |
割り込みシグナル(Ctrl+C など)を無視する | 未実行(--help の説明) |
-p |
パイプ向けの動作モードにする(既定のモードは warn-nopipe) | 未実行(--help の説明) |
--output-error[=MODE] |
書き込みエラー時の動作を指定する | 未実行(--help の説明) |
--help |
使い方を表示する | 実行して確認 |
--version |
バージョンを表示する | 実行して確認 |
--output-error に指定できる MODE は次の4つです(--help の説明より)。
| MODE | 動作 |
|---|---|
warn |
どの出力先への書き込みエラーも知らせる |
warn-nopipe |
パイプ以外の出力先への書き込みエラーを知らせる |
exit |
どの出力先でも、書き込みエラーが起きたら終了する |
exit-nopipe |
パイプ以外の出力先で書き込みエラーが起きたら終了する |
実際の tee --help の冒頭です。
$ tee --version | head -n 1
tee (GNU coreutils) 9.4
$ tee --help
Usage: tee [OPTION]... [FILE]...
Copy standard input to each FILE, and also to standard output.
-a, --append append to the given FILEs, do not overwrite
-i, --ignore-interrupts ignore interrupt signals
-p operate in a more appropriate MODE with pipes.
--output-error[=MODE] set behavior on write error. See MODE below
macOS や BSD 系の tee は別の実装で、使えるオプションが異なる場合があります(この記事では未実行です)。-a は共通して使えるオプションとして広く知られています。
コピペ用:目的別の定型文
そのまま貼って、cmd とファイル名を置き換えて使えます。
# 1. 画面で見ながらログに残す(上書き)
cmd | tee run.log
# 2. 画面で見ながらログに追記する
cmd | tee -a run.log
# 3. エラーメッセージも含めてログに残す
cmd 2>&1 | tee run.log
# 4. bash で 3 を短く書く
cmd |& tee run.log
# 5. 元のコマンドの成否をそのまま返す(スクリプト用)
set -o pipefail
cmd 2>&1 | tee run.log
# 6. root だけが書けるファイルに1行追記する(画面には出さない)
echo "key=value" | sudo tee -a /etc/example.conf > /dev/null
# 7. root だけが書けるファイルを複数行で作る
sudo tee /etc/example.conf > /dev/null <<'EOF'
key1=value1
key2=value2
EOF
# 8. 複数のファイルへ同時に保存する
cmd | tee a.log b.log
# 9. 保存しつつ、次のコマンドへ渡す
cmd | tee raw.txt | sort | uniq -c
6〜7 の /etc/example.conf は説明用の名前です。実在の設定ファイルに使う前に、-a の有無をもう一度確かめてください。
使う前のチェックリスト
| 確認すること | 見るポイント |
|---|---|
| 追記か上書きか | 既存の内容を残すなら -a が付いているか |
| エラーも残したいか | 2>&1 または |& がパイプの前にあるか |
| 失敗を検知したいか | set -o pipefail か PIPESTATUS を使っているか |
| 権限が必要なファイルか | sudo が tee の前に付いているか(echo の前ではなく) |
| 画面表示は必要か | 不要なら > /dev/null を付ける |
| 大事なファイルか | 先に cp -p でコピーを取ったか |
よくある質問
Q. tee とリダイレクト(>)はどう使い分けますか?
A. ファイルに保存するだけでよければ >、画面でも結果を見たいとき・パイプの途中で保存したいとき・sudoで書き込みたいときは tee が向いています。上書きと追記の関係は、> と tee、>> と tee -a がそれぞれ対応します。
Q. sudo tee -a と sudo tee の違いは何ですか? A. -a を付けると末尾に追記、付けないとファイル全体を上書きします。設定ファイルに1行足す用途では -a を付けます。付け忘れると既存の行が消えるため、事前にコピーを取っておくと元に戻せます。
Q. tee を使ったら、コマンドが失敗しても成功扱いになります。なぜですか?
A. パイプの終了ステータスは、既定では最後のコマンド(tee)の結果になるためです。bashでは set -o pipefail を付けるか、${PIPESTATUS[0]} で元のコマンドの終了ステータスを取り出すと、失敗を検知できます。
Q. エラーメッセージがファイルに保存されません。どうすればよいですか?
A. パイプは標準出力だけを渡すためです。cmd 2>&1 | tee file のように、パイプの前に 2>&1 を書くと、標準エラー出力も一緒に保存されます。bashでは cmd |& tee file と短く書けます。
まとめ
- teeは、入力を画面とファイルの両方へ書き出すコマンドです。
cmd | tee fileが基本形です。 - 既定は上書き、追記は -a。
>と tee、>>と tee -a が対応します。 - sudo echo ... > file が Permission denied になるのは、リダイレクトを開くのが一般ユーザーのシェルだからです。
echo ... | sudo tee -a fileに書き換えると追記できます。 - 標準エラーも保存するなら 2>&1 | tee、bashなら |& tee と書きます。
- 複数ファイルへはファイル名を並べるだけ。 画面に出したくないときは
> /dev/nullを付けます。 - teeを挟むと元のコマンドの失敗が見えなくなるため、スクリプトでは
set -o pipefailか${PIPESTATUS[0]}を使います。 - エラーメッセージの先頭が tee: か bash: かで、原因がteeかリダイレクトかを見分けられます。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「teeコマンドの使い方|画面とファイル両方に出力・sudo teeで追記」
株式会社RJCでは、teeコマンドやリダイレクトの仕組みを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。