tsコマンドの使い方

#1 tsコマンドとは?
tsとはtask spoolerの略です。
コマンドをシリアライズに実行したいときに使うコマンドです。
たとえば、コマンドA、コマンドBを実行するとき、コマンドAの実行が完了したら
コマンドBを実行することができるようになります。

1つのターミナルで`# command A && command B`と実行するのではありません。
2つのターミナルでそれぞれ、`# command A`、`# command B`を実行する場合に使用します。

なお、CentOSでは、本コマンドのパッケージがリポジトリに存在しないようです。
商用目的で使うには難しいかもしれません。

Ubuntuでは、下記manページで紹介されています。
http://manpages.ubuntu.com/manpages/eoan/man1/tsp.1.html


#2 環境
VMware Workstation 14 Player上の仮想マシンを使いました。
仮想マシンのOS版数は以下のとりです。

```console:OS版数
[root@server ~]# cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)

[root@server ~]# uname -r
3.10.0-957.el7.x86_64
```

#3 コマンドのインストール
下記URLから`ts-1.0.tar.gz`をダウンロードします。
Red Hat GPGのようにファイルの信頼性が検証できないので、ちょっと不安ですが
個人利用の仮想マシンなのでインストールしてみました。

```console:tar.gzファイルのダウンロード
[root@server ~]# wget http://vicerveza.homeunix.net/~viric/soft/ts/ts-1.0.tar.gz

[root@server ~]# ls ts-1.0.tar.gz
ts-1.0.tar.gz
```

ダウンロードしたファイルを解凍します。

```console:tar.gzファイルの解凍
[root@server ~]# tar xvf ts-1.0.tar.gz
```

`ts-1.0`ディレクトリに移動します。

```console:tsディレクトリに移動する。
[root@server ~]# cd ts-1.0/
[root@server ts-1.0]# ls
COPYING     PORTABILITY  buglist.bug  env.c      info.c  main.c   msgdump.c  server_start.c  testbench.sh
Changelog   PROTOCOL     client.c     error.c    jobs.c  main.h   mymail.sh  setenv          ts.1
Makefile    README       deb          execute.c  list.c  makedeb  print.c    signals.c       ttail.c
OBJECTIVES  TRICKS       debian-pkg   gentoo     mail.c  msg.c    server.c   tail.c          web
```

READMEにしたがって、`make`コマンドを実行します。

```console:makeを実行する。
[root@server ts-1.0]# make
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c main.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c server.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c server_start.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c client.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c msgdump.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c jobs.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c execute.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c msg.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c mail.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c error.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c signals.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c list.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c print.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c info.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c env.c
cc -pedantic -ansi -Wall -g -O0 -D_XOPEN_SOURCE=500 -D__STRICT_ANSI__ -c tail.c
cc  -o ts main.o server.o server_start.o client.o msgdump.o jobs.o execute.o msg.o mail.o error.o signals.o list.o print.o info.o env.o tail.o
```

`make install`を実行して、tsコマンドを`/usr/local/bin`にインストールします。

```console:実行ファイルのインストール
[root@server ts-1.0]# make install
install -c -d /usr/local/bin
install -c ts /usr/local/bin
install -c -d /usr/local/share/man/man1
install -c -m 644 ts.1 /usr/local/share/man/man1
```

tsコマンドのインストール先ディレクトリを確認します。
`/usr/local/bin/ts`にインストールされていることがわかります。

```console:インストール先の確認
[root@server ts-1.0]# which ts
/usr/local/bin/ts
```

```console:版数の確認
[root@server ts-1.0]# ts -V
Task Spooler v1.0 - a task queue system for the unix user.
Copyright (C) 2007-2016  Lluis Batlle i Rossell
```


#4 オプション一覧

