とほほのBash入門

目次

シェルの種類

以降では、現時点で最も利用されている bash に絞って説明していきます。動作確認は bash 4.2 で行っています。他のシェルや他のバージョンでは多少異なることがあります。

プロンプト

プロンプト は「うながす」の意味で、コマンドの入力を促するために表示されます。多くの場合、ユーザ名、ホスト名、カレントディレクトリなどの情報が含まれます。末尾は一般ユーザの場合は $、スーパーユーザーの場合は # となります。

[noda@msv02 ~]$ date
Sun May 24 00:42:55 UTC 2020
[noda@msv02 ~]$

プロンプトはシェル変数 PS1 で変更できます。

[noda@msv02 ~]$ PS1='[\u@\h \W]\$ '

使用可能な特殊文字は下記の通り。

\a		# ビープ音を鳴らす
\A		# 24時間表記の時分(例:23:59)
\d		# 曜日 月 日(例:Sun May 24)
\D{fmt}		# %Y/%m/%d %H:%M:%S などのフォーマットで日時を指定
\e		# エスケープ文字(ESC)
\h		# ホスト名(例:msv02)
\H		# ホスト名(例:msv02.example.com)
\j		# バックグランドジョブの個数(例:2)
\l		# ターミナル(例:/dev/pts/2)の最後のファイル名(例:2)
\n		# 改行
\r		# キャリッジリターン
\s		# シェル名(例:bash)
\t		# 24時間表記の時刻(例:23:59:59)
\T		# 12時間表記の時刻(例:11:59:59)
\@		# AM/PM付き12時間表記の時分(例:11:59 PM)
\u		# ユーザ名(例:tanaka)
\v		# bashのバージョン(例:4.2)
\V		# bashのバージョン(例:4.2.46)
\w		# カレントディレクトリ(例:~/bin)
\W		# カレントディレクトリ(例:bin)
\!		# ヒストリ番号(例:423)
\#		# 現在のセッションにおけるヒストリ番号(例:21)
\$		# ルート権限の場合は #、一般権限の場合は $
\nnn		# 8進数nnnの文字
\\		# バックスラッシュ
\[		# 非表示シーケンスの始まり
\]		# 非表示シーケンスの終了

