勉強メモ3_シェルプログラミングについて記事を見ながら学習

##★0　はじめに

```shell
　世の中には、シェルプログラミングでの設計、開発、テストの案件が
　存在しているため、ちょっと記事を見ながら、軽くLinuxで動かしながら
　学んでみました。
```

##★1　シェルプログラミングについて

```shell
　・シェルスクリプトの場合は拡張子をつけないことが多い
　・シェルはコマンドを実行するインタープリタである
　・psコマンドを実行すると、どのシェルが使われているのかがわかる(CMDを見ると、bashを使っていることがわかる)
　  $ ps
     PID TTY          TIME CMD
     62386 pts/2    00:00:00 bash
     63249 pts/2    00:00:00 ps
```

・シェルの処理の実際のイメージは下記みたいな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730323/dd274401-bfcd-fa91-55a6-7413beab0481.png)

・シェルの種類
シェルにはいくつか種 類がある。Linuxの種類によって利用できるシェルは異なるが、代表的なものは以下のものがある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/730323/8858e29f-172d-3feb-1233-5e8728e3e8d6.png)
※システムで利用可能なシェルは、/etc/shellsファイルで確認できる

・メタキャラクター
シェルにとって特別な意味を持つ文字をメタキャラクターという。ファイル名の指定などに利用
できる主なメタキャラクターは次の表である

| メタキャラクター  | 説明  | 
|---|---|
| *  | 0文字以上の任意の文字列  |
| ?  | 任意の1文字  |
| []  | []内の任意の1文字  |
| \  | メタキャラクターの打ち消し  |





##★2　見た記事

```shell
　とほほ
　http://www.tohoho-web.com/ex/shell.html#options
　QIITA シェルスクリプトBash入門
　https://qiita.com/ebisennet/items/573618ab827ce1660b0e
　シェルスクリプト入門　書き方のまとめ
　http://motw.mods.jp/shellscript/tutorial.html
```

##★4　VMでCentOSとかいれなくても以下のURLでオンラインでできるっぽい

```shell
https://paiza.io/projects/CUEQQn-obO7rcNTuKdSO7A
```

##★5　比較演算子系の英語の略称

| 演算子  | 略称  | 英語  |
|---|---|---|
| =  | -eq  | equal to  |
| !=  | -ne  | not equal to  |
| <  | -lt  | less than  |
| <=  | -le  | less than or equal to  |
| >  | -gt  | greater than  |
| >=  | -ge  | greater than or equal to  |

##★6 コメント複数

```shell
#!/bin/sh

#　1行コメントの場合↓↓↓↓
echo "Hello World!"

#　複数コメント↓↓↓↓
<<COMMENT
echo "Hello world!"
echo "Hello world!"
echo "Hello world!"
COMMENT

```

##★6.1 プログラムを 1 行目(#!/bin/shに「ついて)

```shell
・#!/bin/shを「シェバン」もしくは「シバン」という
　これがあったら次に続くプログラムで実行しなさいよ、という意味になる

・少し特殊な書き方ですが #! 
 としたあとに今回はシェルである bash を利用

・bash がどこにあるかですが、 which bash で調べることができるので 
 /bin/bash をそのままコピーしてあげれば OK 

$ which bash の実施結果
/bin/bash
```

##★6.2 シェルスクリプトの実行について

```shell
・ls -l で見て実行権限がない場合は、以下のコマンドを実行して実行権限をつける
$ chmod +x [スクリプト名]
```


##★7 ユーザーからのキーボード入力を受付

```shell:aa.sh
#!/bin/sh

read -p "名前を入力してください。: " name
echo "こんにちは、 $name.さん"
```
上のaa.shを動かすと、下記になる

```shell
$ ./aa.sh
名前を入力してください。: rinchome
こんにちは、 rinchome.さん
```

##★8 変数の事のメモ

