ShellScriptに入門する(2日目)

今日もやっていきましょう

[ShellScriptに入門する(1日目)](https://qiita.com/kshiva1126/items/955e8f636adae57aaff5)
[ShellScriptに入門する(2日目)](https://qiita.com/kshiva1126/items/fff8bb8d11f4590cc5bb)
[ShellScriptに入門する(3日目)](https://qiita.com/kshiva1126/items/f303453d00ea1aaaad8a)
[ShellScriptに入門する(4・5・6日目)](https://qiita.com/kshiva1126/items/53f65ef205d5342f7023)

## 変数のスコープ
以下のScriptを作成し、実行する

``` myvar2.sh
#!/bin/sh
echo "MYVAR is: $MYVAR"
MYVAR="hello there"
echo "MYVAR is: $MYVAR"
```
```
% chmod 755 myvar2.sh; sh myvar2.sh
MYVAR is: 
MYVAR is: hello there
```
想定した動きと同じ...
interactive shellから変数を受け取るためには`export`コマンドを使用する
ShellScript内で`MYVAR`の値が`What's up?`から`hello there`に変わっているがinteractive shellには変更後の値は渡されない

```
% MYVAR="what's up?"
% export MYVAR
% sh myvar2.sh
MYVAR is: What's up?
MYVAR is: hello there
% echo "$MYVAR"
what's up?
```
また、ShellScript内の変更をinteractive shellに反映させたい場合には `.`(dot)コマンドが使用できる
export MYVARは必要ない

```
% MYVAR="what's up?"
% echo "$MYVAR"
what's up?
% . ./myvar2.sh
MYVAR is: what's up?
MYVAR is: hello there
% echo "$MYVAR"
hello there
```

あと文字列中で変数を展開するときは`${VAR}`の形でも書けるみたい
これからはこっちの書き方を使っていく

## ループ
### forループ
下記のように`for`の後ろに変数を、`in`の後ろにループさせる対象を、`do done`内に実行させたい処理を記述する
また、`*`はワイルドカードで現在ディレクトリ内のすべてのファイルを意味している


``` for2.sh
#!/bin/sh
for i in hello 1 * 2 goodbye
do
  echo "Looping ... i is set to ${i}"
done
```
```
% chmod 755 for2.sh; sh for2.sh
Looping ... i is set to hello
Looping ... i is set to 1
Looping ... i is set to first.sh
Looping ... i is set to for.sh
Looping ... i is set to for2.sh
Looping ... i is set to myvar2.sh
Looping ... i is set to var.sh
Looping ... i is set to var2.sh
Looping ... i is set to 2
Looping ... i is set to goodbye
```

### whileループ
whileの後ろの`[]`内に条件が記述できる

``` while.sh
#!/bin/sh
INPUT_STRING=hallo
while [ "${INPUT_STRING}" != "ciao" ]
do
  echo "Please type something in (ciao to quit)"
  read INPUT_STRING
  echo "You typed: ${INPUT_STRING}"
done
```
```
% chmod 755 while.sh; sh while.sh
Please type something in (ciao to quit)
hallo
You typed: hallo
Please type something in (ciao to quit)
ciao
You typed: ciao
```
`:`は常にtrueと評価されるため無限ループとなる

``` while2.sh
#!/bin/sh
while :
do
  echo "Please type something in (^C to quit)"
  read INPUT_STRING
  echo "You typed: ${INPUT_STRING}"
done
```
```
Please type something in (^C to quit)
hallo
You typed: hallo
Please type something in (^C to quit)
ciao
You typed: ciao
Please type something in (^C to quit)
^C
```

これはファイルを1行ずつ読み取ってcaseで分岐している

``` while3.sh
#!/bin/sh
while read f
do
  case $f in
        hello)       echo "English"    ;;
        howdy)       echo "American"   ;;
        gday)        echo "Australian" ;;
        bonjour)     echo "French"     ;;
        "guten tag") echo "German"     ;;
        konnichiwa)  echo "Japanese"   ;;
        *)           echo "Unknown Language: $f" ;;
  esac
done < myfile
```
```
% echo "hello" > myfile
% echo "konnichiwa" >> myfile
% echo "hoge" >> myfile
% chmod 755 while3.sh; sh while3.sh
English
Japanese
Unknown Language: hoge
```

## Test
`test`はほぼすべてのShellScriptに使われているけどほとんど見当たらない
その理由が`[`が`test`のシンボリックリンクだからであり、すでに [whileループ; while.sh](#whileループ)でも使用していた
そのため `if SPACE [ SPACE "$foo" SPACE = SPACE "bar" SPACE ]`のように`[`の前後にスペースを入れる必要がある
めちゃくちゃオプションがあるから、最初のうちはひたすらググって書くことになりそう

``` test.sh
#!/bin/sh
if [ "${X}" -lt "0" ] # -lt: lesser than
then
  echo "X is less than zero"
fi
if [ "${X}" -gt "0" ]; then # -gt: greater than
  echo "X is more than zero"
fi
[ "${X}" -le "0" ] && echo "X is less than or equal to zero" # -le: lesser or equal
[ "${X}" -ge "0" ] && echo "X is more than or equal to zero" # -ge: greater or equal
[ "${X}" = 0 ] && echo "X is the string or number \"0\""
[ "${X}" = "hello" ] && echo "X matches the string \"hello\""
[ "${X}" != "hello" ] && echo "X is not the stirng \"hello\""
[ -n "${X}" ] && echo "X is of nonzero length" # -n: True if string is not empty.
[ -f "${X}" ] && echo "X is the path of a real file" || echo "No such file ${X}" # True if file exists and is a regular file.
[ -x "${X}" ] && echo "X is the path of an excutable file" # True if the file is executable by you.
[ "${X}" -nt "/etc/passwd" ] && echo "X is a file which is newer than /etc/passwd" # True if file1 is newer than file2
```
```
% X="hello"
% export X
% chmod 755 test.sh; sh test.sh
test.sh: 2: [: Illegal number: hello
test.sh: 6: [: Illegal number: hello
test.sh: 9: [: Illegal number: hello
test.sh: 10: [: Illegal number: hello
X matches the string "hello"
X is of nonzero length
No such file hello
```

# Case
`case ~ esac`の中に記述していく
*)で当てはまらないものすべてをキャッチできる (default:みたいな認識)
多少書き方は違うけど(":"が")"になっているとか)、割とすんなり理解できた

``` talk.sh
#!/bin/sh
echo "Bitte sprich mit mir ..."
while :
do
  read INPUT_STRING
  case $INPUT_STRING in
        hallo)
                echo "Hallo!"
                ;;
        ciao)
                echo "Ciao!"
                break
                ;;
        *)
                echo "Entschuldigung, ich verstehe nicht"
                ;;
  esac
done
echo "Das war's Leute!"
```
```
% chmod 755 talk.sh; sh talk.sh
Bitte sprich mit mir ...
hallo
Hallo!
guten morgen
Entschuldigung, ich verstehe nicht
ciao
Ciao!
Das war's Leute!
```

## 変数(Part2)
`$#`がパラメータの数で、`$0`はファイル名、`$1 ... $9`はそれぞれのパラメータ、 `$@`はすべてのパラメータ

``` var3.sh
#!/bin/sh
echo "I was called with ${#} parameters"
echo "My name is ${0}"
echo "My first parameter is ${1}"
echo "My second parameter is ${2}"
echo "All parameters are $@"
```
```
% chmod 755 var3.sh; sh var3.sh
I was called with 0 parameters
My name is var3.sh
My first parameter is 
My second parameter is 
All parameters are
% sh var3.sh hello ShellScript
I was called with 2 parameters
My name is var3.sh
My first parameter is hello
My second parameter is ShellScript
All parameters are hello ShellScript
```
9個以上のパラメータを取り扱いたいときはループの中で`shift`を用いて引数を1つずつずらしていく

``` var4.sh
#!/bin/sh
while [ "${#}" -gt "0" ]
do
  echo "\$1 is $1"
  shift
done
```
```
% chmod 755 var4.sh; sh var4.sh hoge fuga piyo
$1 is hoge
$1 is fuga
$1 is piyo
```

IFS(Internal Field Separator)は区切り文字が設定をすることができる
bashやzshなどの多くのシェルでスペースや改行文字などがデフォルトで設定されている
複数のデフォルトが設定されているからバックアップするときは`old_IFS=$IFS`じゃなくて`old_IFS="$IFS"`とするのが大事

```
old_IFS="${IFS}"
IFS="${old_IFS}"
```

``` ifs.sh
#!/bin/sh
old_IFS="${IFS}"
IFS=:
echo "Please input some data separated by colons ..."
read x y z
IFS="${old_IFS}"
echo "x is ${x} y is ${y} z is ${z}"
```
```
% chmod 755 ifs.sh; sh ifs.sh
Please input some data separated by colons ...
hello:kshiva:what a day!
x is hello y is kshiva z is what a day!
```

## 変数(Part3)
### デフォルト値
`${VAR:-DEFAULT}`は変数VARに値がセットされていない場合にDEFAULTが展開される
VARにDEFAULTが代入されるわけではない

``` name.sh
#!/bin/sh
echo "What is your name [ `whoami` ]"
read myname
echo "Your name is : ${myname:-`whoami`}"
echo "\$myname is ${myname}"
```
```
% sh name.sh
What is your name [ kshiva ]

Your name is : kshiva
$myname is
% sh name.sh
hoge
Your name is : hoge
$myname is hoge
```

`${VAR:=DEFAULT}`は変数VARに値がセットされていない場合にDEFAULTが展開され、VARにDEFAULTが代入される

``` name2.sh
#!/bin/sh
echo "What is your name [ `whoami` ]"
read myname
echo "Your name is : ${myname:=`whoami`}"
echo "\$myname is ${myname}"
```
```
% chmod 755 name2.sh; sh name2.sh
What is your name [ kshiva ]

Your name is : kshiva
$myname is kshiva
% sh name2.sh
What is your name [ kshiva ]
fuga
Your name is : fuga
$myname is fuga
```

今日はここまで
とりあえず変数からループ処理、testコマンド、caseコマンドの書き方がわかったので、明日から簡単な自作Scriptを書いていく

参考ページ:
https://www.atmarkit.co.jp/ait/articles/1807/05/news041.html#sample3
http://linuxcommand.org/lc3_man_pages/testh.html