シェルスクリプトを書く際に便利なコマンド＆基本の覚え書き

初歩的な事でもすぐに忘れてしまうので、自分用のメモとして書いていきます。
覚え書きには __変数展開__や __文字列操作__など、基本的な事を少し細かく書いていきます。
覚え書きはオマケとして書いていたつもりが、結構な量になってしまい、改めて自分は基本もまともに理解できていないのだと痛感しました(・・;)

# 便利なコマンド集
## trapコマンド
###trapコマンドとは??
>シグナルを受け取ったときの動作を設定します。
指定されたシグナルをシェルが受け取ると，指定された動作を実行します。

わかるようなわからないような…
とりあえず __シグナル__ がよくわからないのでGoogle先生に聞きました。

####シグナル
>シグナルとは、実行中のプロセスに対して、さまざまなイベントを通知するために送出されるものである。
 よく使用される、プロセスを終了するためのシグナルである SIGTERM や SIGKILL などのほかにも、数十種類のシグナルが存在する。

わかりやすく言うと、 __子プロセスが終了した時や``kill``した時などに送られるもの。__


###trap の書式
```
trap [-lp] [[arg] sigspec ...]
trap 'コマンド' [シグナル番号|シグナル名]
```

###オプション一覧

|オプション|説明|
|:--------|:--------|
|-l|シグナル名と対応する番号の一覧表示をする。|
|-p|単独で用いた場合，現在各シグナルに対して設定されている処理内容を表示する。|

###主なシグナル番号一覧

|シグナル番号|シグナル番号の意味|
|:----:|:-----|
|0|シェルが終了した。|
|1|ログアウト処理などによるハングアップが起こった。|
|2|Ctr+C などによる割り込みが発生した。|
|8|浮動小数点演算で例外が発生した。|
|13|送り先のないパイプに書き込んだ。|
|15|kill コマンドなどによるプログラムの終了が起こった。(killコマンドはデフォルトでこのシグナルを使用)|
|17|子プロセスが終了した。|
|21|バックグラウンドプロセスがtty入力を待っている。|
|28|端末エミュレータなどのウィンドウサイズを変更した。|




このtrapコマンドを使うと、スクリプトが正常終了した時にも指定した処理が実行されます。このコマンドを使ってプログラム中の一時ファイルを終了時に削除すると便利です。

```ShellScript
tmpdir="${TMP:-/tmp}/`basename ${0}`.$$"
mkdir -p ${tmpdir}
trap 'rm -r ${tmpdir}' 0
```

スクリプトの先頭で上記のように書いてあげるとスクリプト終了時に一時ファイルの削除が出来るので便利です。


##ブレース展開
bashやzshでは{a,b,c}記法を使うと、複数ファイルを手軽に指定できます。
例えば同じディレクトリに不要なファイル __gomi,trash__があるとします。
普通に削除する場合、
``$ rm /foo/bar/baz/gomi /foo/bar/baz/trash``
としますが、{a,b,c}記法を使えば
``$ rm /foo/bar/baz/{gomi,trash}``
で出来ます。
ブレースを複数使えばそこに入っている値の組み合わせでのリスト出力が出来ます。

```
$ echo {私は,あなたは}{りんごが好き,バナナが好き}
私はりんごが好き 私はバナナが好き あなたはりんごが好き あなたはバナナが好き
```
また、ブレースで数値の連番を出力することも出来ます。

```
$ echo {1..5}
1 2 3 4 5
```


# 【覚え書き】シェルスクリプトの基本中の基本
シェルスクリプトにおいて基本となる事やコマンドをメモ代わりに書いていきます。
##変数展開について
####${var:-str}
シェル変数が未定義か値がnullの場合、指定した値を返します。
この時変数varには値は代入されない。

```
$ echo ${animal}　←変数が未設定
$ echo ${animal:-cat}
cat
$ echo $animal　←変数は設定されていない
```

####${var:=str}
シェル変数が未設定か値がヌルの場合に指定した値を返し、さらに変数varに値が代入されます。

```
$ echo $animal　←変数が未設定
$ echo ${animal:=dog}
dog
$ echo $animal
dog　←変数にdogがセットされた
```