```shell:bb.sh(文字列の変数)
string="Hello world!"
echo string 　   →bb.shを実行したら「string」が表示される。
echo 'string'    →bb.shを実行したら「string」が表示される。
echo "string"    →bb.shを実行したら「string」が表示される。
echo
echo $string     →bb.shを実行したら「Hello world!」が表示される。
echo '$string'   →bb.shを実行したら「$string」が表示される。
echo "$string"   →bb.shを実行したら「Hello world!」が表示される。
echo
echo ${string}   →bb.shを実行したら「Hello world!」が表示される。
echo '${string}' →bb.shを実行したら「${string}」が表示される。
echo "${string}" →bb.shを実行したら「Hello world!」が表示される。
```

```shell:cc.sh(数字の変数)
num=10

echo num      →bb.shを実行したら「num」が表示される。
echo 'num'    →bb.shを実行したら「num」が表示される。
echo "num"    →bb.shを実行したら「num」が表示される。
echo
echo $num     →bb.shを実行したら「10」が表示される。
echo '$num'   →bb.shを実行したら「$num」が表示される。
echo "$num"   →bb.shを実行したら「10」が表示される。
echo
echo ${num}   →bb.shを実行したら「10」が表示される。
echo '${num}' →bb.shを実行したら「${num}」が表示される。
echo "${num}" →bb.shを実行したら「10」が表示される。
echo
```

##★9 変数の内容をechoやprintfで出力

```shell:cc.sh
name=Taro
age=20

echo '$name is $age years old.' →cc.shを実行したら「$name is $age years old.」が表示される。
echo "$name is $age years old." →cc.shを実行したら「rinchome is 32 years old.」が表示される。

printf '%s is %d years old.\n' $name $age →cc.shを実行したら「rinchome is 32 years old.」が表示される。
printf "%s is %d years old.\n" $name $age →cc.shを実行したら「rinchome is 32 years old.」が表示される。
```

##★10　特別な変数の事

```shell:dd.sh
#!/bin/sh

echo $0　→「スクリプトの名前」を表示
echo $1　→「第1引数」を表示
echo $3　→「第3引数」を表示
echo $#　→「与えられた引数の数」を表示
echo $*　→与えられたすべての引数. 引数全体が"で囲まれる？？、区切りは環境変数IFSで指定されたもの
echo $@　→与えられたすべての引数. 引数一つ一つが"で囲まれている？？、スペース区切り
```
上のdd.shを『./dd.sh apple orange banana grape』で実行すると、下記になる

```shell
./dd.sh
apple
banana
4
apple orange banana grape
apple orange banana grape
```

##★11 $?の事

```shell
#コマンドを実行すると、終了時にシェルに対して戻り値を返します。
#正常終了した場合は、0 正常終了しなかった場合は、0以外の値を返す

[rinchome@localhost ~]$ ls -l
合計 0
drwxrwxr-x. 2 rinchome rinchome 6 11月 23 12:14 01
-rw-rw-r--. 1 rinchome rinchome 0 11月 23 12:59 file1
[rinchome@localhost ~]$ ls -l file3
ls: file3 にアクセスできません: そのようなファイルやディレクトリはありません
[rinchome@localhost ~]$ echo $?　→ここで直前の実行結果エラーなので、0以外を返す
2
[rinchome@localhost ~]$ echo $?　→ここで直前の実行結果正常終了しているので、0を返す
0

```

##★12 ||や&&について

```shell
#bashシェルスクリプト上では && はAND制御演算子である。
#以下の場合、command1が成功したらcommand2を実行する。
$ command1 && command2

#|| はOR制御演算子である。
#指定したコマンド1が異常終了した時に、それに対処する形でコマンド2を実行できるので、
#主に事後のエラー処理などを行なう時に使える。
$ command1 || command2

```

##★13 演算子

```shell:ee.sh
#!/bin/sh

x=5
y=10
z=$(( x + y ))　→計算の場合は括弧で囲む

echo "x = $x"
echo "y = $y"
echo "x + y = $x + $y = $z" 
```
上のee.shを実行すると、下記になる

```shell
x = 5
y = 10
x + y = 5 + 10 = 15
```
そのほか演算子のこと

