🐚

楽しいシェルスクリプト入門

2023/12/24に公開

今年は、何度かシェルスクリプトを読んだり、書いたりする機会がありました。
今まであまり使った経験はなかったのですが、個人的に今年印象に残ったことの1つですね!

...ということで!
折角なので、シェルスクリプトの書き方など整理しておこうと思います!!

シェルスクリプトとは?

まず、シェルスクリプトの概要を簡単にまとめます。
シェルスクリプトとは、UNIX/Linuxコマンドを順番に実行させるファイルです。
あらかじめ記述した処理を順番に実行していきます。
自動化したい時などに役立ちます。

実行方法

シェルスクリプトを格納しているディレクトリで、以下のように実行します。

$ ./シェルスクリプトのファイル名

権限の変更

最初に実行する時には、権限を変更しておく必要があります。
実行権限を与えるので、+xを指定します。

$ chmod +x シェルスクリプトのファイル名

chmodは、ファイルやディレクトリのアクセス権限の変更ができるコマンドです。
+は「後に記述した権限を付加する」、xは「実行権限」を意味します。
つまり、シェルスクリプトファイルに実行権限を与えています。

よく使う基本的なコマンドを使う

まずは、皆さんがよく知る基本的なコマンドを実行するシェルスクリプトを実装してみます。
使用するコマンドは以下です。

  • mkdir
  • cd
  • touch
  • ls

ディレクトリの作成・移動や、ファイルの作成をやってみます。
作成したシェルスクリプトは以下です。

basic.sh
#!bin/bash
# ディレクトリの作成
mkdir "sampleDir"

# ディレクトリに移動する
cd "sampleDir"

# ファイルの作成
touch "sample.txt"

# ディレクトリの中身を確認する
ls

お馴染みのコマンドを使っています。
結果として、以下のように出力されます。

もうちょっと使ってみる

基本的なコマンドだけでなく、もう少しできることをやってみます。

変数

シェルスクリプトでは、変数を扱うことができます。

variables.sh
#!bin/bash
x="変数です"
echo $x

xという変数に変数ですという文字列を代入しています。
echoコマンドで変数xの値を出力します。
実行すると、以下のように出力されます。

変数xに代入した変数ですという文字列が出力できています。

変数と文字列を連結することも可能です。

varAndStr.sh
#!bin/bash
x="World"

echo "Hello, ${x}!"

変数xに、Worldという文字列を代入しています。
echoコマンドで、文字列Hello,と変数xを連結させて出力しようとしています。

実行すると、以下のように出力されます。

無事、Hello, World!と出力することができています。

配列

配列を扱うことも可能です。
配列で要素を入れる方法としては、次の2つがあります。

  • 1つずつ要素を入れる方法
  • 一気に要素を入れる方法

まず、配列名[インデックス番号]で、1つずつ要素を入れることができます。

array.sh
#!bin/bash
array[0]="い"
array[1]="ろ"
array[2]="は"
echo ${array[@]}

配列には、文字列が格納されています。
実行すると、以下のように出力されます。

そして、()で囲うことで、一気に要素を入れることができます。

array.sh
#!bin/bash
arrayAll=("あ" "い" "う" "え" "お")
echo ${arrayAll[@]}

配列には、文字列が格納されています。
実行すると、以下のように出力されます。

繰り返し

せっかく配列の作り方を学んだので、繰り返し処理を実装してみましょう。
今回は、for文を使います。
for文を使って、配列の要素を1つずつ出力しようと思います。

まずは、配列を作ります。

loop.sh
array=(123 456 789)

配列arrayの中に、数値123456789の3つの要素が入っています。

for文を実装していきます。

loop.sh
array=(123 456 789)

for i in "${array[@]}";
do
    echo "${i}"
done

変数iに配列arrayの要素を入れて、do~doneの中に書かれた処理を実行していきます。
今回は、要素の出力なので、echoコマンドで変数iを出力するよう実装しています。
実行すると、以下のように出力されます。