プロンプトの色を変更するには \e[太さ;色番号m、元に戻すには \e[m を、\[ ... \] で挟んで指定します。太さは 0 が通常、1 が太字、色は、黒(30)、赤(31)、緑(32)、黄色(33)、青(34)、マジェンダ(35)、シアン(36)、灰色(37)、白(97) などを指定できます。(→ 詳細)

[noda@msv02 ~]$ PS1='\[\e[1;32m\][\u@\h \W]\$ \[\e[m\]

`...` を用いて、コマンドの実行結果をプロンプトに表示することもできます。

[noda@msv02 ~]$ PS1='[`hostname`]\$ '

メッセージ出力(echo, printf)

echo はメッセージを標準出力に書き出します。

echo "Message..."

-n オプションをつけると改行無しで書き出します。

echo -n "Input your name: "

printf は %d や %s などのフォーマットを用いてメッセージを書き出します。

printf "name=%s/age=%d\n" "Yamada" 26

メタ文字

シェルにおいて下記の文字は特別な意味を持つ文字(メタ文字)として扱われます。

*	ファイル名マッチで0文字以上の任意文字列にマッチ
?	ファイル名マッチで1文字の任意文字にマッチ
~	ホームディレクトリ
#	コメント
\	メタ文字を無効化 (\メタ文字)
$	変数展開($FOO)
"	文字列("...$FOO..." では変数展開が行われる)
'	文字列('...$FOO...' では変数展開が行われない)
`	コマンド実行結果参照(`cmd`)
!	ヒストリ参照 (!number)
;	コマンド区切り文字(cmd1 ; cmd2)
|	コマンドのの実行結果を次のコマンドに渡す(cmd1 | cmd2)
<	リダイレクト受信(cmd < file)
>	リダイレクト送信(cmd > file)
&	コマンドをバックグランド実行(cmd &)
( )	コマンドをグループ化((cmd1; cmd2))
[ ]	if文等で使用するテストコマンド
{ }	変数展開 (${FOO})

メタ文字はバックスラッシュ(\)を前に置くことでエスケープ(無効化)できます。

echo AT\&T                → AT&T

文字列 "..." の中では \, $, " ` がメタ文字となります。\ でエスケープできます。

echo "\\ \$ \" \`"        → \ $ " `

文字列 '...' の中では ' のみがメタ文字となります。\ でエスケープすることができないため、一度 '...' を終了させ、\` を記述した後、'...' を再開させます。

echo 'It'\''s a SONY.'    → It's a SONY.

もしくは、$'...' を使用すると '\ がメタ文字となり、\ でエスケープできます。

echo $'\' \\'             → ' \

シェル変数と環境変数($XXX)

bash には シェル変数環境変数 があります。シェル変数はそのシェルの中だけで使用できる変数、環境変数は子プロセスにも引き継がれる変数です。環境変数として定義された値はシェル変数としても参照できます。

set			# シェル変数を一覧表示する
FOO=xxx			# シェル変数を設定する
echo $FOO		# シェル変数を参照する
unset FOO		# シェル変数をクリアする

env			# 環境変数を一覧表示する
export BAR=xxx		# 環境変数を設定する
echo $BAR		# 環境変数を参照する
unset BAR		# 環境変数をクリアする

主な環境変数には下記などがあります。

PATH	コマンドの検索パス(例:/usr/local/sbin:/usr/local/bin...)
HOME	ホームディレクトリ(例:/home/noda)
LANG	言語情報(例:en_US.UTF-8)
PWD	カレントディレクトリ(例:/home/noda/tmp)
_	前回実行したコマンドの最後の引数

下記の様にして実行コマンドに対して、一時的な環境変数を引き渡すことができます。

[noda@msv02 ~]$ BASE_PATH=/opt/base anycommand arg1 arg2

特殊変数($x)

下記の特殊変数も使用できます。

$0	# シェルスクリプト名
$1~$9	# 1番目~9番目の引数
$*	# すべての引数(詳細後述)
$@	# すべての引数(詳細後述)
$#	# 引数の数
$?	# 直前に実行したコマンドの終了ステータス。0は成功、0以外は失敗
$-	# シェルの実行オプション (/bin/bash -opt)
$$	# シェルのプロセスID
$!	# 最後に実行したバックグランドプロセスのプロセスID
$_	# 最後に実行したコマンドの最後の引数

引数を受け取る際は、"$* "$@" "$1" の様に "..." で囲んだ方が安全です。囲まない場合、引数に * を含む文字を受け取った場合、カレントフォルダのファイル名に展開されてしまします。

command "$1"		# 安全。引数の * はそのまま参照できる
command $1		# 危険。引数の * がファイル名に展開されてしまう

$*$@ は似ていますが、"$*""$1 $2 $3 ..."と見なされるのに対し、"$@""$1" "$2" "$3" ... とみなされます。シェルスクリプトが3つの引数を受け取った場合、次のようになります。

command "$*"	# commandは"$1 $2 $3"という1つの引数を受け取る
command "$@"	# commandは"$1" "$2" "$3"という3つの引数を受け取る

変数展開(${...})

下記の例では、シェル変数 COLOR に1文字以上の値が設定されていればその値を、さもなくば white を BGCOLOR に代入します。

BGCOLOR=${COLOR:-white}

変数展開には次のようなものがあります。ここで「定義済」とは変数が定義されていること、「設定済」とは変数が定義され1文字以上の文字を設定されていることを示します。

${FOO}			# FOOの値
${FOO:-word}		# FOOが設定済であればその値、さもなくばword(FOOは未設定のまま)
${FOO:=word}		# FOOが設定済であればその値、さもなくばword(FOOにもwordを設定)
${FOO-word}		# FOOが定義済であればその値、さもなくばword(FOOは未設定のまま)
${FOO=word}		# FOOが定義済であればその値、さもなくばword(FOOにもwordを設定)
${FOO:?word}		# FOOが未設定であればwordを標準出力に出力してシェル終了
${FOO:+word}		# FOOが設定済であればword、未設定時は空文字
${FOO:n}		# FOOのn文字目(0始まり)以降の文字列
${FOO:n:m}		# FOOのn文字目(0始まり)からm文字分の文字列
${FOO#word}		# FOOの先頭からwordにマッチする部分を除外(最短マッチ)(*も使用可)
${FOO##word}		# FOOの先頭からwordにマッチする部分を除外(最大マッチ)(*も使用可)
${FOO%word}		# FOOの末尾からwordにマッチする部分を除外(最短マッチ)(*も使用可)
${FOO%%word}		# FOOの末尾からwordにマッチする部分を除外(最大マッチ)(*も使用可)
${!FOO*}		# FOOではじまる変数名の一覧
${!FOO@}		# 同上
${#FOO}			# FOOの文字数。未設定時は0
${FOO/ptn/word}	# パターンptnへのマッチ部分をwordに置換(部分マッチ,1回のみ)(*も使用可)
${FOO//ptn/word}	# パターンptnへのマッチ部分をwordに置換(部分マッチ,すべて)(*も使用可)
${FOO/#ptn/word}	# パターンptnへのマッチ部分をwordに置換(先頭マッチ)(*も使用可)
${FOO/%ptn/word}	# パターンptnへのマッチ部分をwordに置換(末尾マッチ)(*も使用可)
${FOO^}			# 最初の1文字を大文字化
${FOO^^}		# すべての文字を大文字化
${FOO,}			# 最初の1文字を小文字化
${FOO,,}		# すべての文字を小文字化
${FOO~}			# 最初の1文字を大文字・小文字反転
${FOO~~}		# すべての文字を大文字・小文字反転(何に使うんだろ...)
${FOO^[pattern]}	# patternいずれかの文字と合致時のみ大小文字変換(^^ , ~等でも可)
${!FOO[*]}		# 連想配列FOO[key]のキー一覧(連想配列はdeclare -A宣言が必要)
${!FOO[@]}		# 同上
${FOO[*]}		# 連想配列FOO[key]の値一覧(連想配列はdeclare -A宣言が必要)
${FOO[@]}		# 同上
${#FOO[*]}		# 連想配列FOO[key]の個数(連想配列はdeclare -A宣言が必要)
${#FOO[@]}		# 同上

読み込み専用(readonly)

readonly をつけると読込専用の固定値となり、値を上書きすることができなくなります。

$ readonly MSG=AAA
$ MSG=BBB
-bash: MSG: readonly variable
$

シェルスクリプト(*.sh)

シェルコマンドをファイルに保存して、シェルスクリプトとして実行することができます。拡張子には通常 .sh をつけます。

$ cat > ./test.sh <<EOF
echo "Hello world!"
EOF
$ bash ./test.sh
Hello world!

1行目に #!/bin/bash を書き、ファイルのパーミッションを 755 にしておくとコマンドとして直接実行ができるようになります。

$ cat > ./test.sh <<EOF
#!/bin/bash
echo "Hello world!"
$ chmod 755 ./test.sh
$ ./test.sh
Hello world!

コメント(#)

# から行末まではコメントとみなされて実行されません。

# この行はコメントとみなされます。
echo "Hello world!"

継続行(\)

行末にバックスラッシュ(\)をつけると、ひとつのコマンドを複数行に分割して記述することができます。

echo "This is Japan." \
     "That is America." \
     "That is England."

コマンド区切り(;)

セミコロン(;)を用いると、1行に複数のコマンドを記述することができます。

echo "This is Japan."; echo "That is America."

グループコマンド({...})

コマンド群を {...} で囲むことにより、グループコマンドとしてまとめ、一括でリダイレクト先を指定したりできます。

{ pwd; date; } > output.txt

サブシェル((...))

コマンド群を (...) で囲むことにより、サブシェルでコマンド群を実行します。

( cd dir1; pwd )
( cd dir2; pwd )

コマンド置換($(...), `...`)

$(...) または `...` の中にコマンドを書くと、コマンドの出力が文字列として扱われます。

echo "DEBUG:..." >> $(date +"%Y%m%d").log
echo "DEBUG:..." >> `date +"%Y%m%d"`.log

初期化ファイル(.bash_profile/.bashrc)

ログイン時には次のファイルが実行されます。環境変数の設定や初期化などに使用されます。

/etc/profile		ログイン時に実行される処理(全ユーザ)
~/.bash_profile	ログイン時に一度だけ実行される処理(自分のみ)
~/.bashrc		シェル起動時に毎回実行される処理(自分のみ)

ログアウト時には次のファイルが実行されます。

/etc/bash.bash_logout	ログアウト時に実行される処理(全ユーザ)
~/.bash_logout		ログアウト時に実行される処理(自分のみ)

ファイルを変更後、再読み込みするには、一度ログアウトして再ログインするか、source コマンドまたは . コマンドでファイルを読み込みます。

[noda@msv02 ~]$ source ~/.bashrc
[noda@msv02 ~]$ . ~/.bashrc

入出力・リダイレクト(>)

コマンドは 標準入力(stdin) から読み込み、標準出力(stdout), 標準エラー出力(stderr) に書き出すことができます。

command < file			# fileの内容をcommandの標準入力に渡す

コマンドの標準出力、標準エラー出力をファイルに書き出すには次のようにします。1 は標準出力、2 は標準エラー出力、& は両方を意味します。1>> と省略できます。

command > file			# 標準出力をfileに書き出す
command 1> file		# 標準出力をfileに書き出す(>と等価)
command 2> file		# 標準エラー出力をfileに書き出す
command 1> file1 2> file2	# 標準出力をfile1に、標準エラー出力をfile2に書き出す
command &> file		# 標準出力と標準エラー出力をfileに書き出す
command >& file		# &>と同義

>>> とするとファイルを上書きするのではなく、ファイルに追記するようになります。

command >> file		# 標準出力をfileに追記する
command 1>> file		# 標準出力をfileに追記する(>>と等価)
command 2>> file		# 標準エラー出力をfileに追記する
command 1>> file1 2>> file2	# 標準出力をfile1に、標準エラー出力をfile2に追記する
command &>> file		# 標準出力と標準エラー出力をfileに追記する
command >>& file		# &>>と同義 ... と思ったら嘘。使用できない

2>&1 は 2 の出力を 1 に、1>&2 は 1 の出力を 2にマージすることを意味します。

command > file 2>&1		# &> と等価 (2を1の出力にマージする)
command >> file 2>&1		# &>> と等価 (2を1の出力にマージする)
command 1>&2			# 標準出力を標準エラー出力に書き出す

ヒアドキュメント(<<)

<<EOF と書くと、EOF が現れるまでの複数行をひとつの文字列として扱います。EOF は END_OF_FILE でも FAIRU_NO_OWARI でもなんでもよいです。下記の例では This is... の3行を test.txt に書き込みます。

cat > ./test.txt <<EOF
This is Japan.
That is America.
That is England.
EOF

通常のヒアドキュメントではドキュメント内の変数($...)が展開されますが、<<'EOF' とすると変数が展開されません。

NAME=Tanaka
cat > ./test.txt <<'EOF'
Hello $NAME
EOF

パイプ(|)

パイプ を用いて、あるコマンドの出力を、次のコマンドの標準入力に渡すことができます。パイプでは 1|, 2|, &| は使用できません。

cmd1 | cmd2			# cmd1の標準出力をcmd2の標準出力に渡す
cmd1 |& cmd2			# cmd1の標準出力と標準エラー出力をcmd2の標準出力に渡す
cmd1 2>&1 | cmd2		# &|と同義
cmd1 2> /dev/null | cmd2	# cmd1の標準エラー出力のみをcmd2に渡す

ファイルにも記録しながら画面でも確認したい時は tee コマンドが利用できます。

cmd |& tee file	# 標準出力も標準エラー出力もfileに書き込みながら画面にも表示する

バックグラウンド実行(&)

& を用いて、プロセスをバッググラウンドで実行することができます。プロセスがフォアグラウンドで起動中に Ctrl-z を押すと、プロセスは一時停止中の状態でバックグランドプロセス(ジョブ)になります。

command &		# コマンドをバックグラウンドで実行
jobs			# バックグラウンドプロセスの一覧を表示
fg %2			# 2番ののジョブをフォアグラウンドに
wait %2			# 2番のジョブの終了を待つ
kill %2			# 2番のジョブを停止
Ctrl-z			# フォアグラウンド実行中プロセスを一時停止状態に
bg %2			# 2番のジョブ(一時定期中)を再開

jobs+ 記号がついているのがカレントジョブ、- がついているのがひとつ前のジョブ。%%%+ はカレントジョブ番号、%- はひとつ前のジョブ番号、%文字列 は、コマンドラインの先頭が文字列にマッチするジョブ番号(複数マッチ時はエラー)を示します。

kill %-			# 直前に実行したジョブを停止
kill %foo		# コマンドラインがfooで始まるジョブ(1つ)を停止

ヒストリ(history)

下記のヒストリ機能を使用することができます。

history		# ヒストリの一覧を表示する
		# ひとつ前のヒストリを表示する(Ctrl-pも可)
		# ひとつ後のヒストリを表示する(Ctrl-nも可)
^str1^str1^	# 直前コマンドのstr1str2に置換
!#		# 現在入力中のコマンド
Ctrl-r		# 履歴検索モードに移行

下記のヒストリ参照を利用できます。

!!		# 直前のヒストリ
!123		# 123番目のヒストリ
!-3		# 3個前のヒストリ
!str		# strで始まる直近のヒストリ
!?str?		# strを含む直近のヒストリ(strの後ろが改行の場合は最後の?は省略可)

検索したヒストリに対して、下記の引数参照を利用できます。コマンド名は0番目の引数として扱われます。

!!:n		# n番目の引数
!!:^		# 最初の引数
!!:$		# 最後の引数
!!:n-m		# n番目からm番目までの引数
!!:^-m		# 最初からm番目までの引数
!!:n-$		# n番目から最後までの引数
!!:*		# 最初から最後までの引数(1-$)
!!:n*		# 最初から最後までの引数(n-$)
!!:n-		# 最初から最後のひとつ前までの引数
!:%		# 直近の?str?にマッチした引数

検索したヒストリや引数に対して、さらに下記を付与することができます。:で連結して複数指定できます。

:h		# ファイル名を削除
:t		# ディレクトリ名を削除
:r		# 拡張子を削除
:e		# 拡張子以外を削除
:p		# 表示はするけど実行しない
:q		# 全体を'...'で囲んで表示
:x		# 単語毎に'...'で囲んで表示
:s/str1/str2/	# str1str2に置換(最初のひとつ)
:gs/str1/str2/	# str1str2に置換(全置換)
:Gs/str1/str2/	# str1str2に置換(単語毎にひとつ)
:&		# 直前の置換を繰り返す

行編集(Ctrl-a, Ctrl-b, ...)

コマンドラインでは下記のような行編集を利用できます。

Ctrl-p		# ヒストリのひとつ前のコマンドを呼び出し
Ctrl-n		# ヒストリのひとつ後のコマンドを呼び出し
Ctrl-a		# カーソルを先頭に移動する
Ctrl-e		# カーソルを行末に移動する
Ctrl-b		# カーソルを1文字前に移動する
Ctrl-f		# カーソルを1文字後に移動する
ESC b		# カーソルを1単語前に移動する
ESC f		# カーソルを1単語後に移動する
Ctrl-k		# カーソルから行末までを削除する
Ctrl-u		# カーソルから行頭までを削除する
Ctrl-h		# カーソルから1文字前を削除する
Ctrl-d		# カーソルから1文字を削除する
Ctrl-w		# カーソルから1単語前を削除する

ディレクトリスタック(dirs)

ディレクトリを移動しまくる時、ディレクトリスタックの機能を利用すると便利です。今いるディレクトリを覚えておきたいときは pushd dir で移動し、戻る時は pupd、スタックがいくつかある時は pushd +num で移動します。numは0から数えます。スタックの0番目が常にカレントディレクトリとなります。

# スタックを表示する
dirs			# スタックの一覧を表示する(1列表示)
dirs -v			# スタックの一覧を表示する(複数行表示)
# スタックを増やす
pushd dir		# スタックの0番目にdirを追加する(先頭になるのでそこに移動する)
pushd -n dir		# スタックの1番目にdirを追加する(先頭ではないのでそこには移動しない)
# スタック全体を回転する
pushd +num		# 先頭からnum番目のスタックが先頭になるように回転し、先頭スタックに移動
pushd -num		# 末尾からnum番目のスタックが先頭になるように回転し、先頭スタックに移動
# スタックを削除する
popd			# 0番目のスタックを消す(必然的に1番目のスタックに移動する)
popd +num		# 先頭からnum番目のスタックを削除する
popd -num		# 末尾からnum番目のスタックを削除する
popd -n			# 先頭から1番目のスタックを削除する
dirs -c			# スタックをすべて削除する

条件実行(; && ||)

;, &&, || を用いて複数のコマンド実行条件を制御することができます。

cmd1 ; cmd2		# cmd1が終了したらcmd2を実行する
cmd1 && cmd2		# cmd1が成功したらcmd2を実行する
cmd1 || cmd2		# cmd1が失敗したらcmd2を実行する

条件分岐(if)

if文は、条件が真の場合に処理を実行します。

if 条件; then
    処理
fi

elifelse も使用できます。下記の例では条件1が真の時は処理1を、条件2が真の時は処理2を、さもなくは処理3を実行します。

if 条件1; then
    処理1
elif 条件2; then
    処理2
else
    処理3
fi

条件の部分にはコマンドを指定します。コマンドが成功、つまり、コマンドの実行結果($?)が 0 であれば真、0 以外であれば偽とみなします。

if command1; then
    echo "成功しました"
fi

[ ... ]test コマンドの別名で、... に指定した引数を条件として判断し、合致すれば真となります。if文と組み合わせることで、条件判断ができるようになります。下記の例では、変数 TEMP が未設定または空文字であれば、/var/tmp を設定します。TEMP が未設定の場合にエラーにならないように、$TEMP を "..." で囲んでいます。

if [ "$TEMP" = "" ]; then
    TEMP=/var/tmp
fi

bash では、[ ... ] コマンドの代わりに、ビルトインの条件判断式 [[ ... ]] を使用することができます。[ ... ] よりも高速・高機能です。

if [[ "$TEMP" = "" ]]; then
    TEMP=/var/tmp
fi

[ ... ] の条件には下記などを使用できます。

( exp )			# exp をグルーピング
! exp			# exp が偽であれば
exp1 -a exp2		# exp1 かつ exp2 であれば
exp2 -o exp2		# exp1 または exp2 であれば
str1 = str2		# 文字列 str1str2 が等しければ
str1 != str2		# 文字列 str1str2 が等しくなければ>
-z str			# 文字列 str が0文字であれば(zero)
-n str			# 文字列 str が0文字で以上であれば(not zero)
str			# 文字列 str が0文字で以上であれば(-n str と同じ)
num1 -eq num2		# 数値 num1num2 と等しければ(equal)
num1 -ne num2		# 数値 num1num2 異なっていれば(not equal)
num1 -ge num2		# 数値 num1num2 以上であれば(grater than or equal)
num1 -gt num2		# 数値 num1num2 より大きければ(grater than)
num1 -le num2		# 数値 num1num2 以下であれば(less than or equal)
num1 -lt num2		# 数値 num1num2 より小さければ(less than)
file1 -ef file2	# ファイル file1file2 と同一実体であれば(equal file)
file1 -nt file2	# ファイル file1file2 より新しければ(newer than)
file1 -ot file2	# ファイル file1file2 より古ければ(older than)
-e file			# ファイル file が存在していれば
-s file			# ファイル file が0バイト以上であれば
-f file			# ファイル file がレギュラーファイルであれば
-r file			# ファイル file が読み込み可能であれば
-w file			# ファイル file が書き込み可能であれば
-x file			# ファイル file が実行可能(ディレクトリの場合は移動可能)であれば
-d file			# ファイル file がディレクトリであれば
-h file			# ファイル file がシンボリックリンクファイルであれば(-Lと同義)
-L file			# ファイル file がシンボリックリンクファイルであれば(-hと同義)
-b file			# ファイル file がブロックデバイスファイルであれば
-c file			# ファイル file がキャラクタデバイスファイルであれば
-p file			# ファイル file が名前付きパイプであれば
-S file			# ファイル file がソケットファイルであれば
-k file			# ファイル file にスティッキービットが設定されていれば(chown o+t)
-u file			# ファイル file にセットユーザIDビットが設定されていれば(chown u+s)
-g file			# ファイル file にセットグループIDビットが設定されていれば(chown g+s)
-O file			# ファイル file が実効ユーザIDに所有されていれば
-G file			# ファイル file が実効グループIDに所有されていれば
-t fd			# ファイルディスクリプタ fd がターミナルとして開かれていれば

[[ ... ]] の条件には追加で下記などを使用できます。

exp1 && exp2		# exp1 かつ exp2 であれば
exp2 || exp2		# exp1 または exp2 であれば

条件分岐(case)

case文は、文字列のパターンによって処理を振り分けます。パターンには *? のワイルドカードも使用できます。

case $FILENAME in
  *.txt)  echo "Text file." ;;
  *.html) echo "HTML file." ;;
  *)      echo "Unknwown file." ;;
esac

繰り返し(for)

for ... in 文は、リストをひとつずつ変数に格納しながらループします。下記の例では引数 $@ を順番にひとつずつ処理していきます。引数の * や ? がファイル名に展開されないように "..." で囲んでいます。

for i in "$@"
do
    echo "$i"
done

{n..m} は、nmまでの数値のリストを返却します。下記の例は 1~10 のループを回します。詳細は ブレース展開 を参照してください。

for i in {1..10}
do
    echo "$i"
done

bash では下記の様な記述もできます。

for ((i = 0; i <= 10; i++)) {
    echo "$i"
}

繰り返し(while)

while文は、条件が真の間処理をループします。次の例は標準入力から入力が無くなるまで read コマンドで1行ずつ読み込み、処理します。

while read line
do
    echo $line
done

繰り返し(until)

until文は、条件が真になるまで処理をループします。下記の例は、0~9 のループを回します。

i=1
until [ $i -eq 10 ]
do
    echo $i
    i=`expr $i + 1`
done

選択肢(select)

select文は、選択肢を表示し、入力を受け取るループを回します。下記の例は色を選択してもらう例です。ループを抜けるには break します。

select i in Red Green Blue
do
    case $i in
      Red)   echo "Red!!"; break ;;
      Green) echo "Green!!"; break ;;
      Blue)  echo "Blue!!"; break ;;
      *)     echo "Bad select!!" ;;
    esac
done

実行すると下記の様に表示し、選択を促します。

1) Red
2) Green
3) Blue
#?    ← 1 か 2 か 3 を入力して Enter