```
[root@server ~]# ts -h
usage: ts [action] [-ngfmdE] [-L <lab>] [-D <id>] [cmd...]
Env vars:
  TS_SOCKET  the path to the unix socket used by the ts command.
  TS_MAILTO  where to mail the result (on -m). Local user by default.
  TS_MAXFINISHED  maximum finished jobs in the queue.
  TS_MAXCONN  maximum number of ts connections at once.
  TS_ONFINISH  binary called on job end (passes jobid, error, outfile, command).
  TS_ENV  command called on enqueue. Its output determines the job information.
  TS_SAVELIST  filename which will store the list, if the server dies.
  TS_SLOTS   amount of jobs which can run at once, read on server start.
  TMPDIR     directory where to place the output files and the default socket.
Actions:
  -K       kill the task spooler server
  -C       clear the list of finished jobs
  -l       show the job list (default action)
  -S [num] get/set the number of max simultaneous jobs of the server.
  -t [id]  "tail -n 10 -f" the output of the job. Last run if not specified.
  -c [id]  like -t, but shows all the lines. Last run if not specified.
  -p [id]  show the pid of the job. Last run if not specified.
  -o [id]  show the output file. Of last job run, if not specified.
  -i [id]  show job information. Of last job run, if not specified.
  -s [id]  show the job state. Of the last added, if not specified.
  -r [id]  remove a job. The last added, if not specified.
  -w [id]  wait for a job. The last added, if not specified.
  -k [id]  send SIGTERM to the job process group. The last run, if not specified.
  -u [id]  put that job first. The last added, if not specified.
  -U <id-id>  swap two jobs in the queue.
  -B       in case of full queue on the server, quit (2) instead of waiting.
  -h       show this help
  -V       show the program version
Options adding jobs:
  -n       don't store the output of the command.
  -E       Keep stderr apart, in a name like the output file, but adding '.e'.
  -g       gzip the stored output (if not -n).
  -f       don't fork into background.
  -m       send the output by e-mail (uses sendmail).
  -d       the job will be run only if the job before ends well
  -D <id>  the job will be run only if the job of given id ends well.
  -L <lab> name this task with a label, to be distinguished on listing.
  -N <num> number of slots required by the job (1 default).
```

#5 事前準備
tsコマンドの動作を確認するため、テスト用のスクリプトを作成します。

##5.1 スクリプト作成

/var/log/messageにメッセージを出力するスクリプトを作成します。
PIDとシーケンス番号を1秒間隔で10秒間出力します。

```console:テスト用シェルスクリプト
[root@server ~]# cat tp.sh
#!/usr/bin/bash

NUM_MAX=10
i=0

while [ "$i" -lt ${NUM_MAX} ]
do
  logger "$1 (PID=$$,NUM=$i)"
  sleep 1
  i=$((i+1))
done
```

```console:実行権の付与
[root@server ~]# chmod 744 tp.sh
```