配列arrayに格納された要素が1つずつ出力されていることが確認できました!

さらに、seqコマンドを利用して、連番をつけて出力するなども可能です。
次のコードでは、1〜5の連番を使用して、文字列を出力します。

loop.sh
#!bin/bash
for n in `seq 1 5`
do
    echo "${n}番目です。"
done

実行すると、以下のように出力されます。

連番が出力できると、ファイル名の生成などで利用することができます!

条件分岐

続いては、条件分岐です。
連番を扱ったので、FizzBuzzを実装してみます。
3の倍数の場合はFizz、5の倍数の場合はBuzz、3の倍数かつ5の倍数(つまり15の倍数)の場合はFizzBuzzを出力します。
それ以外の場合は、数字をそのまま出力します。
今回は、表示の都合上、1〜20の範囲で行います。

fizzBuzz.sh
#!bin/bash
for n in `seq 1 20`
do
    if [ $((n % 15)) -eq 0 ]; then
        echo "FizzBuzz"
    elif [ $((n % 5)) -eq 0 ]; then
        echo "Buzz"
    elif [ $((n % 3)) -eq 0 ]; then
        echo "Fizz"
    else
        echo $n
    fi
done

$((n % 3))$((n % 5))で余りを求めています。
-eqtestコマンドというものです。整数同士の比較で用いられ、「等しい」ときにtrueを返します。
-eq以外にもいくつか種類がありますが、紹介は端折らせてください。
いつか思い出したら別で記事にするかも...?

また、「3の倍数かつ5の倍数」は、AND演算子(&&)を使って、以下のように表現することもできます。

fizzBuzz.sh
if [ $((n % 3)) -eq 0 ] && [ $((n % 5)) -eq 0 ]; then
    echo "FizzBuzz"
elif (略)

実行すると、以下のように出力されます。

注意点としては、条件を記述する中括弧([])の中は両サイドスペースを空けないといけないことです。
以下のように記述してしまうと、認識してくれないです。

if [$((n % 15)) -eq 0]; then
    echo "FizzBuzz"
elif (略)

ユーザー入力

続いて、ユーザー入力をやってみます。
皆さんもFWのインストールなどで時々遭遇するかなと思いますが、ユーザーに入力を求め、入力内容に応じた処理を行うものです。

userInput.sh
#!bin/bash
echo "お名前を入力して下さい:"
read input
echo "こんにちは、${input}さん"

今回は、名前の入力を促し、入力された名前を含めた文字列を出力するように実装しています。
「こんにちは、(入力された名前)さん」と出力される想定です。

実行するとまず、以下のように出力されます。

名前の入力を促されます。
そして、名前を入力すると、以下のように出力されます。

「こんにちは、あずにゃんさん」と出力され、入力した名前が入っていることがわかります。

ユーザー選択

ユーザーに入力してもらうだけでなく、選択肢から選択してもらうこともできます。

userSelect.sh
#!bin/bash
echo "好きなスイーツの番号を選んでください。"
select word in "プリン" "シュトーレン" "ジンジャークッキー"
do
    echo "選択したのは、${word}です。"
    break
done

ユーザーに好きなスイーツの番号を選んでもらいます。本日は12/24なので、クリスマスっぽいものも混ぜています🎄
「選択したのは、(選択してもらった番号のスイーツ)です。」と出力される想定です。

selectコマンドを使うことで、番号付きの選択肢が表示され、選択することができるようになります。
実行するとまず、以下のように出力されます。

番号を選ぶよう促されます。
3種類の中から、好きなスイーツの番号を入力すると、以下のように出力されます。

私は「3)ジンジャークッキー」を選んだので、ジンジャークッキーが含まれた状態で、文字列が出力されました!

【応用編】色々組み合わせてみる!

応用編として、これまでご紹介してきた実装を組み合わせたシェルスクリプトを実装してみます。
具体的には、主に以下の実装を組み合わせます。

  • ディレクトリの作成
  • ファイルの作成
  • ユーザー選択
  • 条件分岐
  • 繰り返し(配列)

