ShellScriptに入門する(3日目)

今日もやっていきましょう(自作Script作ってたら日をまたいでた...)

[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)

関数を勉強し忘れてたので関数から...
## 関数
### 関数のスコープ
関数が呼ばれたことによって関数内で$@パラメータの値が変わっている
ただし変数xはグローバル変数であるため、関数内で値が変更されたあとにもその値が反映されている

``` func_scope.sh
#!/bin/sh

myfunc()
{
  echo "I was called as : ${@}"
  x=2
}

echo "Script was called with ${@}"
x=1
echo "x is ${x}"
myfunc 1 2 3
echo "x is ${x}"
```
```
% chmod 755 func_scope.sh; sh func_scope.sh a b c
Script was called with a b c
x is 1
I was called as : 1 2 3
x is 2
```

### 再帰

以下のScriptは再帰処理によって階乗を求めている

``` factorial.sh
#!/bin/sh

factorial()
{
  if [ "${1}" -gt "1" ]; then
    i=`expr ${1} - 1`
    j=`factorial ${i}`
    k=`expr ${1} \* ${j}`
    echo $k
  else
    echo 1
  fi
}

while :
do
  echo "Enter a number:"
  read x
  factorial $x
done
```
```
% chmod 755 factorial.sh; sh factorial.sh
Enter a number:
3
6
Enter a number:
5
120
Enter a number:
0
1
Enter a number:
^C
```

### 別ファイルで定義された関数を読み込む
common.libで定義した関数をfunction2.shで読み込んで呼び出している
また、稀によくみる拡張子`lib`が`library`の略だったのを知れたのは大きな収穫となった
C言語とかで使われてるらしい

``` common.lib
STD_MSG="About to rename some files ..."

rename()
{
  # expects to be called as: rename .txt .txt.bak
  FROM=${1}
  TO=${2}

  for i in *$FROM
  do
    j=`basename $i $FROM`
    mv $i "${j}$TO"
  done
}
```
``` function2.sh
#!/bin/sh
# function2.sh
. ./common.lib
echo "${STD_MSG}"
rename txt txt.bak
```
```
% touch hoge.txt
% ls | grep "hoge"
hoge.txt
% chmod 755 function2.sh; sh function2.sh
About to rename some files ...
% ls | grep "hoge"
hoge.txt.bak
```

### 自作Script紹介(cron生成Script)

