Linuxコマンド(Bash)でバックグラウンド実行する方法のまとめメモ

bashコマンドのバックグランド実行方法について、まとまっている記事が見つからなかったのでまとめメモ
# 通常のバックグラウンド実行

## &でバックグランド実行
参考：http://kazmax.zpp.jp/linux_beginner/process_background.html

もっとも基本的なバックグランド実行、コマンドの後ろに&をつけて実行する。
ターミナルの切断が切れたりしてログアウトした場合に、
この方法で実行したプロセスはkillされてしまうので注意する。
途切れたら困る処理の場合はtmuxのセッション上で実行、
もしくは後述するnohupコマンドでのバックグラウンド実行推奨。


```Bash
# バックグラウンド実行
$ sleep 5 &
[1] 21871

# プロセス確認
$ ps $! # ps 21871
  PID TTY      STAT   TIME COMMAND
21871 pts/0    S      0:00 sleep 5
```


## &で連続したコマンド群をバックグランド実行
複数コマンドを順番に実行する処理をバックグランド実行したい場合
例えば、5秒たったら、「done」という出力を得るコマンド

```Bash
# フォアグラウンド実行
$ sleep 5; echo done
```

これをバックグランドで実行する方法たち

1.シンプルにコマンドをカッコでくくる

```Bash
$ (sleep 5; echo done) &

# ※以下のようにすると`sleep 5`がバックグラウンド実行されないためダメ
# $ sleep 5; echo done &
```

2.コマンドを中かっこでくくる

```Bash
# 前後のスペースとコマンド群の最後のセミコロン必要なの注意
$ { sleep 5; echo done; } & 
```

3.複数行に分けてコマンドを中かっこでくくる

```Bash
$ {
> sleep 5
> echo done
> } &
```

4.shコマンドを非同期実行する
shコマンドの-cオプションでコマンドをひとまとめにして、
shコマンド自体を非同期実行とする

```
$ sh -c 'sleep5; echo done' &
```

5.そもそもシェルにする

```Bash:test.sh
#!/bin/sh
sleep 5
echo done
```

```Bash
$ sh test.sh &
```

#nohupでログアウトしてもバックグラウンド実行
## nohupでsshログアウトしても実行維持
nohupコマンドでsshログアウト時に送られるHUPシグナルによる
バックグラウンドジョブのkillを行われないようにする

HUPシグナルについては以下の資料がとても参考になる
参考：http://qiita.com/yushin/items/732043ee23281f19f983

```Bash
$ nohup sleep 5 &
```
標準出力などがある場合はデフォルトで`./nohup.out`というファイルに出力される。
以下のようにファイルを指定すれば、任意のファイルに出力できる。

```Bash
$ nohup echo test > test.txt &
```

## nohupで連続したコマンド群をバックグランド実行
nohupはかっこでくくる系でまとめて実行することはできないので、
shコマンドでまとめて実行する

```Bash
# コマンド上で実行の場合
$ nohup sh -c 'sleep 5; echo done' &

# 先ほどのシェルスクリプトで実行の場合
$ nohup sh test.sh &
```

# 実行中のジョブをバックグラウンドへ
## 
参考：http://kazmax.zpp.jp/linux_beginner/jobs_fg_bg.html

こんなコマンドでフォアグラウンド実行してしまったとする。

```Bash
$ sleep 100
```
これをバックグラウンド実行するときは、
まず、`control+z`で実行中のJobを停止

```Bash
# 実行後、Control+zで停止
$ sleep 100
^Z
[1]+  停止                  sleep 100

# jobsコマンドで後ほど確認もできる
$ jobs
[1]+  停止                  sleep 100

--------------------------------------

# ジョブが複数あった場合はこんな感じになる
$ jobs
[1]   停止                  sleep 100
[2]-  停止                  sleep 90
[3]+  停止                  sleep 80
# []の中身がジョブ番号
# + : カレントジョブ
# - : 前のジョブ

# 停止しているジョブをバックグラウンド実行
# (ジョブ番号を指定しなければ、カレントジョブとなる)
$ bg ${ジョブ番号}
```
この方法だと通常のバックグラウンド実行と同じで、
ログアウトしてしまうとプロセスがkillされてしまう

## 実行中のジョブをログアウト時もバックグラウンド実行する
disownコマンドを利用する

```Bash
# 先ほどのjobの1番を実行
$ bg 1

# 実行状態になっていることを確認
$ jobs
[1]   実行中               sleep 100 &
[2]-  停止                  sleep 90
[3]+  停止                  sleep 80

# disown %${job番号} で自分のjobテーブルから外れて実行される
$ disown %1

# jobsから消える
$ jobs
[2]-  停止                  sleep 90
[3]+  停止                  sleep 80

# プロセスを見ると動作している
$ ps x | grep sleep | grep -v grep
22222 pts/3    S      0:00 sleep 100
22223 pts/3    T      0:00 sleep 90
22224 pts/3    T      0:00 sleep 80
```
※ジョブが停止中の状態でdisownしてしまうと、
ジョブテーブルから除外され、ログアウトしても消えないプロセスが残ってしまうので、手動でkillすること

# シェルスクリプト内でのバックグランド実行と同期待ち
waitコマンドを使うとプロセスの終了をキャッチすることができる。

```Bash
$ wait ${プロセスID}
# 複数の場合は空白区切りでプロセスIDを並べる
# プロセスIDを指定しない場合は、起動したすべてのバックグランドジョブを待つ
```

これを利用すると複数のバックグラウンド実行が終了した後の処理を記述できる。

## 以前に実行したすべてのバックグランドジョブの同期待ち

```Bash:sync.sh
#!/bin/sh
# 一つ目のジョブ
{
  sleep 10
  echo done1
} &

# 2つめのジョブ
{
  sleep 5
  echo done2
} &

# 終了待ち
wait
echo done3
```
実行すると以下のようになって、期待通りの動作をしていることがわかる。

```Bash
$ sh sync.sh
done2
done1
done3
```

## 一部のジョブの同期待ち
プロセスIDを指定してwaitすれば、特定のプロセスのみを同期することができるので、プロセスIDを添え字に使った配列を利用すると簡単

```Bash:part_sync.sh
#!/bin/sh

# プロセスIDの配列
pids=()

# 一つ目のジョブ (同期する)
{
  sleep 10
  echo done1
} &
# 直前のpidを格納
pids[$!]=$!

# 2つめのジョブ (同期しない)
{
  sleep 20
  echo done2
} &

# 3つめのジョブ (同期する)
{
  sleep 5
  echo done3
} &
# 直前のpidを格納
pids[$!]=$!

# 終了待ち
wait ${pids[@]}
echo done4
```

実行結果は期待通り

```Bash
$ sh part_sync.sh
done3
done1
done4
$ done2
```

# 検証環境
検証した環境

```Bash
$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)

$ bash --version
GNU bash, バージョン 4.2.46(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
```