```shell
#減算　結果がマイナスになる
[rinchome@localhost 01]$ echo $(( 2 - 3 ))
-1

#余りとかも使える
[rinchome@localhost 01]$ echo $(( 3 % 2 ))
1

#インクリメントも使える 1++、++1みたいなのもできる
[rinchome@localhost 01]$ x=2
[rinchome@localhost 01]$ echo $(( x++ ))
2
[rinchome@localhost 01]$ echo $(( x++ ))
3

#べき乗も可能
[rinchome@localhost 01]$ echo $(( 2 ** 3 ))
8

#exprコマンドとかもあるが、実行速度が遅いという欠点があるため、bashが走る環境ならば二重括弧を使う方がいいらしい
[rinchome@localhost 01]$ echo `expr 1 + 1`
2
```

##★14 条件文で[]やtestや&&,||を使っての例

```shell:数字比較
#5と5がイコールなので「"True" → True」を返す
[rinchome@localhost ~]$ [ 5 -eq 5 ] && echo "True" → True
True → True
#5は3以上なので「"True" → True」を返す
[rinchome@localhost ~]$ [ 5 -ge 3 ] && echo "True" → True
True → True
#5は3より大きいので「"True" → True」を返す
[rinchome@localhost ~]$ [ 5 -gt 3 ] && echo "True" → True
True → True
#3は5以下なので「"True" → True」を返す
[rinchome@localhost ~]$ [ 3 -le 5 ] && echo "True" → True
True → True
#3は5より小さいので「"True" → True」を返す
[rinchome@localhost ~]$ [ 3 -lt 5 ] && echo "True" → True
True → True
#1と2は値が同じではないので、「"True" → True」を返す
[rinchome@localhost ~]$ [ 1 -ne 2 ] && echo "True" → True
True → True
```

```shell:文字比較
#「"apple" = "apple"」で同じ値なので、「"True" → True」を返す　※==でもよい
[ "apple" = "apple" ] && echo "True" → True
True → True
[ "apple" == "apple" ] && echo "True" → True
True → True
#「"apple" != "orange"」で違う値なので、「"True" → True」を返す
[rinchome@localhost ~]$ [ "apple" != "orange" ] && echo "True" → True
True → True
#「-z ""」で文字列("")の長さが0なので、「"True" → True」を返す
[rinchome@localhost ~]$ [ -z "" ] && echo "True" → True
True → True
```

```shell:日付比較
# -nt(newerThan)について
# -ot(olderThan)について

```

```shell:ファイル系のチェック
#ファイルやディレクトリーの確認
[rinchome@localhost 01]$ ll
合計 0
drwxr-xr-x. 2 rinchome rinchome 6 11月 23 14:27 dir
-rw-r--r--. 1 rinchome rinchome 0 11月 23 14:27 file

上記↑↑の内容を元に下記↓↓のチェックを実行
#上記の内容を見ると、fileがファイルであるので、「"True" → True」を返す
[rinchome@localhost 01]$ [ -f file ] && echo "True" → True
True → True
#上記の内容を見ると、dirがディレクトリーなので、「"True" → True」を返す
[rinchome@localhost 01]$ [ -d dir ] && echo "True" → True
True → True
#上記の内容を見ると、fileが存在するので、「"True" → True」を返す
[rinchome@localhost 01]$ [ -e file ] && echo "True" → True
True → True
#上記の内容を見ると、fileが読み込み可能なので、「"True" → True」を返す
[rinchome@localhost 01]$ [ -r file ] && echo "True" → True
True → True
#上記の内容を見ると、fileが書き込み可能なので、「"True" → True」を返す
[rinchome@localhost 01]$ [ -w file ] && echo "True" → True
True → True
#上記の内容を見ると、fileが実行可能ではないので、何も返さない
[rinchome@localhost 01]$ [ -x file ] && echo "True" → True
[rinchome@localhost 01]$
#上記の内容を見ると、fileがシンボリックリンクではないので、何も返さない
[rinchome@localhost 01]$ [ -L file ] && echo "True" → True
[rinchome@localhost 01]$

```
```shell:test

#3は、2より大きいので、「1」を返す
[rinchome@localhost 01]$ test 3 -gt 2 && echo "1"
1
#3は、2より小さいのでになっているので、「2」を返さない
[rinchome@localhost 01]$ test 3 -lt 2 && echo "2"
#3は、2より大きいで、失敗じゃなく成功しているので「3」を返さない
[rinchome@localhost 01]$ test 3 -gt 2 || echo "3"
#3は、2より小さいで、成功じゃなく失敗しているので「4」を返す
[rinchome@localhost 01]$ test 3 -lt 2 || echo "4"
4
#3は、2より大きいで、成功しているので「5」を返す
[rinchome@localhost 01]$ test 3 -gt 2 && echo "5" || echo "6"
5
#3は、2より小さいで、失敗しているので「8」を返す
[rinchome@localhost 01]$ test 3 -lt 2 && echo "7" || echo "8"
8
```