今度こそScript作成に必要な文法は舐められたと思うので、Scriptを作成してみた
[作成したScript](https://github.com/kshiva1126/gen_cron)
対話形式でcronが生成できるようなScript(連想配列を使いたかったのでzshを使用している)
作りながらhoge日~fuga日に渡って実行するようなcronなどが生成できないことに気づいたので今後修正を加えていく

<details>
<summary>gen_cron.sh</summary>
<div>

``` gen_cron.sh
#! /usr/bin/zsh
. ./common.lib
typeset -g -A hash
hash=(min \* hour \* day \* month \* week \*)

typeset -a isErrday
while [ "${isErrday}" != y ] && [ "${isErrday}" != N ]
do
  echo "毎日を指定しますか? y/N [ y ]"
  read isErrday
  if [ -z "${isErrday}" ]; then
    isErrday=y
  fi
  if [ "${isErrday}" != y ] && [ "${isErrday}" != N ]; then
    echo "y/Nで選択してください"
  fi
done

if [ "${isErrday}" = N  ]; then
  checkDateOrWeek hash
fi

setTime hash

typeset -a command
while [ -z "${command}" ]
do
  echo "設定したいコマンドを入力してください"
  read command
done

cron="${hash[min]} ${hash[hour]} ${hash[day]} ${hash[month]} ${hash[week]} ${command}"
echo "${cron}"
```
</div>
</details>

<details>
<summary>common.lib</summary>
<div>

``` common.lib
checkDateOrWeek()
{
  typeset -a dateOrWeek
  while [ "${dateOrWeek}" != date ] && [ "${dateOrWeek}" != week ]
  do
    echo "日付を指定しますか? 曜日を指定しますか? date/week [ date ]"
    read dateOrWeek
    case "${dateOrWeek:=date}" in
      date)
        setDate hash
        ;;
      week)
        setWeek hash
        ;;
      *)
        echo "date/weekで選択してください"
    esac
  done
}

setDate()
{
  typeset -a isMonth
  while [ "${isMonth}" != y ] && [ "${isMonth}" != N ]
  do
    echo "月を設定しますか? y/N [ y ]"
    read isMonth
    case "${isMonth:=y}" in
      y)
        setMonth hash
        ;;
      N)
        setDay hash
        ;;
      *)
        echo "y/Nで設定してください"
        ;;
    esac
  done
}

setMonth()
{
  typeset -a month
  while [ "${month}" -lt "1" ] || [ "${month}" -gt "12" ]
  do
    echo "月を設定してください 1~12 [ 1 ]"
    read month
    case "${month:=1}" in
      [1-9] | 1[0-2])
        hash[month]="${month}"
        setDay hash
        ;;
      *)
        echo "1から12で設定してください"
        ;;
    esac
  done
}

setDay()
{
  typeset -a isDay
  while [ "${isDay}" != y ] && [ "${isDay}" != N ]
  do
    echo "日を設定しますか? y/N [ y ]"
    read isDay
    case "${isDay:=y}" in
      y)
        typeset -a day
        while [ "${day}" -lt "1" ] || [ "${day}" -gt "31" ]
        do
          echo "日を設定してください 1~31 [ 1 ]"
          read day
          case ${day:=1} in
            [1-9] | [1-2][0-9] | 3[0-1])
              checkValidityOfDay hash day
              ;;
            *)
              echo "1から31で設定してください"
              ;;
          esac
        done
        ;;
      N)
        ;;
      *)
        echo "y/Nで設定してください"
        ;;
    esac
  done
}

checkValidityOfDay()
{
  case "{$day}" in
    "2")
      if [ "{$day}" -gt 29 ]; then
        x=0
      else
        hash[day]="${day}"
      fi
      ;;
    "4" | "6" | "9" | "11")
      if [ "{$day}" -gt 30 ]; then
        x=0
      else
        hash[day]="${day}"
      fi
      ;;
    *)
        hash[day]="${day}"
        ;;
  esac
}

setWeek()
{
  typeset -a week
  while [ "${week}" -lt "1" ] || [ "${week}" -gt "7" ]
  do
    echo "曜日を選択してください\nmon:1/tue:2/web:3/thu:4/fri:5/sat:6/sun:7 [ 1 ]"
    read week
    case "${week:=1}" in 
      [1-7])
        hash[week]="${week}"
        ;;
      *)
        echo "1から7で設定してください"
        ;;
    esac
  done
}

setTime()
{
  old_IFS="${IFS}"
  IFS=:
  typeset -a isTime
  while [ "${isTime}" != ok ]
  do
    echo "時刻を入力してください [ 00:00 ]"
    typeset -a hour
    typeset -a min
    read hour min
    if [ "${hour}" -lt 10 ]; then
      hour="${hour:1}"
    fi
    if [ "${min}" -lt 10 ]; then
      min="${min:1}"
    fi

    case ${hour:=0} in
      [0-9] | 1[0-9] | 2[0-3])
        hash[hour]="${hour}"
        isTime=ok
        ;;
      *)
        echo $hour
        echo "00:00 ~ 23:59の間で入力してください"
        isTime=ng
        ;;
    esac

    if [ "${isTime}" = ok ]; then
      case ${min:=0} in
        [0-9] | [1-5][0-9])
          hash[min]="${min}"
          isTime=ok
          ;;
        *)
          echo $min
          isTime=ng
          echo "00:00 ~ 23:59の間で入力してください"
          ;;
      esac
    fi
  done
  IFS="${old_IFS}"
}
```    
</div>
</details>



```
% chmod 755 gen_cron.sh; . ./gen_cron.sh
毎日を指定しますか? y/N [ y ]
N
日付を指定しますか? 曜日を指定しますか? date/week [ date ]
date
月を設定しますか? y/N [ y ]

月を設定してください 1~12 [ 1 ]
13
1から12で設定してください
月を設定してください 1~12 [ 1 ]
12
日を設定しますか? y/N [ y ]
hoge
y/Nで設定してください
日を設定しますか? y/N [ y ]
y
日を設定してください 1~31 [ 1 ]
31
時刻を入力してください [ 00:00 ]
23:60
60
00:00 ~ 23:59の間で入力してください
時刻を入力してください [ 00:00 ]
23:59
設定したいコマンドを入力してください
echo "goodbye this year!"
59 23 31 12 * echo "goodbye this year!"
```

今日はここまで
明日もなにかしらのScriptを作ろうと思う