ループ終了(break)

break は最も内側のループ(for, while, until, select)を抜けます。引数(数値n)を指定すると内側から数えて n番目のループを抜けます。

for i in {1..10}; do
  echo $i
  if [[ $i -eq 5 ]]; then
    break
  fi
done

ループ継続(continue)

continue は最も内側のループ(for, while, until, select)の次のループに遷移します。引数(数値n)を指定すると内側から数えて n番目のループに遷移します。

for i in {1..10}; do
  if [[ $i -eq 5 ]]; then
    continue
  fi
  echo $i
done

入力(read)

read はキーボードからの入力を求めます。下記の例では Input your name: と表示した後、キーボードから名前を入力してもらい、それを表示します。

echo -n "Input your name: "
read name
echo "Hello $name!"

シェルの終了(exit)

exit はシェルを終了します。引数に 0~255 の数値を指定するとシェルスクリプトの戻り値として扱われます。通常、成功時には 0 を返却し、エラーの場合は 1~255 の値を返却します。戻り値は $? で参照することができます。

exit
exit 0
exit 1

トラップ(trap)

trap はシェルがトラップを受けた時の動作を指定します。下記の例ではシェルが SIGINT を受け取った時に SIGINT!! を表示して終了します。

trap 'echo "SIGINT!!"; exit' SIGINT
for i in {1..10}; do
  echo $i; sleep 1