##★15 if文のサンプル

```shell:ifのサンプル1
# testコマンドを利用、trueになり1を返す 
if test 3 -gt 2
then
	echo "1"
fi
# []を利用、trueになり2を返す 
if [ 3 -gt 2 ]
then
	echo "2"
fi
#　falseになり、3を返さない
if test 3 -lt 2
then
	echo "3"
fi
#　falseになり、4を返さない
if [ 3 -lt 2 ]
then
	echo "4"
fi

```

```shell:ifのサンプル2(否定形)
#↓みたいに[ ! -f /home/rinchome/01/file ];をすると否定になる

#ファイルが存在しなかった、「file not found」を返す
if [ ! -f /home/rinchome/01/file ]; then
  echo "file not found"
else
  echo "file found"
fi
```

```shell:ifのサンプル3(and)
fileName=/home/rinchome/01/file

#and は、-aを利用 orは、-oを利用
#/home/rinchome/01/fileが存在していて、かつ読み取り可能だったら「OK」を返す
if [ -f ${fileName} -a -r ${fileName} ]; then
  echo "OK"
else
  echo "ERROR"
fi
```

##★16 swichのサンプル

```shell:switchのサンプル1
#!/bin/sh

fruit="apple"
case $fruit in
        apple)
                echo "これはりんごです."
                ;;
        orange)
                echo "これはみかんです."
                ;;
        babana|grape)
                echo "これはバナナかぶどうです."
                ;;
        *)
                echo "これはりんごでもみかんでもバナナでもぶどうでもありません."
                ;;
esac

上記↑実行すると、「これはりんごです.」で返される
```


##★17 forのサンプル

```shell:forのサンプル1(基本形)
#!/bin/sh

for fruit in apple orange banana
do
	echo $fruit
done

上記↑↑を実行した以下↓↓が返される
[rinchome@localhost 01]$ ./aa.sh
apple
orange
banana
```

```shell:forのサンプル2(数字のループ{}を利用)
#!/bin/sh

for i in {1..10}
do
	echo " $i 回目のループです."
done

上記↑↑を実行した以下↓↓が返される
[rinchome@localhost 01]$ ./aa.sh
1 回目のループです.
2 回目のループです.
3 回目のループです.
4 回目のループです.
5 回目のループです.
6 回目のループです.
7 回目のループです.
8 回目のループです.
9 回目のループです.
10 回目のループです.
```

```shell:forのサンプル3(数字のループ　Javaのfor文と同じような書き方のやつ)
#!/bin/sh

for (( i = 0 ; i < 10 ; i++ ))
do
	echo " $i 回目のループです."
done

上記↑↑を実行した以下↓↓が返される
[rinchome@localhost 01]$ ./aa.sh
0 回目のループです.
1 回目のループです.
2 回目のループです.
3 回目のループです.
4 回目のループです.
5 回目のループです.
6 回目のループです.
7 回目のループです.
8 回目のループです.
9 回目のループです.
```

```shell:forのサンプル4(コマンドの標準出力を利用　$()と`を利用)
[rinchome@localhost 01]$ ls
aa.sh  dir  file

上記から以下のシェルを実行
#!/bin/sh
for file in $(ls)
do
	echo $file
done

echo

for file in `ls`
do
	echo $file
done

上記のshellを実行すると、以下になる
[rinchome@localhost 01]$ ./aa.sh
aa.sh
dir
file

aa.sh
dir
file
```

##★18 whileのサンプル

```shell:whileのサンプル1
#!/bin/sh

n=1
while [ $n -le 5 ]
do
	echo " $n 回目のループです."
	n=$(( n+1 ))
