ShellScript　メモランダム

***
ドットインストールの学習メモです。
***

###仮想マシン起動
* ターミナルでvagrantfileのフォルダまで行って
* `vagrant up` 仮想マシン起動
* `vagrant ssh` 仮想マシンにログイン.
 * (`vagrant status` runningを確認)

###shellscriptを書く

* shellscrpt_lessonsフォルダを作成
* helloファイルを作る
* vscodeの右下プレーンテキストをshellscriptに変更
* コマンドを実行するためのプログラムをかく
 * (`#!`（シェバン）の後にbashを指定）
 * (`which bash`　バッシュファイルの場所を調べられる）

```bash:hello
#!/bin/bash
echo hello
```

* ターミナルでパーミッションの変更
 *  `chmod +x hello`
* `./hello`
* helloが表示される

###文字列を表示してみよう
* hello の部分を　`""`　か　`''`　で囲む。
* １行に複数の命令を書きたい場合は`;`を入れる

```bash:bash
echo "hello"; echo "bye"
```

* `# comment` パウンド記号でコメント化

###変数の使用
* 変数はラベルのようなもの
* 変数名は_か英数字。
* 大文字小文字の区別あり。
* =の前後に空白の無いように！

```bash:hello
name="matasaburou"
echo "hello $name"
echo "hello ${name}さん"
```
* 内容を書き換えて欲しく無い場合は readonlyをつける

```bash:hello
readonly name="matasaburou"
name="shanshan"
echo "hello ${name}san"
echo "hello $name"
```

結果 　※shanshanは読み込みに失敗している

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
./hello: line 3: name: 読み込みのみの変数
hello matasaburousan
hello matasaburou
```

###特殊変数
`$1`, `$2`...とコマンドの引数を表す特殊変数を渡すと

```bash:hello
echo "hello $1"
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello mata
hello mata
```

`$0, $#, $@ or $*`で、**スクリプトの名前, 引数の数、　全ての引数**を表示できる

```bash:hello
echo "hello $0"
echo "hello $#"
echo "hello $@"
```

```terminai:terminal
[vagrant@localhost shellscript_lessons]$ ./hello mata sasa yoshi
hello ./hello
hello 3
hello mata sasa yoshi
```

###ユーザーからの入力

```bash:hello
read name
echo "hello $name"
```
ユーザーから１行の入力を受け取り`name`に格納してくれる。

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
newt
hello newt
```
newtを入力したら組み込まれて出力された。

***プロンプトオプション***
入力前にテキストの表示させたい場合

```bash:hello
read -p "お名前は？：" name
echo "hello $name"
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
お名前は？：newt
hello newt
```

***複数の入力を受け取る場合***

```bash:hello
read -p "３つ入力してください：" c1 c2 c3
echo $c1
echo $c2
echo $c3
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
３つ入力してください：red blue black
red
blue
black
```
四つの値を入れてしまうと最後の変数が二つ出力される。
二つの値を入力すると３つ目はブランクで表示される。

###配列を使う


```bash:hello
colors=(red blue pink)
echo ${colors[0]}
echo ${colors[1]}
echo ${colors[2]}
echo ${colors[@]}
echo ${#colors[@]}
```
配列の各要素、全ての要素、要素の個数。

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
red
blue
pink
red blue pink
3
```

***要素の変更***

```bash:hello
colors=(red blue pink)
# echo ${colors[0]}
# echo ${colors[1]}
# echo ${colors[2]}
# echo ${colors[@]}
# echo ${#colors[@]}

colors[1]=silver
colors+=(green orange)
echo ${colors[@]}
```
コメントアウトするには`command + /`

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
red silver pink green orange
```

###数値計算
値は文字列として処理されてしまうので回避する方法。

```bash:hello
echo 5+2

echo `expr 5 + 2`

echo $((5 + 2))

n=5
# (($n=$n+5)) 間違った使い方　
((n=n+5))
echo $n

echo $((10 / 3)) #あまりは返ってこない
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
5+2
7
7
10
3
```

###条件分岐　if

```bash:hello
read -p "name? " name
# if test "$name" = "mata"
# if ["$name" = "mata"]  []の間に空白がないとエラー
if [ "$name" = "mata" ]
then
	echo "hello ${name}!"
elif [ "$name" = "sasa" ]
then
	echo "hello ${name}!!!!!!!"
else
	echo "you are not allowed"
fi
```
thenの位置を変えた書き方もある。こっちの方が良さげ？ 見やすい・・・

```bash:hello
if [ "$name" = "mata" ]; then
	echo "hello ${name}!"
elif [ "$name" = "sasa" ]; then
	echo "hello ${name}!!!!!!!"
else
	echo "you are not allowed"
```

ちなみに結果は・・・

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
name? sasa
hello sasa!!!!!!!
[vagrant@localhost shellscript_lessons]$ ./hello 
name? mata
hello mata!
[vagrant@localhost shellscript_lessons]$ ./hello 
name? gsgsg
you are not allowed
```

###文字列の比較をしてみよう

[[]], (()),など・・・

```bash:hello
# [[]] 文字列やファイルの比較
# (()) 数値の比較

read -p "name? " name
if [[ $name = "mata" ]]; then
	echo "hello ${name}!!"
fi
```

```
[vagrant@localhost shellscript_lessons]$ ./hello 
name? mata
hello mata!
```

```bash:hello
# = or == 文字列が等しい
# != 等しくない
# -z 文字列の長さが０かどうか
# =~ 正規表現

read -p "name? " name
if [[ -z $name ]]; then
	echo "empty..."
fi
---------------------------------
read -p "name? " name
if [[ $name =~ ^t ]]; then #tから始まる
	echo "starts with t..."
fi
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
name? 
empty...
----------------------------------
[vagrant@localhost shellscript_lessons]$ ./hello 
name? tom
starts with t...
```

###ファイルや数値の比較をしてみよう

```bash:hello
# -e 存在しているか
# -f ファイルが存在しているか
# -d ディレクトリが存在しているか

if [[ -f $0 ]]; then
	echo "file exists..."
	fi
--------------------------------
if [[ -d $0 ]]; then
	echo "dir exists..."
fi
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
file exists...
[vagrant@localhost shellscript_lessons]$ ./hello 
なし・・・
```

```bash:hello
# == 等しい
# != 等しくない
# > >= より大きい、以上
# < <= より小さい、以下

# && and
# || or
# ! not

read -p "number? " n
if (( n > 10 )); then
echo "bigger than 10 !!"
fi
```

```
[vagrant@localhost shellscript_lessons]$ ./hello 
number? 1
[vagrant@localhost shellscript_lessons]$ ./hello 
number? 11
bigger than 10 !!
```

###forでループ処理をしてみよう

```bash:hello
for i in {1..5}; do
echo $i
done

for ((i=1; i<=5; i++)); do
echo $i
done
```

```terminal:terminal
#両方とも同じ結果
[vagrant@localhost shellscript_lessons]$ ./hello 
1
2
3
4
5
[vagrant@localhost shellscript_lessons]$ 
```

```bash:hello
colors=(red blue pinkl)
for color in ${colors[@]}; do
	echo $color
done
```

```
terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
red
blue
pinkl
```

```bash:hello
echo $(date)
echo ---------------------

# for item in `date`; do
for item in $(date); do
	echo $item
done
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
2020年 8月 27日 木曜日 10:54:31 JST
---------------------
2020年
8月
27日
木曜日
10:54:31
JST
```

###whileを使ってみよう

```bash:hello
i=0
while ((i < 10)); do
	((i++))
	echo $i
done
echo ----------------

i=0
while ((i < 10)); do
	((i++))
	if ((i == 4)); then
	continue
	fi
	if ((i == 8)); then
	break
	fi
	echo $i
done

while :
do
	read -p "command? " cmd
	if [[ $cmd == "quit" ]]; then
		break
	else
		echo "$cmd"
	fi
done
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello 
1
2
3
4
5
6
7
8
9
10
----------------
1
2
3
5
6
7
command? kaka
kaka
command? sasa
sasa
command? quit
[vagrant@localhost shellscript_lessons]$ 
```

###ファイルの処理をしてみよう

```terminal:terminal
#catでファイル作成
[vagrant@localhost shellscript_lessons]$ cat > color.txt
red
blue
pink
[vagrant@localhost shellscript_lessons]$ cat color.txt 
red
blue
pink
```

```bash:hello
#color.txtの内容に行番号をつけて表示
i=1
while read line; do
	echo $i "$line"
	((i++))
done < color.txt
echo --------------------

#テキスト内でファイルを指定せず、helloの内容に行番号をつけて表示
i=1
while read line; do
	echo $i "$line"
	((i++))
# done < color.txt テキスト内でファイル名をしてしたくない
done
```

（ちょっと分割）
color.txtの内容に行番号をつけて表示

```termnal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
1 red
2 blue
3 pink
```

テキスト内でファイルを指定せず、helloの内容に行番号をつけて表示

```termnal:terminal
[vagrant@localhost shellscript_lessons]$ cat hello | ./hello
1 #!/bin/bash
2 
3 i=1
4 while read line; do
5 echo $i "$line"
6 ((i++))
7 done < color.txt
8 echo --------------------
9 
10 i=1
11 while read line; do
12 echo $i "$line"
13 ((i++))
14 # done < color.txt
15 done
16 
17 
18 
```

ちなみに
なんかごちゃごちゃしてわかりにくいので、
color.textバージョンで両方やってみると

```
[vagrant@localhost shellscript_lessons]$ cat color.txt  | ./hello
1 red
2 blue
3 pink
--------------------
1 red
2 blue
3 pink
```
同じ結果になる。のでokってことで。
よーするに、今のhelloは他のファイルから行を読み取って、
行番号を足すシェルスクリプトになったってこと。
なんて姿になったんだ、お前・・・。

**cat color.txt  | ./hello**
catでファイルを指定して内容を表示
そんで　./helloで行番号追加、
したものを表示してくれるというパイプを使ったコマンドライン。

###caseで条件分岐してみよう

```bash:hello
read -p "signal color? " color
case "$color" in
	red)
		echo "stop!"
		;;
	blue)
		echo "go!"
		;;
	yellow)
		echo "caution!"
		;;
	*)
		echo "wrong signal"
esac
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
signal color? red
stop!
[vagrant@localhost shellscript_lessons]$ ./hello
signal color? yellow
caution!
[vagrant@localhost shellscript_lessons]$ ./hello
signal color? blue
go!
[vagrant@localhost shellscript_lessons]$ ./hello
signal color? pink
wrong signal
```
ちなみに
`blue|green)
  echo "go!"`
とすると　greenでもちゃんとgo!と表示してくれる。

＊ワイルドカードや部分一致なども使える。

### selectでユーザーに選択肢を与える。

```bash:hello
select color in red blue yellow green; do
case "$color" in
	red)
		echo "stop!"
		;;
	blue|green)
		echo "go!"
		;;
	yellow)
		echo "caution!"
		;;
	*)
		echo "wrong signal"
		break #selectはループ処理なのでbreakが必要、今回はここ
esac
done
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
1) red
2) blue
3) yellow
4) green
#? 1
stop!
#? 2
go!
#? 3
caution!
#? 4
go!
#? 5
wrong signal
[vagrant@localhost shellscript_lessons]$ 
```
###関数を使ってみよう

```bash:hello
#関数

# function hello() { これを省略すると↓
hello() {
	echo "hello...$1"
if [[ $1 == "mata" ]]; then
	return 0 #終了ステータスは０
else
return 1 #終了ステータスは１
#終了ステータスは０から２５５の間で指定できる。０は正常終了、それ以外（大抵１）じゃ以上終了。
#returnをつけないと実行したコマンドのステータスがそのまま関数雨の終了ステータスになってしまう。
#残るってことね。
fi
}

#関数の実行
#echo $?で直前の実行結果の終了ステータスを調べる。
hello
echo $?
hello mata ;echo $?
hello sasa ;echo $?
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
hello...
1
hello...mata
0
hello...sasa
1
```


###変数のスコープを理解しよう
```bash:hello

hello() {
	local name="mata"
	echo "hello..."
}

hello
echo $name
#変数はどこでも使えてしまう
#ので、変数のスコープを限定したい場合は
#localを足す
```

```terminal:terminal
[vagrant@localhost shellscript_lessons]$ ./hello
hello...
mata
ビフォーアフター
[vagrant@localhost shellscript_lessons]$ ./hello
hello...
　　　　　　（表示されなくなった！）
[vagrant@localhost shellscript_lessons]$ 
```

以上で１７レッスン終了！
楽しかったのだ１