####${var:?str}
シェル変数が未設定か値がヌルの場合、シェル変数名の後に指定した文学列を返す。
また文字列「シェルプログラム名:var: str」を標準エラー出力に表示し、現在実行中
のスクリプトを中止する。

```
$ echo $animal
$ echo ${animal:?dog}
-bash: animal: dog
$ echo ${animal:?}
-bash: animal: parameter null or not set
```

####${var:+str}
シェル変数が設定されていて値がヌルでない場合、指定した値を返す。
変数が未設定か値がヌルの場合はヌルを返す。

```
$ echo $animal　←変数が未設定
$ echo ${animal:+monkey}　←変数が未設定
$ animal=dog　←変数animalに値dogをセット
$ echo $animal
dog
$ echo ${animal:+monkey}
monkey　←変数が設定されており値がヌルでないため、指定の値を返す
$ echo $animal
dog　←しかし設定されている値は「dog」
```

####${var#pattern}
####${var##pattern}
varの値の先頭からpatternにマッチする部分が削除された状態で展開される。
\#の場合は最短一致、\#\#は最長一致となる。patternにはパス名の展開と同じ規則
が適用される。

```
$ echo ${animal}
$ animal=”monkey:cat:dog:cow”
$ echo ${animal}
monkey:cat:dog:cow
$ echo ${animal#monkey}
:cat:dog:cow　←monkeyが削除された状態で出力
$ echo ${animal#m?}
nkey:cat:dog:cow
$ echo ${animal#m*}
onkey:cat:dog:cow
$ echo ${animal##m*}
```

####${var%pattern}
####${var%%pattern}
\#と逆でvarの値の後ろからpatternにマッチする部分が削除された状態で展開される。%の場合は最短一致、%%は最長一致となる。patternにはパス名の展開と同じ規則が適用される。

####${var/pattern/str}
####${var//pattern/str}
####${var/pattern}
####${var//pattern}
varの値のうちpatternにマッチする部分がstrに置換された状態で展開される。
/の場合は最初にマッチした部分のみが、//の場合はマッチする部分すべてが置き換えられる。

```
$ echo ${meal}　←変数が未設定
$ meal=”catfood:catmilk”
$ echo $meal
catfood:catmilk
$ echo ${meal/cat/dog}
dogfood:catmilk　←最初にマッチした”cat”のみdogに置換
$ echo ${meal//cat/dog}
dogfood:dogmilk　←すべての”cat”がdogに置換された 
```

####${#var}
変数varの値の文字列に置き換えられる。

##文字列操作
シェルスクリプトで文字列の加工･操作は必須。
そんな文字列操作の時によく使うであろうコマンド達の説明。
###cut
文字列の中から所定の位置にある文字列を抜き出すコマンドです。
####書式
`` $ cut [オプション] [ターゲット]``

|オプション|説明|
|:--------|:------|
|-b <バイト数>|切り出すバイト数を指定します。|
|-c <文字数>|切り出す文字数を指定します。|
|-d <文字>|区切り文字を指定します。デフォルトの区切り文字は\<Tab>です。|
|-f <フィールド数>|切り出すフィールド数を指定します。|
|-s|区切り文字を含まない文字列は出力に表示しない。|

###数値の指定方法
|数値の指定方法|説明|
|:----|:----|
|N|Nを切り出します。|
|N-|Nから行末までを切り出します。|
|N-M|NからMまでを切り出します。|
|-M|行頭からMまでを切り出します。|

####使用例
csvファイル(bar.csv)に -s で区切り文字｢、｣を指定し、4フィールドから行末まで切り出してみます。

```example
$ cat bar.csv
1A,2B,3C,4D,5E
AA,BB,CC,DD,EE

$ cut -d , -f 4- bar.csv
4D,5E
DD,EE
```

####※ cutコマンドでは、連続する空白文字を1つの区切り文字として扱うことが出来ないようです。なので空白区切りの文字列を切り出すには``awk``を使う必要があります。