done

シェル実行(exec)

exec は現在のプロセスの子プロセスとしてコマンドを実行するのではなく、現在のプロセスをコマンドのプロセスに置き換えます。例えば、/usr/bin/fgrep の中身は下記のようなシェルスクリプトになっています。これにより、fgrep プロセスと grep 子プロセス、2個のプロセスではなく、fgrep プロセスだけが起動することになり、効率がよくなります。

#!/bin/sh
exec grep -F "$@"

また、exec は下記の様に、以降のコマンドのリダイレクトを制御するのにも使用されます。下記の様な1行を先頭に記述することにより、以後のコマンドの実行結果をログファイルに記録することができます。

#!/bin/bash
exec &> /var/log/xxxx.log
command1
command2
  :

標準出力にもログファイルにも書き出すには次のようにします。

#!/bin/bash
exec 2> >(tee -a /tmp/xxx.log) 1>&2
command1
command2
  :

引数シフト(shift)

shift は引数 $2 を $1 に、$3 を $2 に、$4 を $3... にシフトします。コマンドラインオプションを解析するケースなどで使用されます。

A_FLAG=0
C_FLAG=0

while [[ $# -gt 0 ]]; do
  case $1 in
    -a) A_FLAG=1;;
    -c) C_FLAG=1;;
    -*) echo "Unknown option $1."; exit 1;;
    *) break;;
  esac
  shift