done

上記のshellを実行すると、以下になる
[rinchome@localhost 01]$ ./aa.sh
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
```

```shell:whileのサンプル2

#↓↓↓↓↓↓↓↓のシェルをwhile1.shとして保存

#!/bin/sh

n=1
while [ $n -le 5 ]
do
	echo " $n 回目のループです."
	n=$(( n+1 ))
done

#↓↓↓↓↓↓↓↓のシェルをwhile2.shとして保存
#!/bin/sh

while IFS= read -r line
do
	echo $line
done < "./while1.sh"

上記のshellを実行すると、以下のようにソースのコードが読み込まれる
[rinchome@localhost 01]$ ./while2.sh
#!/bin/sh

n=1
while [ $n -le 5 ]
do
echo " $n 回目のループです."
n=$(( n+1 ))
done
```

```shell:whileのサンプル3

#以下のデータを「file1」として保存
No.1: apple
No.2: orange
No.3: banana

#以下のShellを「while3.sh」として保存
#!/bin/sh

while IFS=' ' read -r field1 field2
do
	echo $field2 $field1
done < "./file1"

#以下while3.shを実行すると、「apple No.1」みたいに逆で表示される。
#今回はsourceコマンドで実行
[rinchome@localhost 01]$ source while3.sh
apple No.1:
orange No.2:
banana No.3:
```

##★19 untilのサンプル

```shell:untilのサンプル1
#!/bin/sh

n=1
until [ $n -gt 5 ]
do
	echo " $n 回目のループです."
	n=$(( n+1 ))
done

上記のshellを実行すると、以下になる
[rinchome@localhost 01]$ ./aa.sh
 1 回目のループです.
 2 回目のループです.
 3 回目のループです.
 4 回目のループです.
 5 回目のループです.
```

##★20 selectのサンプル

```shell:selectのサンプル1

#以下のシェルを「select.sh」として保存
#!/bin/sh

PS3="番号で選択して下さい: "
select fruit in apple orange banana grape exit
do
	case $fruit in
		apple|orange|banana|grape)
			echo "$fruit が選択されました!"
			;;
		exit)
			break
			;;
		*)
			echo "1から4の番号で選んで下さい."
			;;
	esac
done

#上記のシェルを実行すると、以下の様に番号を選択したら処理が行われる
#5を入力したら、処理が終了する
[rinchome@localhost 01]$ source select.sh
1) apple
2) orange
3) banana
4) grape
5) exit
番号で選択して下さい: 1
apple が選択されました!
番号で選択して下さい: 2
orange が選択されました!
番号で選択して下さい: 3
banana が選択されました!
番号で選択して下さい: 4
grape が選択されました!
番号で選択して下さい: s
1から4の番号で選んで下さい.
番号で選択して下さい: exit
1から4の番号で選んで下さい.
番号で選択して下さい: exit
1から4の番号で選んで下さい.
番号で選択して下さい: 5　←ここでbreakが実行され、処理が終了する。
[rinchome@localhost 01]$
```

##★21 置換のサンプル

```shell:サンプル1
${var/old/new}→変数var内の最初のoldをnewに置換する

#以下のシェルを「string1.sh」として保存
#!/bin/bash

for i in apple-apple-apple apple-apple-orange apple-apple-grape
do
	echo ${i/apple/banana}
done

#上記を実行すると、以下になる（最初のappleだけ置換される）
[rinchome@localhost 01]$ source string1.sh
banana-apple-apple
banana-apple-orange
banana-apple-grape
[rinchome@localhost 01]$

