シェルスクリプトについて基本事項まとめ

<h3>シェルスクリプトとは</h3>
シェルのコマンドを記述しておくファイルのこと。
実行したい一連のコマンドをまとめて一つのファイルに書いておいてこれをシェルに読み込ませることで、毎回コマンドを手打ちする必要なく、処理を実行することができる。
条件分岐や繰り返し処理も行えるため、一種のプログラミング言語であるとも言える。

↓こういうやつ

```hello.sh
#!/bin/bash

echo "Hello World !"
```

#### シェルスクリプトを使うメリット ####
- 一度シェルスクリプトを作ってしまえば、後で同じ処理を実行する際に再利用することができる。 
- 他の人にもシェルスクリプトを配布して使ってもらうことができる。
- コマンドの打ち間違いによるヒューマンエラーを事前に防止できる。

---

#### どのシェルを使用するか？ 

シェルが変わればシェルスクリプトの書き方も変わるため、シェルの選択は重要であるとも言える。
現在は、shもしくはbashを使うことが多いようである。
shは長い歴史を持つため古いとも言えるが、過去多くのシェルスクリプトがsh向けに書かれており互換性の面では優れているとも言える。
bashはshの問題点を改善した機能が多く備わっているため、近年ではshよりもむしろbashを使おうという動きが活発になっているようだ。
積極的にbashを使うことが推奨される。

#### シバン　####

上記に登場した

```
#!/bin/bash
```

この部分を指してシバン、もしくはシェバンという。
どのシェルを使用するかの宣言と考えて差し支えない。

---

#### 実行権限
ファイルを実行するにはあらかじめシェルスクリプトに実行権限を付与しておく必要がある。
上記の例で言えば

```
$ chmod +x hello.sh
```
このコマンドをコマンドラインから実行することで作成したファイルhello.shに実行権限が付与され、自由に使うことができる。
実行方法は複数あって、./を利用するか、

```
$ /bin/bash　./hello.sh
```
とするか、もしくはsourceコマンドを使って

```
$ source ./hello.sh
```
と書いて実行する手段がある。
sourceコマンドを利用する場合は、ファイルに実行権限が必要ない。
また、現在設定されているシェル環境の影響を受けることにも留意したい。

#### シェルスクリプトの配置

上記のコマンドで./をつけて実行しているが、これをつけないと

```
-bash: hello.sh: コマンドが見つかりません
```
というエラーが表示される。
相対パスもしくは絶対パスで実行すればエラーにはならないが、絶対パスで実行する場合

```
$ /home/user/work/hello.sh
```
といった具合に非常に手間なコマンドをタイプする必要があるため、./をつけて相対パスで実行する。
多くの場合では、シェルスクリプトを配置するディレクトリを作成する。

```
$ mkdir ~/bin
$ mv hello.sh ~/bin
```

####シェルスクリプトの行
シェルスクリプトでは、複数のコマンドを；で区切ることによって１行にまとめて書くことができる。
また、実行ファイル内で処理が長い一行になってしまった場合、読みやすくする意味で行末に\を書くことで改行することができる。

；の例

```
#!/bin/bash

echo "root directory"; /;ls -l
(root directoryという文字列を表示したのち、カレントディレクトリのファイルを一覧する)
```
改行の例

```
#!/bin/bash
echo　\
"root directory"
```

(Macでバックスラッシュを入力するには、option + ¥キー)

####シェルスクリプトのコメント

シェルスクリプトでは、＃を書くとその行の終わりまでがコメントになる。

コメントの例

```
#!/bin/bash

echo "hello, world"

# ルートディレクトリに移動する
cd /

ls -l #ファイル一覧を表示する

#echo "hello, world"
(コメントアウトされているため、hello worldは表示されない)
```

####変数

シェルスクリプトでも値を書く仁王する変数を定義することができる。
これを「シェル変数」と呼ぶ。
変数の値を参照する際は、頭に＄をつける。

```
#!bin/bash

appdir=/home/usr/myapp

echo $appdir
```

```
実行結果
/home/usr/myapp
```
※変数を定義する際に＄を頭につけてしまうとエラーになる。また、＝の後にスペースを入れない点にも留意。

####クォーティング