##5.2 スクリプトの動作確認
スクリプトが出力するログを確認するため、journalctlコマンドを実行します。
なお、journalctlコマンドの使い方は、[ここ(journalctl コマンドの使い方)](https://qiita.com/hana_shin/items/96095571b7bf1b721255)を参照してください。

```console:journalctl実行
[root@server ~]# journalctl -f
```

もう1つターミナルを開きます。
スクリプトは、引数に`first`という文字列を指定して実行します。

```
[root@server ~]# ./tp.sh first
```

もう1つターミナルを開きます。
スクリプトは、引数に`second`という文字列を指定して実行します。

```
[root@server ~]# ./tp.sh second
```

/var/log/messageを確認すると、最初に実行したスクリプトと、
次に実行したスクリプトの実行結果が入り混じっていることがわかります。

```console:実行結果
[root@server ~]# journalctl -f
10月 22 14:49:51 server root[5427]: first (PID=5426,NUM=0)
10月 22 14:49:52 server root[5429]: first (PID=5426,NUM=1)
10月 22 14:49:52 server root[5432]: second (PID=5431,NUM=0)
10月 22 14:49:53 server root[5434]: first (PID=5426,NUM=2)
10月 22 14:49:53 server root[5436]: second (PID=5431,NUM=1)
10月 22 14:49:54 server root[5438]: first (PID=5426,NUM=3)
10月 22 14:49:54 server root[5440]: second (PID=5431,NUM=2)
-以下、略-
```
tsコマンドを使うと、firstの出力が終わってから、secondの出力を開始できるようになります。
つまり、コマンドをシリアライズに実行することができるようになります。






#6 ジョブ一覧を確認する方法(-l)
ジョブの一覧を確認してみます。
ジョブがなにも実行されていないことがわかります。

```
[root@server ~]# ts -l
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
```


#7 ジョブをシリアライズに実行する方法
スクリプトが出力するログを確認するため、journalctlを実行します。

```
[root@server ~]# journalctl -f
```

もう1つターミナルを開きます。
引数に`first`を指定してクリプトを実行します。以降、`first`のジョブと呼びます。
ジョブのIDが0であることを示しています。

```
[root@server ~]# ts ./tp.sh first
0
```

引数に`second`を指定してクリプトを実行します。以降、`second`のジョブと呼びます。
ジョブのIDが1であることを示しています。

```
[root@server ~]# ts ./tp.sh second
1
```


ジョブ一覧を確認してみます。
firstのジョブは`running`となっていて、実行中であることがわかります。
secondのジョブは`queued`となっていて、実行されるのを待っています。

```
[root@server ~]# ts -l
ID   State      Output               E-Level  Times(r/u/s)   Command [run=1/1]
0    running    /tmp/ts-out.Ihj37A                           ./tp.sh first
1    queued     (file)                                       ./tp.sh second
```

/var/log/messageを確認します。
`first`のジョブの出力が終わってから、`second`のジョブの出力が開始されています。
つまり、`first`と`second`はシリアライズに実行されていることがわかります。

```
[root@server ~]# journalctl -f
10月 22 14:52:07 server root[5480]: first (PID=5479,NUM=0)
10月 22 14:52:08 server root[5484]: first (PID=5479,NUM=1)
10月 22 14:52:09 server root[5486]: first (PID=5479,NUM=2)
10月 22 14:52:10 server root[5489]: first (PID=5479,NUM=3)
10月 22 14:52:11 server root[5491]: first (PID=5479,NUM=4)
10月 22 14:52:12 server root[5493]: first (PID=5479,NUM=5)
10月 22 14:52:13 server root[5495]: first (PID=5479,NUM=6)
10月 22 14:52:14 server root[5497]: first (PID=5479,NUM=7)
10月 22 14:52:15 server root[5499]: first (PID=5479,NUM=8)
10月 22 14:52:17 server root[5501]: first (PID=5479,NUM=9)
10月 22 14:52:18 server root[5504]: second (PID=5503,NUM=0)
10月 22 14:52:19 server root[5507]: second (PID=5503,NUM=1)
10月 22 14:52:20 server root[5509]: second (PID=5503,NUM=2)
10月 22 14:52:21 server root[5511]: second (PID=5503,NUM=3)
10月 22 14:52:22 server root[5514]: second (PID=5503,NUM=4)
10月 22 14:52:23 server root[5516]: second (PID=5503,NUM=5)
10月 22 14:52:24 server root[5518]: second (PID=5503,NUM=6)
10月 22 14:52:25 server root[5521]: second (PID=5503,NUM=7)
10月 22 14:52:26 server root[5523]: second (PID=5503,NUM=8)
10月 22 14:52:27 server root[5525]: second (PID=5503,NUM=9)
```


#8 ジョブの状態を確認する方法(-s)
ジョブの状態を表示してみます。

`first`のジョブ(ジョブID=0)は終了していることがわかります。

```console:ジョブID=0の状態表示
[root@server ~]# ts -s 0
finished
```

`second`のジョブ(ジョブID=1)も終了していることがわかります。

```console:ジョブID=0の状態表示
[root@server ~]# ts -s 1
finished
```


#9 ジョブの詳細情報を確認する方法(-i)
ジョブの詳細情報を表示してみます。
`first`のジョブをキューに入れた時刻、実行時刻、終了時刻、実行時間が
表示されることがわかります。

```console:ジョブID=0の詳細情報
[root@server ~]# ts -i 0
Exit status: died with exit code 0
Command: ./tp.sh first
Slots required: 1
Enqueue time: Tue Oct 22 14:52:07 2019
Start time: Tue Oct 22 14:52:07 2019
End time: Tue Oct 22 14:52:18 2019
Time run: 10.103129s
```

`second`のジョブは、`first`が終了したあとに実行されていることがわかります。
実行時間が約10秒であることがわかります。

```console:ジョブID=1の詳細情報
[root@server ~]# ts -i 1
Exit status: died with exit code 0
Command: ./tp.sh second
Slots required: 1
Enqueue time: Tue Oct 22 14:52:08 2019
Start time: Tue Oct 22 14:52:18 2019
End time: Tue Oct 22 14:52:28 2019
Time run: 10.071855s
```

#10 ジョブのPIDを確認する方法(-p)

`first`のジョブのPIDが5479であることがわかります。

```console:ジョブID=0のPID
[root@server ~]# ts -p 0
5479
```

`second`のジョブのPIDが5503であることがわかります。

```console:ジョブID=1のPID
[root@server ~]# ts -p 1
5503
```


#11 ジョブの実行結果を保存しない方法(-n)
tsコマンドを実行すると、標準出力、標準エラーが/tmp配下に保存されます。
`-n`を指定することで、標準出力、標準エラーが/tmp配下に保存されなくなります。

```cosnole:-nオプションなしの場合
[root@server ~]# ls /tmp/ts-out.*
/tmp/ts-out.5bTNSj  /tmp/ts-out.FI8k3S  /tmp/ts-out.KvdsB5  /tmp/ts-out.T7y4HC  /tmp/ts-out.mQCsbR
/tmp/ts-out.7ErZth  /tmp/ts-out.FSxedU  /tmp/ts-out.NenwCj  /tmp/ts-out.VX9FX0  /tmp/ts-out.nirV9p
```

`-n`オプションを指定してtsコマンドを実行します。

```console:実行結果をファイルの保存しない場合
[root@server ~]# ts -n ./tp.sh first
[root@server ~]# ts -n ./tp.sh second
```

`-n`オプションを指定すると、tsコマンドの実行結果が標準出力に出力されます。

```
[root@server ~]# ts -l
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
2    finished   stdout               0        10.05/0.02/0.01 ./tp.sh first
3    finished   stdout               0        10.06/0.01/0.02 ./tp.sh second
```


#12 同時実行のジョブ数を変更する方法(-S)
ここでは、同時に実行可能なジョブ数を1(default)から2に変更してみます。

たとえば、ジョブA,ジョブB,ジョブCと実行したとき、
ジョブAとジョブBは同時（パラレル）に実行されますが、
ジョブCは、ジョブA,Bが終了したあとに実行されます。

##12.1 同時に実行可能なジョブ数を確認する方法
同時に実行可能なジョブ数はデフォルトで1つです。

```console:同時実行可能なジョブ数の確認
[root@server ~]# ts -S
1
```

##12.2 同時に実行可能なジョブ数を変更する方法


```console:同時実行可能なジョブ数の変更
[root@server ~]# ts -S 2
[root@server ~]# ts -S
2
```


`first`,`second`,`third`の3つのジョブを同時に実行してみます。

```
[root@server ~]# ts -n ./tp.sh first; ts -n ./tp.sh second; ts -n ./tp.sh third
```

/var/log/messageを確認すると、途中までは、`first`と`second`がパラレルに
実行されていることがわかります。`first`と`second`が終了すると、`third`の
実行が開始されていることがわかります。

```
[root@server ~]# journalctl -f
10月 22 16:20:04 server root[5875]: first (PID=5869,NUM=0)
10月 22 16:20:04 server root[5876]: second (PID=5873,NUM=0)
10月 22 16:20:05 server root[5879]: first (PID=5869,NUM=1)
10月 22 16:20:05 server root[5880]: second (PID=5873,NUM=1)
10月 22 16:20:06 server root[5884]: first (PID=5869,NUM=2)
10月 22 16:20:06 server root[5883]: second (PID=5873,NUM=2)
10月 22 16:20:07 server root[5888]: second (PID=5873,NUM=3)
10月 22 16:20:07 server root[5887]: first (PID=5869,NUM=3)
10月 22 16:20:08 server root[5891]: second (PID=5873,NUM=4)
10月 22 16:20:08 server root[5892]: first (PID=5869,NUM=4)
10月 22 16:20:09 server root[5895]: second (PID=5873,NUM=5)
10月 22 16:20:09 server root[5896]: first (PID=5869,NUM=5)
10月 22 16:20:10 server root[5900]: second (PID=5873,NUM=6)
10月 22 16:20:10 server root[5899]: first (PID=5869,NUM=6)
10月 22 16:20:11 server root[5903]: first (PID=5869,NUM=7)
10月 22 16:20:11 server root[5904]: second (PID=5873,NUM=7)
10月 22 16:20:12 server root[5907]: first (PID=5869,NUM=8)
10月 22 16:20:12 server root[5908]: second (PID=5873,NUM=8)
10月 22 16:20:13 server root[5912]: second (PID=5873,NUM=9)
10月 22 16:20:13 server root[5911]: first (PID=5869,NUM=9)
10月 22 16:20:14 server root[5916]: third (PID=5915,NUM=0)
10月 22 16:20:15 server root[5918]: third (PID=5915,NUM=1)
10月 22 16:20:16 server root[5920]: third (PID=5915,NUM=2)
10月 22 16:20:17 server root[5923]: third (PID=5915,NUM=3)
10月 22 16:20:18 server root[5925]: third (PID=5915,NUM=4)
10月 22 16:20:19 server root[5927]: third (PID=5915,NUM=5)
10月 22 16:20:20 server root[5929]: third (PID=5915,NUM=6)
10月 22 16:20:21 server root[5931]: third (PID=5915,NUM=7)
10月 22 16:20:22 server root[5933]: third (PID=5915,NUM=8)
10月 22 16:20:23 server root[5935]: third (PID=5915,NUM=9)
```



#13 実行結果をクリアする方法(-C)
ジョブ一覧を確認します。
`first`および`second`が`finished`の状態になっています。
つまり、実行が完了していることがわかります。

```console:実行結果の確認
[root@server ~]# ts -l
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
0    finished   /tmp/ts-out.FSxedU   0        10.05/0.01/0.02 ./tp.sh first
1    finished   /tmp/ts-out.nirV9p   0        10.06/0.00/0.02 ./tp.sh second
```

ジョブの実行結果をクリアします。
実行結果がクリアされていることがわかります。

```console:実行結果のクリア
[root@server ~]# ts -C
[root@server ~]# ts -l
ID   State      Output               E-Level  Times(r/u/s)   Command [run=0/1]
```




#Z 参考情報
[cron + α が欲しい時には ts(Task Spooler) のご利用をご検討下さいというメモ](https://cloudpack.media/23287)
[時間のかかる複数のコマンドをキューに突っ込んで処理させる『Task Spooler』を試してみる](https://orebibou.com/2018/06/%E6%99%82%E9%96%93%E3%81%AE%E3%81%8B%E3%81%8B%E3%82%8B%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%AB%E7%AA%81%E3%81%A3%E8%BE%BC%E3%82%93/)
[簡単なキュー登録による複数タスクのバッチ処理を可能にするTask Spooler](https://mag.osdn.jp/09/01/20/0150258)
[Queuing tasks for batch execution with Task Spooler](http://vicerveza.homeunix.net/~viric/soft/ts/article_linux_com.html)