シェルスクリプト作成Tips sh

zshを使ってます。
まだ途中だけどとりあえず投下して追記していきたい。。。

##参考
[【初心者向け】シェルスクリプトの作り方と実行方法](https://eng-entrance.com/linux-shellscript-do)
[スクリプト中で実行しているコマンドを表示する](http://sig9.hatenablog.com/entry/2015/12/15/220312)
[実用的なシェルスクリプト](https://www.kushiro-ct.ac.jp/yanagawa/ex/3-unix/03.html)
[シェルスクリプト](http://be.nucl.ap.titech.ac.jp/~koba/mem/sh_script.html)
[【簡単】シェルスクリプトでテキストを改行する方法](https://eng-entrance.com/linux-shellscript-newline)
[bashのif文でワイルドカード・正規表現を用いて条件判断をさせる | 俺的備忘録 〜なんかいろいろ〜](https://orebibou.com/2017/01/bashのif文でワイルドカード・正規表現を用いて条件/)
[bashのsetコマンドで覚えておきたい使い方9個 | 俺的備忘録 〜なんかいろいろ〜](https://orebibou.com/2015/12/bashのsetコマンドで覚えておきたい使い方9個/)
[シェルスクリプトで複数行のコメントアウト](https://qiita.com/kaw/items/ffdaf5d23627c0bd7e87)
[Shell 特殊変数 - Qiita](https://qiita.com/a_yasui/items/ec4f75b300410af8958d)
[Bashの便利な構文だがよく忘れてしまうものの備忘録 - Qiita](https://qiita.com/Ping/items/57fd75465dfada76e633)


## Tips
`#!/usr/bin/zsh`
これはスクリプトの一行目に記述する定型文。
この一行目で、実行時どのシェルでスクリプトを実行するかが決まる。
（bashやzshの独自機能を使わないのであれば`#!/usr/sh`でいいっぽい）

`zsh -x test.sh`
-x オプションを付けてシェル実行（例としてtest.shを実行）すると
shファイル内に記述されたコマンドも含めて表示する（デバッグ用途？）。

**`:`**
if文分岐内容などで`何もしない処理`を表す。
実際のコードで「何もしないから何も書かない」でいるとエラーになるので
**エラー回避のための記述**になる。


# コメントアウト
```zsh:1行
echo hoge
# echo　"ここは実行されない"
echo piyo
```
複数行の場合は **<< 任意の文字列** で複数行を括る。
コメントアウト終了部分には << は記述不要

```zsh:複数行

echo hoge
<< COMMENT
echo "ここは実行されない"
echo "実行されないんですよ！"
COMMENT
```
```zsh:例外
#!/bin/bash シェルスクリプト名.sh

シェルスクリプト1行目のコメントアウトは
bash（を起動させるため）のパス指定として機能している。（ただのコメントアウトではない）

ここの記述がおかしいと（パスが違うので）実行権つけてもスクリプト名だけで実行ができなかったり
そもそもシェル内で使用できる記述形式が変化したり （結果エラーwww） する。
```

# スクリプト名取得
```zsh:
echo "$0"  #シェル名のフルパス出力
echo "$(basename $0)"  #シェル名だけ出力

# /home/huga/hoge.sh
# hoge.sh
```
https://maku77.github.io/linux/path/path-of-script.html



# 引数
シェルスクリプトや関数等に渡す値。
任意の値をスクリプトや関数に引き継がせて処理を進めたいときなどに使用。

```zsh:配置
# スクリプト名.sh 引数

sample.sh arg
```
```zsh:引数を連結して表示
echo "$*"

# 変数に入れてから表示させる
list=($*)
echo "${list[*]}"
```
```zsh:引数を渡して実行した関数を変数に格納
#!/bin/bash array2.sh

[[ $# -eq 0 ]] && echo "nothing" | command return

function calc_one(){
  sum=0
  for c in "$@"; do sum=$(($sum + $c)); done
  echo "$sum"
}

result=`calc_one $*`
echo "$result"
```
```zsh:実行例
$ array2.sh 1 2 3
6
```
```zsh:引数の内容表示種別
echo "$#"  # 引数の数
echo "$1"  # 1つ目の引数の内容
echo "$*"  # 全引数の内容
echo "$@"  # 全引数の内容
```
```zsh:実行例
$ sample.sh a b c d e

5
a
a b c d e
a b c d e
```

https://anmino.hatenadiary.org/entry/20091019/1255988476

https://wa3.i-3-i.info/word1442.html

https://hacknote.jp/archives/33122/


#変数
宣言時は
`fff_fff=""`のように記述（`=`間のスペースは空けない）
呼出し時は$つけて
`$fff_fff`

変数に**コマンド内容を設定**する場合は
`` cmd=`cd ~/$HOME` ``のように**バッククォートでコマンド部分を挟んで**記述

# ファイルのパスを取得する（相対/絶対）

```zsh:相対パスを取得
#dirname ${0}

path=`dirname ${0}`
echo $path
```
```zsh:絶対パスを取得
#cd $(dirname ${0}) && pwd

filepath=`cd $(dirname ${0}) && pwd`
echo $filepath
```

https://www.kabegiwablog.com/entry/2017/08/17/193843


# 四則演算
```zsh:記述サンプル
echo $((1 + 2))	# 足し算
echo $((3 - 4))	# 引き算
echo $((5 * 6))	# かけ算
echo $((8 / 7))	# 割り算（割り切れない場合は小数以下が消える）
echo $((10 % 9))	# 剰余
echo $((11 ** 12))	# 階乗
```
```zsh:符号間のスペースは空けなくても良い
echo $((114+514)) 
```

## 数字の判定
```ruby:
-gt -ge   -eq   -le  -lt
 >   >=    =     <=   <
```
```zsh:
-eq # =  equal 等しい
-gt # >  greater than
-ge # >= greater than or equal
-lt # <  less than
-le # <= less than or equal
```

https://linuxfan.info/post-2967

https://qiita.com/toshihirock/items/461da0f60f975f6acb10


#関数
```zsh
#記述形式
testKansu(){
  echo "処理内容"
}

#呼び出し
testKansu
```

#コマンドあれこれ
###read
```zsh
#入力を受け付ける
read HENSU
echo $HENSU

#文字表示と組み合わせる場合
read -p "何か入力してください" ANSWER
echo $ANSWER

ecco -n "何か入力してください"; read ANSWER
[[ -n $ANSWER ]] && echo $ANSWER #入力がない場合、実行されない
```
```zsh:関数に渡す
funcOne(){
  echo "おはよう! "$HIKISU"ぎ君"
  echo -e "さよなら! ${HIKISU}ぎ君\nそいつは $1ぎだ！"
}

read -p "何か入力してください" HIKISU
funcOne $HIKISU #入力内容を引数にして関数に投げたりできる
```

# コマンドを繋げて実行（セミコロン アンパサント x2 パイプ x2）

```zsh:ワンライナーでの「if・else」文記述などの際に
;  複数のコマンドを1行で記述して繋げて実行

&& 先のコマンドが真で完了（true 成功）した際に次のコマンドを実行

|| 先のコマンドが偽で終了（false 失敗）した際に次のコマンドを実行
```
```zsh:サンプル
# ; セミコロン前後のスペースは割と曖昧でも実行できる
pwd;ls
pwd; ls
pwd ;ls
pwd ; ls
pwd    ; ls
pwd ;         ls

# ; 何個でもぉ 連結可能
echo;pwd;echo;uname;hostname

# && 先のコマンドが成功すれば続けて後のコマンドを実行
mkdir test_01 && cd $_

# || 簡単なtestコマンドなら1行で書ける
a=1;test $a -eq 2 || echo no
```
```zsh:;
echo 'Hoge!';pwd;ls -l

# 文字列括りにダブルクォーテーション（"）を使うと
# 含まれる文字列によっては実行結果がおかしくなるので注意
```

https://qiita.com/egawa_kun/items/714394609eef6be8e0bf

```zsh:\で、改行を無視して1行のコマンドとして実行可能
echo "Hoge!, \
tororo ms\
--nkb."

# Hoge!, tororo ms--nkb.
```

http://www.gadgety.net/shin/tips/unix/shell.html

# リダイレクト

## ファイル内容を初期化
```zsh:「：」で上書きして内容を空にする
: > test.txt
```

## 標準出力に表示させずにコマンド実行 /dev/null
コマンドは実行させたいが、余計な結果出力を省きたい場合に使用

```zsh:エラーも含めてコマンド結果を全て表示しない
&>/dev/null
```
```zsh:
ls &>/dev/null
# ls >/dev/null 2>&1
```
```zsh:エラーは表示
>/dev/null
```
```zsh:
ls >/dev/null
# ls 1>/dev/null
```

https://qiita.com/kiyodori/items/a37cfc23f068a5c16b2c

https://qiita.com/ritukiii/items/b3d91e97b71ecd41d4ea

https://qiita.com/laikuaut/items/e1cc312ffc7ec2c872fc

## その他
```zsh:
# 追記 >>
echo 'hoge' >> test.txt

# 強制上書き >| or >!
echo 'important' >| test.txt
echo 'important' >! test.txt

# 強制追記 >>| or >>!
echo 'important add' >>| test.txt
echo 'important add' >>! test.txt
```
※ 強制上書きは危険なので注意
※ シェルによっては `>!` は使えなかったり

#if文記述（testコマンド）
testコマンドで条件判定できる。[] で囲むとtestコマンドと同じ効果になる。
if文は基本的に [] を利用して条件式を記述する。

`if []` = `test `
`if [ "$1" == "piyo" ]` = `test "$1" == "piyo"`


```zsh:通常形式
if [ "$1" == "piyo" ]; then
  echo "TRUE"
elif [ "$url" == "katu" ]&&[ "$url" == "dearu" ]; then
  echo "AND条件"
elif [ "$url" == "mosikuha" ] || [ "$url" == "matawa" ]; then
  echo "OR条件"
else
  :
fi
```
```zsh:ワンライナー形式
#値（場所）が存在する
[ -e ~/nanyanen/honma ] && echo "aruyo"
#値（場所）が存在しない
[ ! -e ~/nandeyanen/ahoka ] && echo "naiyo--"
#値がある（空でない）1
[ -n $nokara ] && echo $ataiari
#値が空である
[ -z $atai ] && echo $karadesu

#参照したファイルの中身が0バイトではない
[ -s $file ] && echo $nakamiGAarimasu


#ファイルか判定
[ -f $file ] && echo "file desuyo."
#ディレクトリか判定
[ -d $dir ] && echo "directory desuyo."

#ファイルが存在し、実行権限が付与されている
[[ -x `which hoge` ]] && echo "hoge dekiruyo."


# || で else文を設定できる
[ $hoge = $foo ] && echo "true" || echo "false"

# [[ ]] で囲えば 複数条件を記述可能
[[ ! -z $hoge && $piyo == "foo" ]] && out=` > $bar`

```
https://qiita.com/zayarwinttun/items/0dae4cb66d8f4bd2a337

http://d.hatena.ne.jp/srkzhr/20080201/1201833497

http://sweng.web.fc2.com/ja/program/bash/bash-check-file.html

http://karoten512.hatenablog.com/entry/2018/01/19/002558

# for文
```zsh:基本例（ワンライナー）
for i in a a a; do echo bbb; done
# bbb
# bbb
# bbb
```
```zsh:配列内の値を利用
arr=(el psy cong lue)
for i in ${arr[@]}
do
    echo $i
done

#el
#psy
#cong
#lue
```

**引数全て** = `$*` `"$@"`

```zsh:引数の内容を全て表示（ワンライナー）
for c in $*; do echo $c; done

for c in "$@"; do echo $c; done
```
```zsh:引数があれば実行（if_&_for）
if [[ -n $* ]]; then
    for content in $*
        do echo $content
    done
fi
```

# while文
```zsh:基本
while 条件式
do
  …
  break
  …
done
```
```zsh:break条件以外は無限ループ
# hige 以外はお断り
while :
do
  read key
  [[ $key = hige ]] && break
done
clear
echo "髭！"
```
```zsh:continueでループ継続
# hige を打っても終われない
while :;do read key;[[ $key = hige ]] && continue; done
```
```zsh:exitで強制終了
while :
do
  read key
  exit
done
echo "じゃあの"

```
```zsh:ワンライナー例
# while 条件式; コマンド ; done

while true ; do ps aux | grep httpd ; echo ""; sleep 2 ; done ;

while sleep 1; ls -l | wc -l ; done

while read line; do echo "${line}" done < text.txt
```

https://shellscript.sunone.me/while.html

https://www.server-memo.net/shellscript/while.html

http://blog.livedoor.jp/s06280hk/archives/1791016.html

https://qiita.com/ryuichi1208/items/55b73d8ae75993dc10c1

# case文
条件部分はクォート囲みありでもなしでもおk

```zsh:基本
case $1 in
  # 条件 ) コマンド ;;
  1 ) echo "(・・)" ;;
  "3" ) echo "(・・)(・・)(・・)" ;;
  8 ) echo "(・・) x 8" ;;
  * ) echo $1 ;;
esac
```

```zsh:while文と併用してなんちゃって対話形式
while :
do
  read key
  case "$key" in
    "q" ) echo "break で終了";break ;;
    "a" ) echo "$keyやろ" ;;
    ??l* ) echo "3文字目がlやろ" ;;
    hoge ) echo "${key}やろ！" ;;
    hige ) echo "髭！" ;;
    94 ) echo "$key-95=-1" ;;
    [hH]* | [pP]* ) echo "$key 先頭が h or H or p or P の文字列" ;;
    [A-Z]* ) echo "$key 先頭が大文字の文字列" ;;
    * ) echo "$key （・⊇・）?" ;;
  esac
done
```
`コマンドで条件分岐`するには`バッククォート`で括る

```zsh:コマンドで条件分岐
ASTERISK="*"

# case `コマンド` in
case `echo $(($RANDOM % 100))` in
  8* ) echo " 8なんとか" ;;
  9* ) echo " ほぼ100？ ほぼ10？" ;;
  *5 ) echo " 5の倍数" ;;
  * | $ASTERISK ) echo " （・⊇・）?" ;;
esac
```

https://shellscript.sunone.me/case.html


# 日時を利用
```zsh:出力
# 年月日時分秒
echo `date +%Y%m%d%H%M%S`
```
```zsh:日時をファイル名に含めてファイル作成
touch test`date +%Y%m%d`.txt
```

# 指定の数値分だけ出力

```zsh
for i in {1..10}; do echo $i; done
```

https://hokaccha.hatenablog.com/entry/20110719/1311067089

# 算術式展開
```zsh
x=2
y=5
sum=$((x+y))

echo $sum
#7

echo $((x+y))
#7
```
```zsh:1から100まで足す(while)
#!/bin/bash

i=1
sum=0
while [[ $i -le 100 ]]; do ((sum=sum+i)); ((i+=1)); done
echo $sum

```
```zsh:forでやる
#!/bin/bash

sum=0
for a in {1..100}; do ((sum=sum+a)); done
echo $a
```

https://qiita.com/ftakao2007/items/89a3ac707ad65f0b076e

https://qiita.com/d_nishiyama85/items/a117d59a663cfcdea5e4


# 配列
```zsh:文字列
arr=(hoge hoge2 hoge3)
echo ${#arr[*]}
# 3
echo ${#arr[@]}
# 3
echo ${#arr[1]}
# 2
echo ${arr[2]}
# hoge3

hoges="p pk piyo"
echo $hoges
# p pk piyo
```
```zsh:1引いた数
arr=(hoge hoge2 hoge3)

key_max=$((${#arr[*]} - 1)) #添字の最大値を取得
```

# 文字列の切り出し（cut）
```zsh:基本
# cut -d "区切り文字" -f 何列目
```
```zsh:例
date
#> Sun Dec 14 20:11:45 JST 2018


## 時刻（時分秒）を 切り出し
date | cut -d " " -f 4
#> 20:11:45

## さらに 時間（hour）だけ 切り出し
date | cut -d " " -f 4 | cut -d: -f 1
#> 20
```

# 特殊記号
```zsh:
$#・・・・・・シェルスクリプト実行時の引数の数
$@,$*・・・シェルスクリプト実行時の全引数の変数
$$・・・・・・現在のプロセス番号(PID)

$?・・・・・・直近のコマンドの実行結果のステータスコード
```
```zsh:$?の結果種類について

	0・・・・コマンド正常終了
	1・・・・コマンド異常終了
	2・・・・シェルスクリプトの異常終了（文法エラー、文字列の比較など）

	126・・・権限のないファイルを実行した場合のエラー
	127・・・"command not found" 時のエラー
	130・・・強制停止（Ctl-C）時のエラー
```

http://oomatomo.hatenablog.com/entry/33667953

# 正規表現
`[[]]`で囲み、`=~`を用いて正規表現でif文とかできる。
正規表現の条件には**クォーテーションを使わない**ように。 

```zsh:=~
name=nmhogen
[[ $name =~ hoge.*$ ]] && echo hoge!
```
```zsh:変数を利用
tel='09012345678'
reg='[0-9]{3}-?[0-9]{4}-?[0-9]{4}'
# 0~9 の 3文字-4文字-4文字 の組み合わせ

[[ $tel =~ $reg  ]] && echo $tel
```

http://takuya-1st.hatenablog.jp/entry/2016/12/22/175514

---
`==`でも正規表現できる

```zsh:別例
if [[ `file today_memo.txt` == *text* ]]; then echo 'text-file des4os4'; fi
```

# ランダム（乱数）
```zsh:$RANDOM（0～Nの範囲）
# $RANDOM を使う。シェルがbashかzshなら使える。shは使えない

echo $(($RANDOM % 10))
# 0 〜 9 でランダムに出力
```
```zsh:/dev/random
# シェルの種類に関係なく乱数を使える

echo $(( $(od -vAn -N4 -tu4 < /dev/random) % 10 ))
# 0 〜 9 でランダムに出力
```

https://qiita.com/piroor/items/6300e665bc8ac314211b

https://news.mynavi.jp/article/20100430-shell-random-number/



# 音（サウンドファイル）を鳴らす
:bangbang:`yum でインストールできないかも？`
```zsh:afplay
# afplay サウンドファイルのパス
afplay ~/Desktop/hoge/piyo/foo.wav                                  

## たいていの拡張子はカバーしてるっぽい？
```
```zsh:オプション
# 再生する時間　[秒数指定] （ファイルの再生時間より長くはできない）
afplay  -t 0.01 ファイルパス
afplay  -t 30 ファイルパス

# 音の品質　[0/1 →　低/高] （違いがわかんね）
afplay  -q 0 ファイルパス
afplay  -q 1 ファイルパス

# ファイル再生（音声・時間）を圧縮する　[0は通常再生（だと思う）]
afplay  -r 0 ファイルパス
afplay  -r 50 ファイルパス

# -v [音量] は使わないほうがいい。スピーカーからHAKAIONが鳴りうるので
```

https://webkaru.net/dev/bash-script-alert-sound/

---