done

echo A_FLAG=$A_FLAG
echo C_FLAG=$C_FLAG
for i in "$@"; do
  echo "File: $i"
done

オプション解釈(getopts)

getopts はオプション解釈に便利なツールです。下記の例ではパラメータ無しの -a オプションと、パラメータ有りの -c オプションと、ファイルリストを引数とするコマンドのオプション解釈ロジックです。"ac:" はオプションとして -a と -c を受け取り、: はオプションパラメータがあることを示します。パラメータは $OPTARG に格納されます。

#!/bin/bash
while getopts "ac:" OPT
do
  case $OPT in
    a) A_FLAG=1;;
    c) C_FLAG=1; CONFIG_FILE=$OPTARG;;
    *) echo "Usage: cmd [-a] [-c config_file] file..."; exit 1;;
  esac
done

echo A_FLAG=$A_FLAG
echo C_FLAG=$C_FLAG
echo CONFIG_FILE=$CONFIG_FILE
shift $((OPTIND - 1))
for i in "$@"; do
  echo "File: $i"
done

ソース読み込み(source)

source は指定したシェルスクリプトを現在のシェルで実行します。サブプロセスではなく現在のシェルで実行するため、ファイル内に記述した alias、bind、環境変数等も有効となります。

source ~/.bashrc