###sed
文字列の置換や特定の行を抜き出すコマンドです。
#####書式
`` $ sed -e 's/置換前文字列/置換後文字列/g' [ターゲット]``

sedコマンドは-eオプションを複数指定することでコマンドを複数実行することが出来ます。
``$ sed -e ... -e ...``

-e のあとのパラメータは __アドレス,コマンド1文字、コマンドパラメータ__の順に書くことで処理内容をを表します。アドレスはどの行を処理対象とするかを表します。アドレスを省略すると全行が対象となります。
1行目からN行目まで処理をしたい場合は、アドレスに,N
N行目からM行目までを処理したい場合は、N,Mと書けばいいです。
コマンドには __s,d,p__などがあります。

###メタ文字
sed の正規表現に利用できるメタ文字です。いくつかのメタ文字(｢\+｣や｢\(｣など)はエスケープして使う必要があります。

|メタ文字|メタ文字の意味|
|:------|:--------|
|^|先頭|
|$|後方|
|.|任意の1文字|
|*|直前の文字の0回以上の繰り返し|
|\+|直前の文字の1回以上の繰り返し|
|\?|直前の文字が0回または1回のみ|
|[]|文字クラス、[abc0-9]ならば数字とa,b,cのどれか1文字|
\||or、[ab<パイプライン>ap]ならばabまたはap|
|\{3\}|直前の文字がN回だけ出現|
|\{N,M\}|直前の文字がN〜M回出現|
|\b|単語区切り|
※ orのところのパイプラインがエスケープ出来ず、汚くなってしまい申し訳ないです(T_T)



####sコマンド
sコマンドは正規表現で置換処理をします。
#####使用例
``$ sed -e 's/abc/ABC/g'``

最後の _g_ は、全てのマッチした文字列を置換することを意味します。
_g_ が無くても全行で置換を実行しますが、1行に2つ以上マッチングした場合は1つ目しか実行されません。

区切り記号の / は他の記号でもよく、パスの置換対象に / が含まれている場合は、 ! など他の記号を使ったほうがいいです。

置換後の文字列に & を指定すると、マッチした文字列の部分が出力され、\1,\2,… を指定すると、マッチした文字列のうち正規表現内で括弧でグルーピングされた部分が出力される。

```
$ echo xx34 | sed -e 's/xx\(.\)/-& \1-/g'
-xx3 3-4
```

sedは後方参照が行えます。後方参照したい箇所を``\(``と``\)``で囲み、参照は``\1``や``\2``などのように行います。
上記の例では、 _xx34_ の _3_ の部分を\1に記憶しています。
その後 & でマッチした文字列 __xx3__ に先頭に - を付け、 __\1-__ で3-4と加工

…であっているはず(・_・;)
正規表現は全然わからないので間違ってたらすいません。

####dコマンド
dコマンドは行を削除します。
#####使用例
1行目から5行目までを削除して、6行目以降を出力
``$ sed 1,5d``

__!__を付けると、逆に対象行以外を削除するコマンドに
なります。

6行目以降を削除して1行目から5行目のみを出力

```
# !はシェルが特別に解釈してしまうので、
# シングルクォーテーションで囲むか\でエスケープしましょう。
$ sed '1,5!d'
$ sed 1,5\!d
```

####pコマンド
pコマンドは単に行を出力するコマンドです。通常は -n オプションと組み合わせます。sedコマンドでは処理結果をデフォルトで出力しますが、 -n オプションを付けるとデフォルトの出力がされなくなり、 pコマンドの出力のみになります。

###tr
文字列を変換するコマンドです。
####書式
``$ tr [オプション] 文字列1 文字列2``

####オプション

|オプション|オプションの説明|
|:-------|:--------|
|-c|文字列1で指定した文字列以外を文字列2に置き換えます。|
|-d|文字列1で指定した文字を削除します。|
|-s|同じ文字の繰り返しを1文字に置き換えます。|
|-t|変換を行う前に文字列1を文字列2の長さに切り詰める。|

trコマンドには _CLASS_というものがあり、これを使って文字列の変換をすることが出来る。
####CLASS

|CLASS|CLASSの説明|
|:-----|:-------|
|[:alnum:]|アルファベットと数字|
|[:alpha:]|アルファベット|
|[:blank:]|スペースや水平タブ|
|[:cntrl:]|コントロール文字|
|[:digit:]|数字|
|[:graph:]|スペースを含まない表示可能な文字|
|[:lower:]|小文字アルファベット|
|[:print:]|スペースを含む表示可能な文字|
|[:punct:]|句読点|
|[:space:]|スペースや水平タブ、垂直タブ|
|[:upper:]|大文字アルファベット|
|[:xdigit:]|16進数文字|

#####使用例
文字列を大文字に変換

```
$ cat hoo.txt
0123456789abcdefghijkl

$ cat hoo.txt | tr '[a-z]' '[A-Z]'
0123456789ABCDEFGHIJKL
# CLASSを使った場合
$ cat hoo.txt | tr '[:lower:]' '[:upper:]'
0123456789ABCDEFGHIJKL
```

###awk
1つ以上のスペースで区切られた複数の文字列を処理するときに便利なコマンドです。
awkは読み込んだテキストをレコード(デフォルトでは行に相当)に自動的に分割します。さらにこのレコードをフィールドというものに分割します。このフィールドはデフォルトでは英文の単語に相当するものです。フィールドはレコードの先頭から __$1,$2,$3,...__と分割され、最後の要素は __$NF__に格納され、 __NF__にはフィールド数が格納されます。

####awkの組み込み変数

|組み込み変数|組み込み変数の説明|デフォルト値|
|:--|:--|:--|
|FILENAME|現在の入力ファイル名|"-"|
|RS|入力のレコード区切り文字|改行(\n)|
|FS|入力のフィールド区切り文字|スペース|
|ORS|出力のレコード区切り文字|改行(\n)|
|OFS|出力のフィールド区切り文字|スペース|
|NR|現在のレコード数|
|NF|現在のフィールド数|
|$0|現在の入力レコード|
|$n|現在の入力レコードにおけるn番目のフィールド|

例として、スペースで区切られた以下の様なテキストファイル(hoo.txt)があるとします。


```
$ cat hoo.txt
1 2 3 4 5
a b c d e
6 7 8 9 0
f g h i j
```


|行数($NR)\\フィールド数($NF)|$1|$2|$3|$4|$NF|
|:--|:--|:--|:--|:--|:--|
|NR=1|1|2|3|4|5|
|NR=2|a|b|c|d|e|
|NR=3|6|7|8|9|0|
|NR=4|f|g|h|i|j|

この場合、レコードとフィールドを表にしたものが上の表です。


####書式
``$ awk 'BEGIN{ 前処理 } 条件部 { 処理 } END{ 後処理 }'``
BEGINブロックはプログラム開始時に1度だけ実行され、ENDブロックはプログラム終了時に1度だけ実行される。


##条件分岐
###if
条件によって処理を分岐させたい場合、if文を使います。
｢if｣に対応した｢fi｣で閉じます。
####構文

```
if [コマンド]; then
	戻り値が0のときの処理
fi
```
__｢コマンドの戻り値が0のときのみ、then以下の処理を実行する｣__のが基本です。
__!__をコマンドの手前に付けると条件が逆になり、0以外の戻り値になったときに条件が満たされます。
__※[]の中は半角スペースを開けないとエラーになります!!__
``["hoge" == "hoge]``はダメで、``[ "hoge" = "hoge" ]``は大丈夫みたいです。
なんでダメなのかはわからないです(笑)

#####よく使う条件式
|条件式|条件式の説明|
|:--|:--|
|文字列1 = 文字列2|文字列1と2が同じなら真を返す。|
|数値1 -eq 数値2|数値1と2が同じなら真を返す。|
|数値1 -ne 数値2|数値1と2が同じでなければ真を返す。|
|数値1 -lt 数値2|数値1が2より小さければ真を返す。|
|数値1 -le 数値2|数値1が2と同じか小さければ真を返す。|
|数値1 -gt 数値2|数値1が2より大きければ真を返す。|
|数値1 -ge 数値2|数値1が2と同じか大きければ真を返す。|
|&&|AND|
|\|\||OR|

###case
変数の値によって複数の処理に分岐させたい場合は、case文を使います。
####構文
```
case $変数 in
	パターン1) 処理;;
	パターン2) 処理;;
	パターン3 | パターン4) 処理;;
	*) 処理;;
```
｢;;｣は｢case文を抜ける｣という意味です。｢*)｣は｢いずれのパターンにも当てはまらない場合｣という意味で省略出来ます。パターンを｢|｣で区切って論理和(OR)をとることも可能です。また、パターンには正規表現が使えます。
###while
条件が真である間、処理を繰り返し実行します。途中でループを抜けたい場合は __break__、ループの先頭に戻りたい場合は __continue__を使用します。
####構文
```
while [条件]
do
	処理
done
```
また、ファイルを1行毎に読み込んで処理させたい場合は、以下のような構文になります。

```
while read line
do
	処理(${line}を処理)
done < ファイル
```
__line__は変数で、指定したファイルを1行毎に読み込んで順次 __line__に格納します。ファイルの終端(EOF)まできたら __while__ を抜けます。

###for
与えられた引数の数だけ、処理を繰り返し実行したい場合は、for文を使用します。途中でループを抜けたい場合は __break__、ループの先頭に戻りたい場合は __continue__を使用します。

####構文

```
for 変数 in 引数1 引数2 …
do
	処理
done
``` 
変数に与えられた引数を順次代入していき、引数がなくなり次第ループを抜けます。
#####使用例
シェルに与えられた引数``$@``を変数``arg``に順次格納して表示してみます。

```
#!/bin/sh
for arg in $@
do
	echo ${arg}
done
```

```
$ for.sh 10 20 30 40 50
10
20
30
40
50
```

###until
条件が偽である間、処理を繰り返し実行したい場合は、until文を使用します。途中でループを抜けたい場合は __break__、ループの先頭に戻りたい場合は __continue__を使用します。

####構文

```
until [条件]
do
	処理
done
```
until文の用途は主に、あるコマンドが正常終了するまでコマンドを繰り返し実行したい場合などに便利です。


## chmod
ファイルやディレクトリのアクセス権を変更するコマンド。
シェルスクリプトを書いたら必ず使うコマンドです。

####書式
``$ chmod [オプション] [モード] [ターゲット]``

#####オプション

|オプション|オプションの説明|
|:--|:--|
|-c|パーミッションが変更されたファイルのみ詳細に表示します。|
|-f|パーミッションを変更できなかった場合にもエラーメッセージを表示しません。|
|-v|経過を表示します。|
|-R|ディレクトリとその中のファイルを再帰的に変更します。|

#####モード
変更するアクセス権を記述します。モードは記号による記述方法と数字による記述方法の2通りあります。


|記号|記号の説明|
|:--|:--|
|u|所有者|
|g|グループ|
|o|その他のユーザー|
|a|全てのユーザー|
|+|権限の追加|
|-|権限の削除|
|=|権限の変更|
|r|読み込み権限(4)|
|w|書き込み権限(2)|
|x|実行権限(1)|

|パーミッション|8進数|
|:--|:--|
|---|0|
|--x|1|
|-w-|2|
|-wx|3(2+1)|
|r--|4|
|r-x|5(4+1)|
|rw-|6(4+2)|
|rwx|7(4+2+1)|

#####使用例
例として全てのユーザーに実行権限を与えてみます。

```
# 記号表記
$ chmod a+x hoge

# 数値表記
$ chmod 777 hoge
```

数値表記の場合、3桁の数字でそれぞれのユーザーに対する権限を表記します。
例えば __rwxrw-r--__ とするとき、

|所有者|グループ|その他|
|:--|:--|:--|
|7(4+2+1)|6(4+2)|4|

となり、764という感じになります。

##最後に
色々と詰め込みすぎて、まとまりのない記事になってしまいました。(汗)
普段何気なく使っているコマンドも、調べてみると様々な使い方、書き方があるものなんですね。
こんな記事でも誰かの役に立てばいいなぁ…