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

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

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