エイリアス(alias)

インタラクティブモードのシェルではエイリアスを使用することができます。

alias			# 定義済みエイリアス一覧を表示する
alias ll='ls -l'	# エイリアスを定義する
unalias ll		# エイリアスを削除する

バインド(bind)

bind で Ctrl-x などのキー入力にコマンドや関数を割り当てることができます。下記の例では Ctrl-l に ls -lF を割り当てています。

bind -x '"\C-l": "ls -lF"'

ジョブ制御(Ctrl-Z, jobs, fg, bg)

ジョブ制御は、巨大なディレクトリの削除など、時間のかかる処理を始めてしまった場合に、バックグランドジョブとして裏で実行を継続しながら他の作業を行う場合などに便利です。Ctrl-Z は現在実行中のコマンドを中断してシェルに戻ります。

$ sleep 100
Ctrl-Z
[1]+  Stopped     sleep 100
$

jobs はシェル内から実行中のジョブの一覧を表示します。Stopped はそのジョブが停止中であることを示します。

$ jobs
[1]+  Stopped     sleep 100
$

bg は指定したジョブをバックグランドジョブとして再開します。ジョブは jobs で表示される番号に % をつけて指定します。引数を省略すると jobs の結果で + がマークされているジョブが対象となります。

$ bg %1
[1]+ sleep 100 &
$ jobs
[1]+  Running     sleep 100 &
$