```

```shell:サンプル2
${var//old/new}→変数var内の全てのoldをnewに置換する

#以下のシェルを「string2.sh」として保存
for i in apple-apple-apple apple-apple-orange apple-apple-grape
do
	echo ${i//apple/banana}
done

#上記を実行すると、以下になる（appleが全て置換される）
[rinchome@localhost 01]$ source string2.sh
banana-banana-banana
banana-banana-orange
banana-banana-grape
[rinchome@localhost 01]$

```

##★22　削除のサンプル

${var#pattern}→変数var内の最初の文字から検索し, patternにマッチした最短の文字列の一致部分を削除する

${var##pattern}→変数var内の最初の文字から検索し, patternにマッチした最長の文字列の一致部分を削除する

${var%pattern}→変数var内の最後の文字から検索し, patternにマッチした最短の文字列の一致部分を削除する

${var%%pattern}→変数var内の最後の文字から検索し, patternにマッチした最長の文字列の一致部分を削除する

```shell

#以下のシェルを作成し、「delete.sh」する
#!/bin/sh

str=apple-apple-apple

echo ${str#*-}
echo ${str##*-}
echo ${str%-*}
echo ${str%%-*}

#実行すると以下のようになる
[rinchome@localhost 01]$ source delete.sh
apple-apple
apple
apple-apple
apple
[rinchome@localhost 01]$
```

##★23　関数のこと

```shell:関数のサンプル1

#下記にシェルを作成し、「function.sh」として保存
#!/bin/sh

hello() { echo "Hello $1."; }

goodbye()
{
	echo "Goodbye $1."
	echo "Goodbye $2."
}

hello Taro
goodbye Taro Jiro

#上記を実行すると、以下のようになる
[rinchome@localhost 01]$ source function.sh
Hello Taro.
Goodbye Taro.
Goodbye Jiro.
[rinchome@localhost 01]$

```

##★24　bash起動時における設定ファイルの実行順序


><a href="https://qiita-image-store.s3.amazonaws.com/0/29682/52eb2250-8bb6-a094-c8f0-c0d63aa45d1a.png"><img src="https://qiita-image-store.s3.amazonaws.com/0/29682/52eb2250-8bb6-a094-c8f0-c0d63aa45d1a.png" width="80%"></a>

> * ログイン起動：CUIモードやsshでログインした時
> * 対話起動：GUIからターミナルを開いた時や、bashをコマンドで直接実行した時

##★25　感想

　何かの仕事で携わらないとやることないかも。。。
　変数x=10 みたいにスペースいれるとコンパイルエラーになるんですね。。。
　どういう開発の時に、この言語利用するんだろう。。。



##★26　その他技術メモ（JSPファイルの更新日付を最新化するシェルを作成してみた）

```shell:jspUpdateDate.sh
#!/bin/sh

#共通関数（JSPの更新日付を確認）
function confirmJspUpdateDate () {

    ls -l "${projectPath}"/jsp/price/applePrice.jsp
    ls -l "${projectPath}"/jsp/tax/smokeTax.jsp

}

#プロジェクトのパス変数を指定
projectPath="/var/xxxProject/"

#JSPの更新日付を確認するfunctionを呼び出し
confirmJspUpdateDate

#JSPファイルの更新日付を最新化
touch "${projectPath}"/jsp/price/applePrice.jsp
touch "${projectPath}"/jsp/tax/smokeTax.jsp

#JSPの更新日付を確認するfunctionをもう１回呼び出し
confirmJspUpdateDate
```
　
##★27　その他技術メモ（１個前に実行した結果が正常終了したかエラーになったか確認するシェル（IF文は1行で作成））

以下、「sample27.sh」を作成

```shell:sample27.sh
#!/bin/bash

set -o history

ls -ld /home/rinchome/python.pl
if [ $? != 0 ];  then echo "実行エラーになったコマンド→　`history | tail -n 2 | head -n 1`" ;  fi

ls -ld /home/rinchom/python2.pl
if [ $? != 0 ];  then echo "実行エラーになったコマンド→　`history | tail -n 2 | head -n 1`" ;  fi

```

上記を実行すると、以下のようになる
参考URL：https://qiita.com/POPOPON/items/623d84ffddcfcde45e5a

```shell:
-bash-4.2$ pwd
/home/rinchome
-bash-4.2$ ls
python.pl
sample27.sh
-bash-4.2$ ./sample27.sh
-rw-rw-r--. 1 rinchome rinchome 0  7月 31 17:16 /home/rinchome/python.pl
ls: /home/rinchom/python2.pl にアクセスできません: そのようなファイルやディレク トリはありません
実行エラーになったコマンド→　    3  ls -ld /home/rinchom/python2.pl
-bash-4.2$
```