bashでは基本的に単語はスペースによって区切られる。
文字列を指定する際に使うのがクォートで''(シングルクォート)もしくは""(ダブルクォート)を使って表現する。

クォートの例

```
$ cat 'my file'
```

クォートの中で＄を使う場合注意が必要で、シングルクォートの中では＄は文字として認識され、ダブルクォートの中では変数として認識される。

＄の例

```quote.sh
#!/bin/bash
country=Japan
echo 'I came from $country'
echo "I came from $country"
```

実行結果は以下の通り

```
$ ./quote.sh
I came from $country
I came from Japan
```

####コマンド置換

シェルスクリプトを書いていて、コマンドの出力結果をシェルスクリプト内で使いたい際に必要になる。
$()という形式で使用する。

コマンド置換の例

```
#!/bin/bash

filename=$(date '+%Y-%m-%d')
touch "$filename"
```

シェル変数filenameに本日の日付を代入するという処理。
コマンド置換はダブルクォートの中でも有効である。

```
$ echo "Today is $(date '+%Y-%m-%d')."

Today is 2021-12-17.
```

シングルクォートの代わりにバッククォートを使用することもできる。

####位置パラメータ

シェルスクリプトからコマンドライン引数を扱う場合は”位置パラメータ”と呼ばれる変数を利用する。
例えば下記で言うと、./parameters.shは$0、aaaは$1、bbbは$2、cccは$3を表す。

```
$ ./parameters.sh aaa bbb ccc
```

テストようのシェルスクリプトを作成してみる。

```test.sh

#!/bin/bash

echo "\$0 = $0"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
```

コマンドラインからコマンドライン引数を与えて実行してみる。

```
$ ./test.sh aaa bbb ccc

$0 = ./test.sh
$1 = aaa
$2 = bbb
$3 = ccc
```

となる。
引数の個数を表示する場合下記のように$#と言う変数を使う。


```
$ ./test.sh aaa bbb ccc

$0 = ./test.sh
$1 = aaa
$2 = bbb
$3 = ccc
$# = 3
```

####制御構造

bashは他のプログラミング言語同様条件分岐、繰り返し処理等を行うことができる。
シェルスクリプトをプログラミング言語とみなす見方もあるようだ。(SESの弊社では職務経歴書を少しでもかっこよくするため、使える技術一覧にシェルスクリプトとわざわざ記載させられる。笑)

#####if文

条件を評価して、審議によって処理を分岐する。

```if-bin.sh
#!/bin/bash

if [ "$1" = "bin" ]; then
  echo "OK"
else
  echo "NG"
fi
```

(個人的にfiてなんやねん!って思いました。else ifはシェルスクリプトではelif)

####testコマンドと演算子



####for文

forはスペースやタブで区切られた単語のリストに対して繰り返し処理を行う構文である。

```for.sh

#!/bin/bash

for name　in aaa bbb ccc
do
  echo $name
done
```

以下実行例

```
$ ./for.sh
aaa
bbb
ccc
```

固定の文字列だけでなく、パス名を指定して展開することもできる。

```
#!/bin/bash

for filename in *.html
do
  head -n 1 "$filename"
done
```

seqというコマンドを利用してコマンド置換を記述する。

```
$ seq 1 5
1
2
3
4
5
```

####case文

caseは指定された文字列がパターンにマッチするかどうかを判断し、マッチしたパターンに対応した処理を行う。

```
#!/bin/bash

case "$1" in
  *.txt)
  less "$1"
  ;;
*.sh)
  vim "$1"
  ;;
*)
  echo "not supported file : $1"
  ;;
esac
```

また、|を利用して書く場合もある。
$1がstartもしくはstopならばOK,それ以外ではNGを出力する。

```
#!/bin/bash

case "$1" in
  start|stop)
  echo "OK"
  ;;
  *)
echo "NG"
  ;;
esac
```

####while文

whileは指定した条件が死んである限り繰り返し処理を行う。
testコマンドによるwhileループ（while.sh）

```while.sh
#!/bin/bash

i=1
while [ "$1" -le 10 ]
do
  echo "$1"
  i=$((i + 2)) #これは算術展開式と呼ばれる。
done
```

実行結果は以下の通りである。

```
$ ./while.sh
1
3
5
7
9
```

####シェル関数

