- Linux
Linux echoとprintfの使い方|改行・変数・色付き出力まとめ
echoは「文字列を1行出力して最後に改行を付ける」コマンド、printfは「書式を指定して出力する」コマンドです。 画面に一言出すだけなら echo "文字列"、改行の有無・桁そろえ・エスケープ(\n や \t)・色を正確に扱いたいなら printf '書式\n' 値 を選ぶと、どのシェルでも同じ結果になります。
echo は手軽な一方、-e や -n の結果がシェルによって変わります。 この記事では、同じコマンドを bash 5.2・zsh 5.9・dash 0.5.12(Ubuntu の /bin/sh)・BusyBox 1.36.1 で動かして比較表にしました。あわせて、ANSI カラーの色コード一覧、コピペで使える色付きログ関数、tput と NO_COLOR 対応、printf の書式指定子と -v、クォートによる変数展開の違いを、実行結果つきでまとめています。macOS は今回の環境では動かせなかったため、該当箇所に「未実行」と明記しています。
echoとprintfの早見表
やりたいことから探せる逆引き表です。 printf の列は、最後の2行(-v と %q)を除いて、今回試した bash・zsh・dash・BusyBox のどれでも使える書き方です。
| やりたいこと | echo(bash) | printf(どのシェルでも同じ) |
|---|---|---|
| 1行出力する | echo "hello" |
printf '%s\n' "hello" |
| 改行なしで出力 | echo -n "abc" |
printf '%s' "abc" |
| タブ・改行を入れる | echo -e "a\tb\nc" |
printf 'a\tb\nc\n' |
| 変数を出力 | echo "$name" |
printf '%s\n' "$name" |
| 変数をそのまま文字として出力 | echo '$name' |
printf '%s\n' '$name' |
| 空行を出力 | echo |
printf '\n' |
| ゼロ埋め(0007) | なし | printf '%04d\n' 7 |
| 小数第2位まで | なし | printf '%.2f\n' 3.14159 |
| 桁そろえ(左寄せ・右寄せ) | なし | printf '%-10s %5s\n' name qty |
| 赤い文字で出力 | echo -e "\e[31mNG\e[0m" |
printf '\033[31m%s\033[0m\n' "NG" |
| ファイルに書く / 追記する | echo "x" > f / echo "x" >> f |
printf '%s\n' "x" > f |
| 標準エラーに出す | echo "err" >&2 |
printf '%s\n' "err" >&2 |
| 結果を変数に入れる(bash / zsh) | id=$(echo ...) |
printf -v id 'ITEM-%04d' 7 |
| シェルに貼れる形に変換(bash / zsh) | なし | printf '%q\n' 'my file.txt' |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linux echoとprintfの使い方|改行・変数・色付き出力まとめ」
検証環境
記事中の実行結果は、すべて次の環境で出したものです。
| 項目 | バージョン |
|---|---|
| OS | Ubuntu 24.04.4 LTS |
| bash | 5.2.21 |
| zsh | 5.9 |
dash(/bin/sh のリンク先) |
0.5.12 |
| BusyBox sh | 1.36.1 |
/bin/echo、/usr/bin/printf |
GNU coreutils 9.4 |
echo と printf は、どちらも「シェルに組み込まれた版」と「単体のコマンド」の2種類があります。ふだん動いているのは組み込み版です。
$ type -a echo printf
echo is a shell builtin
echo is /usr/bin/echo
echo is /bin/echo
printf is a shell builtin
printf is /usr/bin/printf
printf is /bin/printf
組み込み版はシェルごとに別々に作られているため、同じ echo でも動きに差が出ます。 これが次の比較表の背景です。
echoの基本と -e・-n オプション
文字列を出力する・改行なしにする(-n)
$ echo hello
hello
$ echo -n "abc"; echo "<"
abc<
-n を付けると最後の改行が付かないため、続けて出した < が同じ行に並びます。
エスケープを解釈する(-e)と解釈しない(-E)
bash の echo は、既定では \t や \n をそのまま文字として出します。-e を付けるとタブや改行に変わります。
$ echo 'a\tb'
a\tb
$ echo -e 'a\tb'
a b
$ echo -e "a\nb" | wc -l
2
| エスケープ | 意味 | エスケープ | 意味 |
|---|---|---|---|
\n |
改行 | \\ |
バックスラッシュそのもの |
\t |
タブ | \e または \033 |
ESC(色の指定で使う) |
\r |
行頭に戻る | \c |
ここで出力をやめる(改行も付かない) |
\xHH |
16進数で指定した1バイト | \0NNN |
8進数で指定した1バイト |
bash・zsh・dash・BusyBoxでの結果比較(実行済み)
同じスクリプトを4つのシェルで動かし、出力を1バイトずつ確かめた結果です。 表の「TAB」はタブ文字、「改行」は改行文字が出力されたことを表します。
| コマンド | bash 5.2 | zsh 5.9 | dash 0.5.12(sh) | BusyBox sh |
|---|---|---|---|---|
echo 'a\tb' |
a\tb のまま |
a TAB b | a TAB b | a\tb のまま |
echo -e 'a\tb' |
a TAB b | a TAB b | -e a TAB b(-e が文字として出る) |
a TAB b |
echo -n 'abc' |
abc(改行なし) | abc(改行なし) | abc(改行なし) | abc(改行なし) |
echo 'a\nb' |
a\nb のまま |
a 改行 b | a 改行 b | a\nb のまま |
echo -E 'a\nb' |
a\nb のまま |
a\nb のまま |
-E a 改行 b |
a\nb のまま |
echo -ne 'a\n' |
a 改行 | a 改行 | -ne a 改行 改行 |
a 改行 |
echo 'x\cy' |
x\cy のまま |
x だけ(改行なし) | x だけ(改行なし) | x\cy のまま |
echo '\033[0m' |
文字のまま | ESC に変わる | ESC に変わる | 文字のまま |
printf 'a\tb\n' |
a TAB b | a TAB b | a TAB b | a TAB b |
printf '%s\n' '-n' |
-n |
-n |
-n |
-n |
読み取れることは3つです。
- bash と BusyBox は「
-eを付けたときだけ」エスケープを解釈する - zsh と dash は「何も付けなくても」エスケープを解釈する。 dash は
-eをオプションとして扱わず、文字として出力する - printf は4つのシェルすべてで同じ結果になる
スクリプトの1行目が #!/bin/sh のとき、Ubuntu や Debian では dash が動きます。bash で試して動いた echo -e "..." が、sh script.sh で実行すると行頭に -e と表示されるのはこのためです。
bashの設定で結果が変わる例(xpg_echo)
bash には、echo の動きを切り替える xpg_echo という設定があります。POSIX モードと組み合わせると、dash とほぼ同じ動きになり、-n も文字として出力されました。
$ bash --posix -O xpg_echo -c "echo -n 'abc'" | od -c
0000000 - n a b c \n
0000007
| コマンド | bash(既定) | bash --posix -O xpg_echo |
|---|---|---|
echo 'a\tb' |
a\tb のまま |
a TAB b |
echo -e 'a\tb' |
a TAB b | -e a TAB b |
echo -n 'abc' |
abc(改行なし) | -n abc 改行 |
macOS の /bin/sh はこの設定に近い動きをすると一般に知られています(未実行)。macOS の既定シェルである zsh については、上の表の zsh 5.9 の列が参考になります。Linux と macOS の両方で動かすスクリプトでは、次の節の printf を使うと、こうした差を意識せずに済みます。
どのシェルでも同じ結果にする書き方
「エスケープを使う」「改行を付けない」「- で始まる文字列を出す」の3つは printf に置き換えます。
| echo での書き方 | printf での書き方 |
|---|---|
echo -e "a\tb" |
printf 'a\tb\n' |
echo -n "abc" |
printf '%s' "abc" |
echo "$var"(中身が -n や \t を含むかもしれない) |
printf '%s\n' "$var" |
変数の中身が -n のとき、echo はそれをオプションとして受け取ります。printf の %s は、中身をそのまま文字として出します。
$ x=-n; echo "$x"; echo "<"
<
$ printf '%s\n' "$x"
-n
Windows 形式のパス(C:\temp\new)のようにバックスラッシュを含む文字列でも、%s なら形が変わりません。
$ echo -e "C:\temp\new"
C: emp
ew
$ printf '%s\n' "C:\temp\new"
C:\temp\new
変数展開とクォートの違い
ダブルクォート・シングルクォート・クォートなし
ダブルクォートは変数を展開し、シングルクォートは書いたとおりに出力します。 bash 5.2 での結果です。
$ name="RJC demo"
$ echo "$name"
RJC demo
$ echo '$name'
$name
$ echo "\$name"
$name
$ echo "${name}_v2"
RJC demo_v2
$ echo "$name_v2|"
|
最後の例は、name_v2 という別の変数として読まれ、空になりました。変数名のすぐ後ろに文字が続くときは ${name} と波かっこで区切ります。
| 書き方 | 変数 $name |
コマンド置換 $(...) |
* などのワイルドカード |
連続する空白 |
|---|---|---|---|---|
"..."(ダブルクォート) |
展開する | 実行する | 展開しない | そのまま残る |
'...'(シングルクォート) |
展開しない | 実行しない | 展開しない | そのまま残る |
| クォートなし | 展開する | 実行する | ファイル名に展開する | 1つにまとまる |
クォートなしで起きる違いを、実行結果で確かめます。
$ files="a.txt b.txt"
$ echo $files
a.txt b.txt
$ echo "$files"
a.txt b.txt
$ echo *.sh
t.sh
$ echo "*.sh"
*.sh
$ text=$(printf 'a\nb\n')
$ echo $text
a b
$ echo "$text"
a
b
クォートなしでは空白や改行が区切りとして扱われ、1つの空白にまとめられます。 変数を出力するときは "$var" とダブルクォートで囲むのを基本にすると、中身がそのまま出ます。
コマンドの結果・計算・文字数を埋め込む
$ echo "len=${#name} up=${name^^} def=${undefined:-none}"
len=8 up=RJC DEMO def=none
$ printf '%s\n' "total: $((3 * 4))"
total: 12
$ echo 'single: $HOME $(date)'
single: $HOME $(date)
| 書き方 | 意味 |
|---|---|
$(コマンド) |
コマンドの出力を埋め込む |
$((式)) |
計算結果を埋め込む |
${#var} |
文字数 |
${var:-既定値} |
変数が空のときに既定値を使う |
${var^^} |
大文字に変換(bash 4 以降) |
シングルクォートや $ そのものを出力する
$ echo "cost: \$5"
cost: $5
$ echo 'it'\''s'; echo "it's"
it's
it's
$ を文字として出したいときは、ダブルクォートの中で \$ と書くか、全体をシングルクォートで囲みます。シングルクォートの中にシングルクォートを入れたいときは、ダブルクォートで囲むのがいちばん簡単です。
複数行を出力する
$ echo "line1
line2"
line1
line2
$ echo $'tab\there'
tab here
$'...' は bash と zsh の書き方で、クォートの中の \t や \n をシェル自身が変換します。echo の -e に頼らずに済むので、変数に色コードや改行を入れておくときにも使えます(後の色付きログ関数で使います)。
printfの使い方と書式指定子早見表
基本の形:printf '書式' 値...
printf は最後に改行を付けません。改行が必要なら書式に \n を書きます。
$ printf '%s\n' hello
hello
$ printf 'no newline'; echo '<'
no newline<
値が書式より多いときは、書式が繰り返し使われます。配列や複数の値を1行ずつ出すときに便利です。
$ printf '%s is %d years\n' Sato 30 Suzuki 25
Sato is 30 years
Suzuki is 25 years
$ items=(pen note clip); printf '%s\n' "${items[@]}"
pen
note
clip
値が足りないときは、文字列は空、数値は 0 として扱われます。
$ printf '%s=%s\n' name pen price
name=pen
price=
書式指定子の早見表(実行結果つき)
| 指定子 | 意味 | コマンド例 | 出力 |
|---|---|---|---|
%s |
文字列 | printf '%s\n' hello |
hello |
%d |
整数 | printf '%d\n' 42 |
42 |
%5d |
5桁の幅で右寄せ | printf '[%5d]\n' 42 |
[ 42] |
%-5d |
5桁の幅で左寄せ | printf '[%-5d]\n' 42 |
[42 ] |
%05d |
5桁でゼロ埋め | printf '[%05d]\n' 42 |
[00042] |
%+d |
符号を付ける | printf '%+d %+d\n' 5 -5 |
+5 -5 |
%5s / %-5s |
文字列を右寄せ / 左寄せ | printf '[%5s] [%-5s]\n' ab ab |
[ ab] [ab ] |
%.3s |
先頭3文字だけ | printf '%.3s\n' abcdef |
abc |
%.2f |
小数第2位まで | printf '%.2f\n' 3.14159 |
3.14 |
%8.3f |
全体8桁・小数3桁 | printf '%8.3f\n' 3.14159 |
3.142 |
%e |
指数表記 | printf '%e\n' 31415.9 |
3.141590e+04 |
%x / %X |
16進数 | printf '%x %X\n' 255 255 |
ff FF |
%o |
8進数 | printf '%o\n' 8 |
10 |
%c |
先頭の1文字 | printf '%c\n' hello |
h |
%% |
% そのもの | printf '%5.1f%%\n' 85.25 |
85.2% |
%b |
値の中のエスケープを解釈 | printf '%b\n' 'a\tb' |
a(タブ)b |
%q |
シェルに貼れる形に変換(bash / zsh) | printf '%q\n' 'my file.txt' |
my\ file.txt |
%*d |
幅を引数で渡す | printf '%*d\n' 6 42 |
42 |
%(書式)T |
日時(bash 4.2 以降) | printf '%(%Y-%m-%d)T\n' 0 |
1970-01-01 |
%d には16進数(0x1F)や8進数(010)の値も渡せます。
$ printf '%d %d\n' 0x1F 010
31 8
ゼロ埋め・桁そろえのコピペ例
連番のファイル名や、列をそろえた一覧は printf の得意分野です。
$ for i in 1 2 3; do printf 'img_%03d.png\n' "$i"; done
img_001.png
img_002.png
img_003.png
$ printf '%-10s %5s\n' name qty pen 12 notebook 3
name qty
pen 12
notebook 3
$ printf '=%.0s' {1..20}; echo
====================
最後の例は、%.0s(0文字だけ出す)で値を消し、= を値の数だけ繰り返す書き方です。区切り線を引きたいときに使えます。
指定した幅より値が長いときは、切り詰められずにそのまま出力されます。
$ printf '%5s|%-5s|\n' 12345678 12345678
12345678|12345678|
%s と %b の違い
%s は値をそのまま、%b は値の中の \t や \n を変換して出します。
$ printf '%s\n' 'a\tb\nc'
a\tb\nc
$ printf '%b\n' 'a\tb\nc'
a b
c
echo -e "$var" の代わりに printf '%b\n' "$var" と書けば、bash・zsh・dash のどれでも同じ結果になります(dash でも %b が動くことを確認済み)。
%q:空白や記号を含む値を、シェルに貼れる形にする
$ printf '%q\n' 'my file.txt' "it's" 'a$b' ''
my\ file.txt
it\'s
a\$b
''
%q は、空白・クォート・$ などを、シェルにそのまま貼り付けて使える形に変換します。 実行するコマンドをログに残すときや、コマンド文字列を組み立てるときに役立ちます。
$ f='my file.txt'; cmd=$(printf 'ls -l %q' "$f"); echo "$cmd"
ls -l my\ file.txt
printf -v:出力を変数に入れる
-v 変数名 を付けると、画面に出す代わりに変数へ代入します。 $(...) と違って別のプロセスを作らないので、ループの中で何度も使うときに向いています。
$ printf -v id 'ITEM-%04d' 7; echo "$id"
ITEM-0007
-v と %q は bash と zsh で使えます。dash では次のエラーになりました。
$ dash -c "printf '%q\n' 'a b'"
dash: 1: printf: %q: invalid directive
$ dash -c "printf -v x '%d' 1"
dash: 1: printf: Illegal option -v
#!/bin/sh のスクリプトでは、id=$(printf 'ITEM-%04d' 7) のようにコマンド置換で受け取ると、dash でも ITEM-0007 が得られます(実行して確認)。
| 機能 | bash 5.2 | zsh 5.9 | dash 0.5.12 | BusyBox sh |
|---|---|---|---|---|
%s %d %05d %.2f %x |
使える | 使える | 使える | 使える |
%b |
使える | 使える | 使える | 使える |
%q |
使える | 使える | invalid directive |
invalid format |
-v 変数名 |
使える | 使える | Illegal option -v |
-v が文字として出る |
printfでつまずきやすい4つのポイント
1. 書式に変数を直接入れると、% が指定子として読まれる
$ msg='50% done'
$ printf "$msg\n"
50 0one
$ printf '%s\n' "$msg"
50% done
% d が書式として解釈され、値がないため 0 が入りました。変数は書式ではなく値の側(%s の相手)に渡すのが基本です。
2. 書式が - で始まるとオプション扱いになる
$ printf '-%s\n' x
bash: line 1: printf: -%: invalid option
printf: usage: printf [-v var] format [arguments]
$ printf -- '-%s\n' x
-x
-- を先に書くと、「ここから後ろはオプションではない」という合図になります。
3. %d に数値以外を渡すとエラーが出て 0 になる
$ printf '%d\n' abc
bash: line 1: printf: abc: invalid number
0
$ printf '%d\n' 3.5
bash: line 1: printf: 3.5: invalid number
3
上の3つのエラー文は bash -c '...' で実行したときの表示です(line 1 の部分は実行のしかたで変わります)。小数は %.0f や %.2f で受けます。なお zsh 5.9 では、printf '%d\n' abc はエラーを出さずに 0 を出力しました。
4. 日本語の桁そろえはシェルによって結果が変わる
$ bash -c "printf '%5s|\n' あい"
あい|
$ zsh -c "printf '%5s|\n' あい"
あい|
bash 5.2 は幅をバイト数で数え(「あい」は UTF-8 で6バイト)、zsh 5.9 は文字数で数えました。日本語を含む表は、列をタブで区切って column -t に渡すなど、別の方法でそろえると安定します。
ファイルへの書き込み・標準エラー・進捗表示
ファイルに書く(>)・追記する(>>)
> は新しく書き込み、>> は末尾に追記します。 printf は値を複数渡すと書式を繰り返すので、複数行のファイルを1コマンドで作れます。
$ printf '%s\n' "name=demo" "port=8080" > app.conf
$ cat app.conf
name=demo
port=8080
$ echo "debug=on" >> app.conf
$ wc -l app.conf
3 app.conf
タブ区切りのファイルも、書式に \t を書くだけで作れます。cat -A で確かめると、タブ(^I)と行末($)が意図どおり入っています。
$ printf '%s\t%s\n' id name 1 pen > t.tsv
$ cat -A t.tsv
id^Iname$
1^Ipen$
エラーメッセージは標準エラー(>&2)に出す
通常の結果は標準出力、エラーや警告は標準エラーに分けておくと、結果だけをファイルに保存できます。
$ { echo out; echo err >&2; } > o.txt 2> e.txt
$ cat o.txt
out
$ cat e.txt
err
同じ行を書き換えて進捗を表示する(\r)
\r(行頭に戻る)を先頭に置き、改行を付けずに出力すると、同じ行を上書きできます。cat -v で見ると、^M(\r)で行頭に戻りながら出力していることがわかります。
$ for i in 1 2 3; do printf '\rstep %d/3' "$i"; done | cat -v; echo
^Mstep 1/3^Mstep 2/3^Mstep 3/3
端末では step 3/3 だけが残って見えます。最後に echo で改行を入れておくと、次のプロンプトが同じ行に重なりません。
色付き出力:ANSIカラーの色コード一覧
基本の形
色は「ESC[ 番号 m」で始め、「ESC[0m」で元に戻します。 ESC は \033(8進数)、\e、\x1b(16進数)のどれでも書けます。cat -v を通すと、実際に出力されている制御文字を確認できます(^[ が ESC です)。
$ printf '\033[31mred\033[0m\n' | cat -v
^[[31mred^[[0m
$ echo -e "\e[1;32mbold green\e[0m" | cat -v
^[[1;32mbold green^[[0m
番号は ; で区切って複数指定できます。1;32 は「太字」かつ「緑」です。
文字色・背景色・装飾のコード一覧
| 色 | 文字色 | 背景色 | 明るい文字色 | 明るい背景色 |
|---|---|---|---|---|
| 黒 | 30 | 40 | 90 | 100 |
| 赤 | 31 | 41 | 91 | 101 |
| 緑 | 32 | 42 | 92 | 102 |
| 黄 | 33 | 43 | 93 | 103 |
| 青 | 34 | 44 | 94 | 104 |
| マゼンタ | 35 | 45 | 95 | 105 |
| シアン | 36 | 46 | 96 | 106 |
| 白 | 37 | 47 | 97 | 107 |
| 既定の色に戻す | 39 | 49 | なし | なし |
| 番号 | 装飾 | 解除する番号 |
|---|---|---|
| 0 | すべて元に戻す | なし |
| 1 | 太字 | 22 |
| 2 | 薄く表示 | 22 |
| 4 | 下線 | 24 |
| 7 | 文字色と背景色を入れ替える | 27 |
| 書き方 | 意味 |
|---|---|
\033[31m |
赤い文字 |
\033[1;31m |
太字の赤 |
\033[1;37;41m |
赤い背景に太字の白 |
\033[38;5;208m |
256色のうち208番(オレンジ系)を文字色に |
\033[38;2;255;100;0m |
RGB(255,100,0)を文字色に |
\033[0m |
すべて元に戻す |
256色・RGB 指定は、端末が対応している場合に表示されます。実際に見える色合いは端末の配色設定によって変わります。
$ printf '\033[1;37;41m FAIL \033[0m \033[4munder\033[0m\n' | cat -v
^[[1;37;41m FAIL ^[[0m ^[[4munder^[[0m
シェルごとの書き方の差
色の指定でも、echo はシェルによって結果が変わります。先ほどの比較表のとおり、bash では echo '\033[0m' は文字のまま出力され、-e が必要です。printf なら bash・zsh・dash のどれでも \033 を ESC に変換しました。
| 書き方 | bash | zsh | dash |
|---|---|---|---|
printf '\033[31mred\033[0m\n' |
色が付く | 色が付く | 色が付く |
echo -e "\e[31mred\e[0m" |
色が付く | 色が付く | 行頭に -e が出る |
echo "\033[31mred\033[0m" |
文字のまま | 色が付く | 色が付く |
コピペ用:色付きログ関数(NO_COLOR・パイプ対応)
色コードは、出力先が端末のときだけ付けるのがおすすめです。 ファイルに保存したり、別のコマンドにパイプで渡したりするときに色コードが混ざると、^[[31m のような文字がログに残るためです。次のスクリプトは、3つの条件を満たすときだけ色を付けます。
[ -t 1 ]:標準出力が端末につながっているNO_COLORが設定されていない(色を付けないでほしいという利用者の意思表示として、多くのツールが採用している環境変数)TERMがdumbではない
#!/usr/bin/env bash
# 色付きログ関数(NO_COLOR と 端末判定に対応)
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != "dumb" ]; then
C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'
C_BLUE=$'\033[34m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
else
C_RED=''; C_GREEN=''; C_YELLOW=''; C_BLUE=''; C_BOLD=''; C_RESET=''
fi
log_info() { printf '%s[INFO]%s %s\n' "$C_BLUE" "$C_RESET" "$*"; }
log_ok() { printf '%s[OK]%s %s\n' "$C_GREEN" "$C_RESET" "$*"; }
log_warn() { printf '%s[WARN]%s %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; }
log_error() { printf '%s%s[ERROR]%s %s\n' "$C_BOLD" "$C_RED" "$C_RESET" "$*" >&2; }
log_info "backup start"
log_ok "3 files copied"
log_warn "disk usage 85%"
log_error "cannot open items.csv"
端末で実行したときの出力を cat -v で見える形にしたものです(script コマンドで疑似端末を用意して実行。行末の ^M は疑似端末が付ける改行コードです)。
^[[34m[INFO]^[[0m backup start^M
^[[32m[OK]^[[0m 3 files copied^M
^[[33m[WARN]^[[0m disk usage 85%^M
^[[1m^[[31m[ERROR]^[[0m cannot open items.csv^M
同じスクリプトをパイプにつないだとき(bash log.sh 2>&1 | cat -v)と、NO_COLOR=1 を付けて端末で実行したときは、どちらも色コードなしになりました。
[INFO] backup start
[OK] 3 files copied
[WARN] disk usage 85%
[ERROR] cannot open items.csv
メッセージは %s の相手として渡しているので、85% のように % を含んでいてもそのまま出力されます。警告とエラーは >&2 で標準エラーに出しており、script.sh > result.txt としても画面に残ります。
tputで色を付ける
tput は、端末の種類(TERM)に合った制御文字を調べて出力するコマンドです。 色コードを自分で書く代わりに使えます。TERM=xterm-256color での出力です。
| コマンド | 意味 | 出力(cat -v で表示) |
|---|---|---|
tput setaf 1 |
文字色を赤に(0〜7 は上の表の 30〜37 と同じ順) | ^[[31m |
tput setaf 9 |
明るい赤 | ^[[91m |
tput setab 4 |
背景色を青に | ^[[44m |
tput bold |
太字 | ^[[1m |
tput smul |
下線 | ^[[4m |
tput sgr0 |
すべて元に戻す | ^[(B^[[m |
tput colors |
使える色数 | 256 |
tput cols |
端末の横幅 | 80 |
#!/usr/bin/env bash
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
red=$(tput setaf 1); green=$(tput setaf 2); bold=$(tput bold); reset=$(tput sgr0)
else
red=''; green=''; bold=''; reset=''
fi
printf '%s%sNG%s build\n' "$bold" "$red" "$reset"
printf '%sOK%s test\n' "$green" "$reset"
端末で実行したときと、パイプにつないだときの出力です。
# 端末(疑似端末で実行し cat -v で表示)
^[[1m^[[31mNG^[(B^[[m build^M
^[[32mOK^[(B^[[m test^M
# パイプ(bash tp.sh | cat -v)
NG build
OK test
色に対応していない端末(TERM=dumb)では、tput colors は -1 を返し、tput setaf 1 は何も出力せず終了コード 1 になりました。上のスクリプトのように tput colors が 8 以上かどうかを確かめておくと、どの端末でも表示が整います。
| 方法 | 向いている場面 |
|---|---|
| ANSI コードを直接書く | 依存を増やしたくない短いスクリプト。コンテナなど tput が入っていない環境 |
| tput | 端末の種類に合わせたい、色数を調べたい、横幅(tput cols)も使いたいとき |
echoとprintfの使い分け
| 場面 | おすすめ | 理由 |
|---|---|---|
| 対話中にちょっと表示する | echo | 短く書ける |
| 変数の中身をそのまま出す | printf '%s\n' "$var" |
-n や \t を含んでいても形が変わらない |
| 改行なし・エスケープ・色 | printf | シェルによる差がない |
| 桁そろえ・ゼロ埋め・小数 | printf | echo にはない機能 |
#!/bin/sh のスクリプト |
printf | dash では echo -e の -e が文字として出る |
| ファイルへ1行書く | どちらでも | echo "x" >> file で十分 |
よくある質問
Q. echo -e を付けたら、行頭に「-e」と表示されます。なぜですか?
A. スクリプトが dash(Ubuntu や Debian の /bin/sh)で動いているためです。dash の echo は -e をオプションとして扱わず、文字として出力します。printf 'a\tb\n' に置き換えるか、1行目を #!/usr/bin/env bash にして bash で実行すると解決します。
Q. 改行なしで出力するには、echo -n と printf のどちらがよいですか?
A. 今回試した bash・zsh・dash・BusyBox では、echo -n はどれも改行なしで出力しました。一方、bash を --posix -O xpg_echo で動かすと -n が文字として出力されました。設定に左右されない書き方としては printf '%s' "文字列" がおすすめです。
Q. printf で % そのものを出力するには?
A. 書式の中では %% と書きます(例:printf '%5.1f%%\n' 85.25 で 85.2%)。変数の中に % が含まれるときは、printf '%s\n' "$var" のように %s の相手として渡すと、そのまま出力されます。
Q. 色を付けたログをファイルに保存すると、^[[31m のような文字が入ります。
A. 色コードがそのまま保存されているためです。色を付ける前に [ -t 1 ] で出力先が端末かどうかを確かめ、端末のときだけ色コードを使うようにすると、ファイルやパイプにはコードなしの文字だけが出ます。本文の色付きログ関数がその形になっています。
まとめ
- echo は手軽に1行、printf は書式を決めて正確に。 改行は printf では
\nを自分で書く echo -eの結果はシェルで変わる。 bash と BusyBox は-eが必要、zsh と dash は既定でエスケープを解釈し、dash は-eを文字として出す- どのシェルでも同じ結果にしたいなら
printf '%s\n' "$var"。 変数は書式ではなく値として渡す - 変数は
"$var"とダブルクォートで囲む。 シングルクォートは展開せず、クォートなしは空白がまとまる - 書式指定子は
%s%d%05d%.2f%b%qを押さえる。-vと%qは bash と zsh で使える - 色は
\033[31mで始めて\033[0mで戻す。 文字色 30〜37、背景色 40〜47、太字は 1 - 色コードは端末のときだけ付ける。
[ -t 1 ]とNO_COLORを確かめると、ファイルやパイプに余分な文字が入らない
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Linux echoとprintfの使い方|改行・変数・色付き出力まとめ」
株式会社RJCでは、シェルの出力の仕組みと使い分けを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。