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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?