Linux学習 第7章 簡単なシェルスクリプトを書いてみよう

📢 この記事は gemini-3-flash-preview によって翻訳されました

第7章 簡単なシェルスクリプトを書いてみよう

もし何度も繰り返すタスクがあって、そのたびに大量のコマンドを打ち込まないといけないなら、シェルスクリプトを書いて一つのコマンドで全部済ませちゃうのがいいよ。

7.1 シェルスクリプトを理解する

シェルスクリプトは、コマンド、関数、変数、その他シェルで使える機能をひとまとめにしたもの。これらをプレーンテキストファイルに書き込むことで、一つのコマンドとして実行できるようになるんだ。

Windowsのバッチファイル (.bat) みたいなものだね。

7.1.1 シェルスクリプトの実行とデバッグ

シェルスクリプトのいいところは、どんなテキストエディタでも開いて中身を確認できること。逆に欠点は、大きくて複雑なスクリプトだと、コンパイル済みのプログラムより実行が遅くなりがちなことかな。実行方法は基本的に2つあるよ。

  1. スクリプト名を bash の引数として渡す。例:bash myscript

  2. シェルスクリプトの1行目にインタプリタの名前 (#!/bin/bash) を書いて、そのファイルに実行権限を付けてから (chmod +x myscript)、コマンドラインでスクリプトのパスを入力して実行する。例:(./myscript.sh)

実行するとき、スクリプト名の後に続くのがコマンドライン引数だよ。

コメントは # を使う。

スクリプトの最初に set -x を入れるか、$ bash -x myscript と打つことで、実行中のコマンドを表示しながらデバッグできるよ。

7.1.2 シェル変数を理解する

シェル変数の名前はは大文字と小文字を区別するから注意してね。定義するときは等号 (=) の前後にスペースを入れないのがルールだよ。例:

NAME=value

変数にはテキスト、数字、アンダースコアなどの定数を代入できる。

コマンドの結果を変数に入れることもできるよ。例:MYDATE=$(date) と書くと、date コマンドの出力が変数 MYDATE に入るんだ。

こうすると、変数 MYDATE を使うたびに一回 date コマンドが動いて、その結果が MYDATE に代入される。代入時の実行結果が欲しいときは、コマンドを引用符 ' で囲めばOK。


特殊なシェル文字:ドル記号 ($)、引用符 (')、アスタリスク (*)、感嘆符 (!) など。

もしコマンドラインで $HOME という文字列をそのまま表示したいなら、$ をエスケープする必要がある。echo '$HOME'echo \$HOME を使えばいいよ。つまり:

  • シェルに特定の文字をそのまま解釈させたいときは、バックスラッシュ \ を使う。

  • 文字の集まりをそのまま解釈させたいときは、シングルクォート (') で囲む。

  • 一部の文字だけそのまま解釈させたいときは、ダブルクォート (") で囲む。この場合、ドル記号 ($)、引用符 (')、感嘆符 (!) は解釈されるけど、アスタリスク (*) などの他の文字はそのまま扱われるよ。

変数に値を代入するときはそのまま名前を使うけど、変数を参照(値を取得)するときは頭にドル記号 ($) をつけるのを忘れないで。

例:ある変数の値を新しい変数に入れる:newVar="$oldVar"


  1. 特殊なシェル位置パラメータ

位置パラメータ、つまり コマンドライン引数 は、$0$1$2$n という名前になっているよ。

$0 は呼び出されたスクリプト自身の名前で、それ以外はコマンドラインから渡された引数の値が入るんだ。例えば:

1
2
3
4
5
6
#!/bin/bash

echo "最初の引数は $1 、2番目の引数は $2 "
echo "このスクリプトの名前は $0 "
echo "全部で $# 個の引数が渡されたよ"
echo "すべての引数はこれ:$@ "

./myscript hello bye というコマンドを実行すると、結果はこうなる:

1
2
3
4
最初の引数は hello 、2番目の引数は bye
このスクリプトの名前は /home/yexca/tmp/myscript
全部で 2 個の引数が渡されたよ
すべての引数はこれ:hello bye

もう一つ面白いのが $? で、これは最後に実行されたコマンドの終了ステータスを受け取る。普通、正常に終われば 0 が返ってくるよ。


  1. 引数を読み取る

read コマンドを使うと、ユーザーの入力を受け取ることができる。

1
2
3
4
#!/bin/bash

read -p "2つの単語を入力してね:" var1 var2
echo "今、 $var1$var2 が入力されたよ"

  1. Bash でのパラメータ展開

変数の値が欲しいときは頭にドル記号 ($) をつけて $var と書くけど、これは実は ${var} の短縮形なんだ。

Bash にはパラメータの値をいろいろな方法で展開するルールがある。よく使うものを ${var} を例に挙げておくね。

説明
${var:-value}もし変数が未設定か空なら、value として展開する
${var#pattern}var の値の 前方 から、pattern と 最短 で一致する部分を削る
${var##pattern}var の値の 前方 から、pattern と 最長 で一致する部分を削る
${var%pattern}var の値の 末尾 から、pattern と 最短 で一致する部分を削る
${var%%pattern}var の値の 末尾 から、pattern と 最長 で一致する部分を削る

これを使うと、こんな便利なことができるよ:

1
2
3
4
5
6
7
8
9
myFileName=/home/yexca/myfile.txt
# file は myfile.txt になる
file=${myFileName##*/}
# dir は /home/yexca になる
dir=${myFileName%/*}
# name は myfile になる
name=${file%.*}
# extension は txt になる
extension=${file##*.}

7.1.3 シェルスクリプトで計算をする

Bash の変数は型が決まっていない(非型指定)。declare で明示しない限り、変数は文字列として扱われるんだ。でも計算するときは自動的に整数に変換されるから、代入時に型を気にする必要はないよ。

内蔵の let コマンド、外部の expr コマンド、あるいは bc コマンドを使って整数の計算ができる。

例:let result=$num/16let num=$RANDOM

インクリメント演算子の i++++i も使えるよ。

let コマンドは、演算子と数字の間にスペースを入れちゃダメ。

expr コマンドは逆に、演算子と数字の間にスペースが必要。

bc コマンドはスペースを気にしなくていいし、浮動小数点(小数)の計算もできるよ。

7.1.4 シェルスクリプトでプログラミング構造を使う

  1. “if…then” 文
1
2
3
if [ $var -eq 1 ]; then
    echo "変数は 1 だよ"
fi

数字を比べるなら -eq がいいけど、文字列を比べるなら等号 (=) のほうが使いやすいよ。

1
2
3
if [ $str = "hello" ]; then
    echo "hello"
fi

不等号 != もある。

elif を使えば選択肢を増やせるし、else で「それ以外」の場合を指定できる。

1
2
3
4
5
6
7
8
str="$HOME"
if [ -f "$str" ]; then
    echo "$str は普通のファイルだね"
elif [ -d "$str" ]; then
    echo "$str はディレクトリだね"
else
    echo "これは何かな???"
fi

よく使うテスト条件のリストだよ:

演算子テストの内容
-a fileファイルが存在するか(-e と同じ)
-b fileブロックデバイスファイルか
-c fileキャラクタデバイスファイルか
-d fileディレクトリか
-e fileファイルが存在するか(-a と同じ)
-f file普通のファイルか(ディレクトリやデバイスファイルじゃないか)
-g fileSGID ビットがセットされているか
-h fileシンボリックリンクか(-L と同じ)
-k fileスティッキービットがセットされているか
-L fileシンボリックリンクか(-h と同じ)
-n string文字列の長さが 0 より大きいか
-O file自分がそのファイルの所有者か
-p file名前付きパイプか
-r file読み取り可能か
-s fileファイルが存在して、サイズが 0 より大きいか
-S fileソケットファイルか
-t fileターミナルに接続されたディスクリプタか
-u fileSUID ビットがセットされているか
-w file書き込み可能か
-x file実行可能か
-z string文字列の長さが 0 か

こっちは2つの変数を比べるとき:

演算子テストの内容
expr1 -a expr2両方の式が真か
expr1 -o expr2どちらかの式が真か
file1 -nt file2ファイル1のほうが新しいか(タイムスタンプで比較)
file1 -ot file2ファイル1のほうが古いか(タイムスタンプで比較)
file1 -ef file22つのファイルが同じ実体(ハードリンクかシンボリックリンク)か
var1 = var22つの変数が等しいか
var1 -eq var22つの変数が等しいか
var1 -ge var2var1 が var2 以上か
var1 -gt var2var1 が var2 より大きいか
var1 -le var2var1 が var2 以下か
var1 -lt var2var1 が var2 より小さいか
var1 != var22つの変数が等しくないか
var1 -ne var22つの変数が等しくないか

さらに、テスト演算子を &&(AND)や ||(OR)と組み合わせて、C言語の三項演算子みたいに書くこともできるよ。

C言語:a>b ? a : b

Shell:[ $a -gt $b ] && echo $a || echo $b

単独でも使える。例えば: [ $a -eq $b ] && echo $a は「a と b が等しければ、a を表示する」。

[ -d "$dirName" ] || mkdir "$dirName" は「$dirName というディレクトリがなければ、mkdir で作る」。


  1. case コマンド

C言語の switch 文みたいなもので、条件分岐に使うよ。基本の形はこんな感じ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
case "VAR" in
    Result1)
        body
        ;;
    Result2 | Result3)
        body
        ;;
    *)
        body
        ;;
esac
  1. for…do ループ

for ループはリストの中身を順番に処理するときに使うよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for VAR in LIST
do
    body
done

# またはこう書く

for VAR in LIST ; do
    body
done 

例えば:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
for num in 0 1 2 3 4
do
    echo "数字は $num だよ"
done

# コマンドの出力をリストにすることもできる

for file in $(ls /bin) ; do
    echo $file
done
  1. while…do と until…do ループ

構造はこんな感じ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# while...do:条件が真の間繰り返す
while condition
do
    body
done

# until...do:条件が真になるまで繰り返す
until condition
do
    body
done

7.1.5 便利なテキスト操作プログラムを使う

よく使われるのは grep, cut, tr, awk, sed。ほとんどが標準入力と標準出力を使うように設計されているよ。

  1. 一般正規表現アナライザー (grep)

ファイルやテキストの中から特定のパターンを探す方法。強力な検索ツールとして使えるよ。

形式:grep 探したい内容 入力元

詳しくは man grep で確認してみて。

  1. テキストの切り出し (cut)

cut コマンドはテキストやファイルから特定のフィールドを抜き出すことができる。例えば:

grep /home /etc/passwd | cut -d':' -f6 -

まず grep で /etc/passwd から /home を含む行を探して、それを cut に渡す。cut: を区切り文字として使って、6番目のセグメント (-f6) を取り出しているよ。

  1. 文字の変換や削除 (tr)

tr は文字ベースのコンバーターで、文字を置換したり削除したりできる。

1
2
3
4
5
6
7
8
9
# 大文字を小文字に変換
FOO="AbcDEF"
echo $FOO | tr [A-Z] [a-z]

# ファイル名の中のスペースをアンダースコアに変換する例
for file in *; do
    f=$(echo "$file" | tr ' ' '_')
    [ "$file" = "$f" ] || mv -i -- "$file" "$f"
done
  1. ストリームエディタ (sed)

sed は簡単なスクリプトエディタ。特定のパターンに一致する行を消したり、文字を置換したりといった編集ができる。

かなり奥が深いから、オンラインドキュメントで調べてみてね。

7.1.6 簡単なシェルスクリプトを使ってみる

電話リストの例だよ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/bin/bash
# (@)/ph
# とてもシンプルな電話リスト
# "ph new 名前 番号" でリストに追加
# "ph 名前" で電話番号を検索できるよ

PHONELIST=~/.phonelist.txt

# 引数がない場合はエラーメッセージを出す
if [ $# -lt 1 ]; then
    echo "誰の番号が知りたいの?"
    exit 1
fi

# 新しい番号を追加する場合
if [ "$1" = "new" ]; then
    shift
    echo $* >> $PHONELIST
    echo "$* をデータベースに追加したよ"
    exit 0
fi

# 検索。ファイルが空じゃないかチェック
if [ ! -s $PHONELIST ]; then
    echo "まだ電話リストに誰もいないよ!"
    exit 1
else
    grep -i -q "$*" $PHONELIST    # 静かに検索
    if [ $? -ne 0 ]; then    # 見つかったかな?
        echo "残念、その名前はリストになかったよ"
        exit 1
    else
        grep -i "$*" $PHONELIST
    fi
fi
exit 0

7.2 小結

シェルスクリプトを書けるようになると、よくあるシステム管理タスクを自動化できるようになって、すごく便利になるよ。

Visits Since 2025-02-28

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。