同じ処理を何度も使う場合、関数に格納して使いまわすことができる。

```homesize.sh
#!/bin/bash

homesize  #まだ定義されていないためエラーになる

homesize()
{
date
du -h ~| tail -n 1
}
```

シェル関数の中で位置パラメータを使うこともできる。

```func-parameter.sh
#!/bin/bash

print_parameters ()
{
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
echo
echo "$# arguments"
echo "script name = $0"
}

print_parameters aaa bbb ccc
```

以下実行例。
シェル関数内では、位置パラメーターが関数の引数に置き換わっていることがわかる。

```
./func_parameters.sh
$1 = aaa
$2 = bbb
$3 = ccc
$4 =

3 arguments
script name = ./func_parameters.sh
```
シェル関数の終了ステータス

```
checkparam ()
{
if [ -z "$1" ]; then
  return 1
fi
ls "$1"
}
```
---

####シェルスクリプトの欠点

シェルスクリプトでは変数に型を定義できない他、オブジェクト指向プログラミングも出来ないため大規模開発に不向き出ると言うことが言える。
また、多くの開発言語が統合開発環境（IDE）によってサポート機能の恩恵を受けられる中、シェルスクリプトにはそれがないことから、開発には不向きである。
１行ずつコマンドを実行していくため、動作がどうしても遅くなってしまう点にも注意が必要である。



####実際に書いてみる

実用的なシェルスクリプトを作るに辺り、まずは簡単な実例をもとに解説をしていく。ここでは、日記を書くためのシェルスクリプトを作成する。

####日記を書くシェルスクリプト

まずはこれから作成していく日記ファイルにその日の日付をつけるスクリプトを書く。

```diary.sh
#!/bin/bash

vim$(date'+%Y%m%d').txt
```

次に日記データを保存していくディレクトリを作成し、日記データを作成した場合はこのディレクトリに保存していきますよというスクリプトを書いていく。

```
#!/bin/bash　　

#日記データの保存ディレクトリ
directory="${HOME}/diary"

#データ保存ディレクトリがなければ作成する

if[!d"$directory"];then
　　mkdir"$directory"
fi
　　vim"${directory}/$(date'+%Y%m%d').txt"
```

次に日付を判定して、その日最初の日記であった場合タイトルに日付を代入するスクリプトを書いていく。

```
#!/bin/bash

#日記データの保存ディレクトリ

directory="${HOME}/diary"

#データ保存ディレクトリがなければ作成する

if[!d"$directory"];then
  mkdir"$directory"
fi

#日記ファイルパスの組み立て
diaryfile="${directory}/$(date'+%Y%m%d').txt"

#日記ファイルがなければ（今日はじめて書くならば）、先頭に日付を挿入if[!e"$diaryfile"];then
  date'+%Y/%m/%d'>"$diaryfile"
fi

vim"$diaryfile"
```

このように単純作業の自動化には、シェルスクリプトは非常に有効であると言える。

####ファイル一覧を表示するシェルスクリプト

```tree.sh
#!/bin/bash

list_recursive()
{
local filepath=$1

echo "$filepath"
if [-d" $filepath" ];then
#ディレクトリである場合は、その中に含まれるファイルや
#ディレクトリを一覧表示する

fi
}

list_recursive"$1"
```

####検索コマンドを作ってみる

```findgrep.sh
#!/bin/bash

pattern=$1

find . -type f | xargs grep "$pattern"
```

###まとめ

実は実務でシェルスクリプトを扱ったこともあり、それなりに自信がある中で学習した。
https://qiita.com/KentaroYoshizumi/items/13400db22ddf1547cb33
蓋を開けてみると知らないことはまだまだたくさんあり、改めて一生勉強の世界なのだと気が引き締まったし、再度使用する際にはより複雑なことに挑戦できるなと感じた。
シェルスクリプトで実現できることを考えると、もはやプログラミング言語の一つであり、覚えておいても決して損がないなと素直に思う。
しかしRubyやPHPと比較すると、アプリケーションを作れない都合上どうしても機能面で見劣りがしてしまうし、第2第3の使用言語というよりは、飛び道具寄りな扱いになってしまう。
今後ともどんどん積極的に使用して、自在に扱えるようになれたらと思う次第でありました。