fg は指定したジョブをフォアグランドジョブとして再開します。ジョブは bg と同様に指定します。

$ fg %1
sleep 100
$

関数(function)

下記の様に関数を定義することができます。

function myfunc() {
    echo "func is called."
}

function は省略可能です。

myfunc() {
    echo "func is called."
}

関数はコマンドの様に呼び出すことができます。

myfunc arg1 arg2 arg3

引数は、$0, $1, $2, ... $*, $@, $# で参照します。

myfunc() {
    for i in "$@"
    do
        echo "[$i]"
    done
}

local をつけて宣言した変数は関数内のローカル変数として扱われます。

function myfunc() {
    local MSG="Hello"
       :
}

return で値を返すとコマンドの戻り値として扱われ、$? で参照することができます。

function myfunc() {
    return 1
}
func
echo $?

ブレース展開({...})

比較的新しくサポートされた機能ですが、ブレース {...} で囲まれた文字を展開できる機能があります。for文 と組み合わせることにより威力を発揮します。

{1..10}			# 1 2 3 ...10 に展開
{1..10..2}		# 2個とばしに 1 3 5 7 9 に展開
{aa,bb,cc}		# aa bb cc に展開
file.{txt,bak}		# file.txt file.back に展開
file{1..3}.txt		# file1.txt file2.txt file3.txt に展開
file{1..3}.{txt,bak}	# file1.txt file1.bak file2.txt ... に展開