また、新たに取り入れたものとして、ファイルへの出力があります。
作成したファイル(insert.sql)にテキストを出力します。
ファイルへの出力には、リダイレクション>>)を利用しています。
標準出力としてターミナルにそのまま結果を表示するものを、ファイルへ出力します。

実現することとしては、複数のSQL文(INSERT文)を生成することです。
INSERT文1つでカンマ区切りで複数行追加はできますが、実装を簡略化するため、複数のINSERT文を生成しています。

以下のような流れを想定しています。

  1. ディレクトリ(sql)を作成
  2. ファイル(insert.sql)を作成
  3. ユーザーに好きな料理のジャンルを選択してもらう
  4. ユーザーの選択に応じて、テーブル名とメニューの配列を変数に代入
  5. メニューの配列でループを回して、メニューの数に応じたINSERT文を生成する
  6. 作成したファイル(insert.sql)にINSERT文を出力する

※前提条件としては、各ジャンルのテーブル自体はすでにCREATE済みです。
※面白くしたいという気持ちも若干あったので、イマイチな実装かもしれないです。SQLナニモワカラナイ!笑

以下、完成したシェルスクリプトです。

advance.sh
#!bin/bash
dirName="sql"
sqlFileName="insert.sql"

mkdir $dirName
cd $dirName
touch $sqlFileName

echo "作りたいデータを選んでください"
select type in "洋食" "中華" "和食"
do
    echo "${type}が選択されました。"

    if [ $type = "洋食" ]; then
        tableName="westernDishes"
        menuList=("ハンバーグ" "パスタ" "オムライス")
    elif [ $type = "中華" ]; then
        tableName="chineseDishes"
        menuList=("焼売" "餃子" "チャーハン" "担々麺" "麻婆豆腐")
    else
        tableName="japaneseDishes"
        menuList=("焼き魚" "味噌汁" "おひたし" "肉じゃが")
    fi

    for i in ${!menuList[@]}
    do
        id=$((i + 1))
        name=${menuList[$i]}
        insertText="INSERT INTO $tableName VALUES (${id}, ${name});"

        echo ${insertText} >> ${sqlFileName}
    done
    break
done

上記シェルスクリプトを実行すると、以下のようになります。
標準出力としては、選択された番号とテキストが表示されているのみです。

ディレクトリを確認すると、sqlディレクトリがきちんとできているようです。

sqlディレクトリの中にはinsert.sqlが生成されており、その中にはきちんとSQL文が出力されていました!!

idを生成するところは、uuidgenというコマンドでuuidを生成することもできるので、そちらでも良いかもしれないですね!

おわりに

お読みくださり、ありがとうございます。

今年は、tmuxを使ったり、シェルスクリプトを読んで起動がどのように行われているのか確認したり、何かとシェルスクリプトを読んだり書いたりする機会がありました。応用編で挙げたような、INSERT文を大量に生成したり、画像のコピーを大量に生成するといった処理も実際にお仕事で書いたりしました。

他の言語も同様ですが、シェルスクリプトも書いて動かして、書いた通りに動くと、とても達成感がありました👏
あと、やっぱり便利ですね!!

また、今年シェルスクリプトを書く経験をして、「開発環境を作る・よりよくする業務って、面白いな!」と感じました。
今後も、シェルスクリプトと仲良くしていこうと思います!!

大変なことも多々ありましたが、とても良い経験ができた1年だったかなと思います👍

もう少しシェルとシェルスクリプトについてなど学習・整理してまとめたかったのですが、それはまたいつか。(覚えていたら。)

参考資料

Linux豆知識 035 シェルスクリプト
【 chmod 】 ファイルやディレクトリのアクセス権を変更する
【 select 】 ループ制御構造を作る
リダイレクションで出力を追記するには
標準入出力とリダイレクションを使いこなす

参考文献

Software Design 2022年6月号 シェルの基本大全

GitHubで編集を提案

Discussion