【 ${var:-"abc"} 】マイナーだけど作業に便利なBashのシェル機能とか標準コマンドを紹介してみた

どうも。朝は紅茶を飲まないとご飯が喉を通らないタイプの人間なかちおです。
ちなみに明日は[虎ノ門:arrow_upper_right:](http://www.ni-cs.com/blog/kisarazu)に出かけてくるので後日レポートでも書きます。

#  シェルについて

今回は、bashやその他互換シェルで使える、少し便利なシェルの機能を紹介します。
殆どがシェルスクリプトで何か自動化するときに使えるものです。ぜひ作業効率upに使ってください。

## 変数
### 配列
シェルでは変数(環境変数)が使えますが、配列を使うことができます。
ほとんど記号はC言語と同じです。
方法は簡単で、=の右辺をカッコで囲んであげれば、空白区切りで要素が認識されます。このとき文字列はクォートで囲む必要があります。

```
$ a=(1 2 "hello" "world")
$ echo ${a[1]}
1
$ echo ${a[3]}
hello
```

### 代入
`${}`を使うことで変数の展開ができますが、そのときにオプションをつけることができます。

```bash
$ echo ${arg:-"abc"}
```
この場合、変数argが定義されていればargの内容定義されていなければabcを返します。
これをシェルスクリプトの中で`HOST=${HOST:-"192.168.1.1"}`のように使うことで、`export HOST="10.1.1.1"`のように環境変数をセットしているときにはそれを使い、そうで無いときはデフォルト値192...を使うという処理ができます。

他にも:+や:?があります。

```bash
$ var=test # 代入してやってみる
$ echo ${var:-"abc"} # $varが定義されていればvarの値、なかったら"abc"を返す
test
$ echo ${var:="abc"} # $varが定義されていればvarの値、なかったらvarに"abc"を代入して"abc"を返す。
test
$ echo ${var:?"abc"} # $varが定義されていればvarの値、なかったら標準エラー出力に"abc"を表示する。
test
$ echo ${var:+"abc"} # $varが定義されていれば"abc"、なかったら空文字を返します。
abc

$ unset var # 今度は消してやってみる
$ echo ${var:-"abc"} # $varが定義されていればvarの値、なかったら"abc"を返す
abc
$ echo ${var:="abc"} # $varが定義されていればvarの値、なかったらvarに"abc"を代入して"abc"を返す。
abc
$ unset # 上で代入されちゃったのでまた消す
$ echo ${var:?"abc"} # $varが定義されていればvarの値、なかったら標準エラー出力に"abc"を表示する。
bash: var: abc
$ echo ${var:+"abc"} # $varが定義されていれば"abc"、なかったら空文字を返します。
 # 空文字
```

### 変数の指定取り出し

また、長さを指定することもできます。
`${変数:offset:length}`と指定し、offset番目の文字からlength文字分表示します。
lengthを省略すると一番最後まで全部表示します。
ちなみにカウントは0から始まるようです（先頭の文字は0番目）

```bash
$ str=abcdefghi
$ echo ${str:0} # 0文字目から最後まで全部
abcdefghi
$ echo ${str:5} # 5文字目から最後まで全部
fghi
$ echo ${str:4:2} # 4文字目から2文字分
ef
```

また、lengthを負の数にすると、offset番目からではなく、後ろの文字からlength番目の数まで表示します。
この場合も、いちばん後ろは0から始まるようです。

```bash
$ str=abcdefghi
$ echo ${str:3:-2} # 「3文字目」から「後ろから2文字目」まで返す
defg
$ echo ${str:0:-6} # 「0文字目」から「後ろから6文字目」までを返す
abc
```
## $(( ))

$((と))で囲むと、中で計算したものが帰ってきます。四則演算、余り、ビット演算など色々なことができます。16進数も使用可能です。
また、環境変数は`$`なしで使用可能です。

```bash
$ echo $(( (1 + 2) * 100 % 11))
3
$ a=123 b=456 && echo $((a+b))
579
$ echo $(( ( 0xf0 ^ 4 ) >> 1 ))
122
```
## `` , $()

ダブルクウォート2つまたはダラーカッコでコマンドを囲むことで、そのコマンドの返り値（出力）をその部分に置き換えることができます。

```bash
$ cat a.txt
https://google.com

$ curl "$(cat a.txt)"

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://www.google.co.jp/?gfe_rd=cr&amp;dcr=0&amp;ei=TK3VWc2IG7DEXtvltZgD">here</A>.
</BODY></HTML>
```

## $$

現在のpid(プロセスID)が入ってる特殊変数です。
シェルスクの中で呼び出すことにより、自身のpidを確認できます。

## kill
プロセスを殺すのに使いますが、本質的にはシグナルというモノをプロセスに投げています。
`kill -9 1000` のように使うことで、pid1000番のプロセスに9版のシグナルを送ることができます。
9番を受け取るとプロセスは終了するようになっています。
また、他にもユーザーがシグナルを設定することができ、たとえばphpのデーモンプロセスはSIGUSR1というシグナルを送ることで、ファイルを終了せずに読みなおします。
プロセスとプロセスの低レベルな通信です。

シグナルには、番号以外に名前もついています。
シグナル2番はSIGINTとも書くことができて、Ctrl-Cをされたときにシグナルが呼び出されます。他にもプロセスを終了させるのは前述された(SIGKILL)9、他にもSIGUSRというユーザーが勝手に使っていいシグナルもあります。

## trap
以前書きましたが、シグナルをキャッチするコマンドです。Ctrl-Cなどの割り込みからSIGUSRなどをキャッチできます。これをkillと組み合わせて使うことで、シグナルにあった動作をさせることができます。

```bash:a.sh
function event(){ ## 関数作成
    echo 'get signal !!'
}

trap event SIGUSR1 ## これで5版のシグナルが来たときはeventコマンド(関数)を実行する
echo $$ ## pid出力
while :
do
sleep 10
done ## 無限ループ

```
上のシェルスクを実行したら、もう一つターミナルを開いて、表示されたプロセスIDにSIGUSR1のシグナルを投げてみましょう。

```bash
$ kill -SIGUSR1 プロセスID
```

そうすると、シェルスクでget signal !!と表示されると思います。こうして割り込み処理に使えます。

## : (コロン)
なにもしないコマンドです。そして、返り値は常に0です。なので上のwhileのときに使うとtrueと書くよりスッキリします。また、if文は中身に何かを書かないとエラーになるのですが、そういうときにもこれを書いておくことでシンプルなコードを書くことができます。

## ブルース展開
シェルでは、簡単な展開機能があります。スペース区切りを1つの塊と見て、その中の`{}`の要素を取り出しスペース区切りにします。また、「..」で数字やアルファベットを2つつなげると間がすべて指定されたとみなします。seqコマンドみたいな感じです。

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

$ echo 今日、{私,僕}はご飯を食べました。
今日、私はご飯を食べました。 今日、僕はご飯を食べました。

$ echo {a,b}{c,d}
ac ad bc bd

$ cat test{1..5}.c
cat1の内容
cat2の内容
cat3の内容
cat4の内容
cat5の内容

$ for i in {a,b,c,d}.c ; do echo $i ; gcc $i ; done
# echo a.cをしてからgcc a.cをabcdの四回繰り返します
```

## wall

そのOSで動作する全てのユーザーのすべてのシェルにメッセージを送ります。
どう動くか試してみたかったら、複数ターミナルを開いた状態で`wall -- helloworld`と入れてみてください。
サーバーを複数人で利用しているときにこれからシャットダウンすることを伝える場合なんかに便利です。

## lsof

`lsof ファイル名`
で、そのファイルを読み書きしているプロセスを確認できます。
プロセスの動作確認や、ソケットの持ち主を探すときなどに有用です。

## nohup

コマンドの前にnohupと入れることで、nohup経由でプロセスが立ち上がります。
こうして立ち上がったプロセスは、sshセッションがキレたりユーザーがログインしても、終了せず動作し続けます。
時間のかかるmakeなどをする場合に使えば、家を出る前に会社のサーバーにsshして`nohup make`することで、sshを切ってもサーバー側でビルドを継続できます。

## shift pageup , shift pagedown
あまりシェル本体とは関係無いですが、シェルの画面を遡るキーです。
サーバーにディスプレイを繋げてCUIなどで作業しているときによく使います、マウス使えないですからね。ちなみにpagedown,upキーがない方はfn と矢印キーで代用してもだいたい動きます。

## まとめ
シェル最高!