数値計算($((...)))

シェルで簡単な数値計算を行う際には expr コマンドを用いていました。

$ expr 2 + 5
7

ただし、パラメータはすべてコマンドの引数として指定するため、数値と演算子の間にスペースが必要、掛け算の * はシェルのメタ文字と解釈されないように \ でエスケープしたりなど少々面倒でした。

$ expr 2 \* 5
10

最近のシェルでは $((...)) を用いて演算を行うことができます。

$ echo $((2+5))
7
$ echo $((2*5))
10

ただし、expr$((...)) も小数や sqrt() などの高度な演算を行うことはできません。bc コマンドもありますが、桁数の指定が面倒なこともり、awk を使用するのが便利そうです。

$ echo "3.2*4.3" | bc
13.7
$ echo "scale=2; 3.2*4.3" | bc
13.76
$ echo "sqrt(2.00000)" | bc
1.41421
$ awk 'BEGIN { print 3.2*4.3 }'
13.76
$ awk 'BEGIN { print sqrt(2) }'
1.41421

配列((...), [...])

下記の様にして配列変数を定義・参照することができます。= の前後に空白文字を入れないようにしてください。

FOO1=( AAA BBB CCC )
FOO2=(
    AAA
    BBB
    CCC
)

echo ${FOO1[0]}
echo ${FOO1[1]}
echo ${FOO1[2]}

for i in ${FOO2[@]}
do
    echo $i
done

Unicode文字($'\Uxxxx')

$'\U....' で16進数指定した Unicode 文字を表すことができます。

echo $'\U3042'

シェルオプション(set)

bask の引数もしくは set コマンドで、シェル自体の動作に関するオプションを指定することができます。

/bin/bash -opt			# シェル起動時のオプションで指定
set -o				# シェルオプションの一覧を表示する
set -opt			# setコマンドで有効にする(例:set -a)
set -o option			# setコマンドで有効にする(例:set -o allexport)
set +opt			# setコマンドで無効にする(例:set +a)
set +o option			# setコマンドで無効にする(例:set +o allexport)

例えばシェルスクリプトの中で下記の様にオプションを設定すると、コマンドの実行トレースを表示するようになります。

#!/bin/bash -x
command...

下記の様に set を用いると、オプションを設定した箇所以降でトレースが有効になります。

#!/bin/bash
command...
set -x		# set -o xtrace と同意
command...

指定可能なオプションには下記があります。

# 展開系
allexport(-a)			# シェル変数が設定されたら環境変数に自動的にexportする
braceexpand(-B)			# ブレース展開を有効にする
noglob(-f)			# *や?のファイル名展開を無効にする

# ヒストリ系
history				# ヒストリ機能を有効にする
histexpand(-H)			# !番号によるヒストリの参照を有効にする

# 操作モード系
emacs				# コマンド編集キーをEmacs風にする
vi				# コマンド編集キーをvi風にする
posix				# POSIX互換モードで動作する

# デバッグ系
errexit(-e)			# コマンドがひとつでもエラーになればシェルを終了する
pipefail			# パイプライン中のコマンドがひとつでもエラーになればパイプを終了する
errtrace(-E)			# エラートラップ(trace '...' ERR)を有効にする
functrace(-T)			# 関数の中でもトラップを発生させる
xtrace(-x)			# コマンド実行トレース表示機能を有効にする
verbose(-v)			# シェル実行トレース表示機能を有効にする
noexec(-n)			# 文法チェックのみ行い実行しない

# 安全性
noclobber(-C)			# リダイレクトによるファイルの上書きを禁止する
ignoreeof			# Ctrl-D でログアウトしない
nounset(-u)			# 未定義の変数参照をエラーとする($*,$@は除く)
physical(-P)			# cdコマンド等でシンボリックリンクをたどらない
privileged(-p)			# 特権モード。各種変数や実行ユーザIDを周りから受け取らない
interactive-comments		# コメント(#)を利用可能にする(インタラクティブモードのみ)

# 通知系
monitor(-m)			# バックグラウンドプロセスの終了を通知する
notify(-b)			# バックグラウンドプロセス終了時即座に通知する

# その他
hashall(-h)			# ディレクトリハッシュテーブルをすべて記録する
keyword(-k)			# コマンド引数に指定した代入文も環境変数としてコマンドに渡す
onecmd(-t)			# コマンドをひとつ読み込み、実行してから終了する(詳細不明)
nolog				# 未使用

Copyright (C) 2020-2024 杜甫々
初版:2020年5月24日 最終更新:2024年5月16日
http://www.tohoho-web.com/ex/shell.html