シェルスクリプトって意外と便利

こんにちわ
[グローバルセンス株式会社](https://gb-sense.com)のskanehiraです。

最近、意外と便利だなと感じているので、少しシェル芸に興味を持ち始めました。
今回の記事はシェルスクリプトについて触れていこうと思います。

# **記事のテーマの至るまでの背景**
ここ２ヶ月弱、仕事と資格勉強でなにかと記事を書く気になれず、久しぶりの投稿になってしまいました。
最近はAPI開発がメインで、今の試験フェーズに入っています。

そこで、API試験を久しぶりにシェルスクリプトを使ってシナリオ試験を自動化しています。
久しぶりなので、いろいろと忘れてしまってGoogle先生に頼りながら作っています。

また忘れてしまったときに
・過去に何をやってきたのか
・どんなものを作ったのか
を記録として残しつつ、同じようなことをしようとしている方の役に立てればと思い、
この記事にを書こうと考えました。

Jmeterを使うという手もありますが、
まだあまり理解していないところもあり、別の機会に自分の勉強のため記事を書こうと思います。

# **シェルスクリプトにした理由**
API試験を行う上で単発コールであればシェルを使うまでもないが、
一連のシナリオを行う上で、やはり自動化したほうが良いと思います。

自動化のメリットは
・バグが出た時などの再試験が楽
・バグ再現が楽
・一部手順が違うシナリオをソースを少し変えるだけで簡単につくれるので楽
があるんじゃないかなと思います。

場合によってはシェルスクリプトを作るほうが、
時間が掛かってしまうこともあるかもしれないですが、
そこは試験内容や納期を見て、試験手法を決めれば良いと思います。

# **仮APIを用意**
先日[api server with golang](https://qiita.com/skanehira/items/afc8067136c8b4af021e)の記事で作ったAPIを使います。
簡単なCRUDは一通りできる様になっています。

# **API試験で使うコマンド**

# リクエスト発行  
APIの`POST`、`GET`、`PUT`、`DELETE`試験を行う時は`curl`を使います。  
会員登録機能のAPI試験を実施するとして、curlを使う場合はこんな感じになります。

```shell
# 登録
curl -s -v -X POST -H "Content-type:application/json" -d @data.json http://{host}:{port}/users
# 取得
curl -s -v -X GET http://host:port/users?=id{id}
# 更新
curl -s -v -X PUT -H "Content-type:application/json" -d @data.json http://{host}:{port}/users?id={id}
# 削除
curl -s -v -X DELETE http://{host}:{port}/users?id={id}
```

`curl`を使用して、データをPOST、PUTしたい場合は
   ・ -HでヘッダにContentTypeを指定する
   ・ -dでPOST,PUTするデータを指定する
が必要になります。  

curl意外にも、`postman`や`jmeter`などのAPI試験ツールがあります。
そちらに関してはそのうち記事にする予定です。

----

# リクエストデータの加工  
ここで、仮に複数のユーザを登録する試験を実施するとします。
その場合、動的にPOSTするユーザ情報を書き換える必要があります。
動的にテキストの中身を書き換えるには`sed`コマンドを使います。
流れとしてはこんな感じになります。

1. データテンプレートを用意（template_user.json)
2. 動的に書き換える部分に関しては`{name}`というふうにします。
3. `sed -e "s/{name}/skanehira/" template_user.json > user.json`で動的に登録ユーザデータを作成する

```shell:test.sh
#!/bin/bash

# content-type
ContentType="Content-type:application/json"

# create user
for i in 1 2 3 4
do
  sed -e "s/{name}/skanehira${i}/" -e "s/{password}/neko${i}/" -e "s/{description}/test${i}/" template_user.json > user.json
  curl -s -X POST -H $ContentType -d @user.json http://localhost:8080/users | jq
done
```
若干違うのは、jqにより整形後、idをgrepしているところです。
jsonをしておけばキレイに改行されるので、個別でデータを抜き取りやすいです。

- 実行結果

```shell-session:result
[skanehira@MacBook shell]$ cat template_user.json
{
	"name": "{name}",
	"password": "{password}",
	"description": "{description}"
}
[skanehira@MacBook shell]$
[skanehira@MacBook shell]$ bash test.sh
{
  "id": "e49f0357-1ea4-4317-bd53-f22b3c9dd513",
  "name": "skanehira1",
  "password": "neko1",
  "description": "test1",
  "create_at": "2018-08-08T09:15:09.839949794+09:00",
  "update_at": "2018-08-08T09:15:09.839949794+09:00"
}
{
  "id": "dd1efe09-6b16-4eda-9896-f4286ac40512",
  "name": "skanehira2",
  "password": "neko2",
  "description": "test2",
  "create_at": "2018-08-08T09:15:09.874687633+09:00",
  "update_at": "2018-08-08T09:15:09.874687633+09:00"
}
{
  "id": "7163168f-6930-4561-bf55-58b93568fa65",
  "name": "skanehira3",
  "password": "neko3",
  "description": "test3",
  "create_at": "2018-08-08T09:15:09.907001575+09:00",
  "update_at": "2018-08-08T09:15:09.907001575+09:00"
}
{
  "id": "26ad166e-4644-4669-a283-d1302663de6a",
  "name": "skanehira4",
  "password": "neko4",
  "description": "test4",
  "create_at": "2018-08-08T09:15:09.938044918+09:00",
  "update_at": "2018-08-08T09:15:09.938044918+09:00"
}
[skanehira@MacBook shell]$
```
----

# レスポンスデータの再利用  
レスポンスにはuser idが含まれているので、それを取得して更新する時に使用したりします。
こういう時は`awk`と`grep`と`cut`を使います。

自分では、これくらいしかやり方を思いつかなかったのですが、
他にもっと良いやり方がある方は教えてもらえると嬉しいです。

例えばidを抽出したい時はこんな感じで取ります。

```shell-session
[skanehira@MacBook shell]$ cat response.json
{
  "id": "26ad166e-4644-4669-a283-d1302663de6a",
  "name": "skanehira4",
  "password": "neko4",
  "description": "test4",
  "create_at": "2018-08-08T09:15:09.938044918+09:00",
  "update_at": "2018-08-08T09:15:09.938044918+09:00"
}
[skanehira@MacBook shell]$ cat response.json | grep id | awk '{print $2}' | cut -c 2-36
26ad166e-4644-4669-a283-d1302663de6
```

`grep`で`id`文字列の行を取得して、
`awk`でデータ本体の列だけ取得して、
`cut`で不要の`"`や","をカットします。

正規表現で一発で取ってこれないかなと`awk`で試してみましたが、
うまくいきませんでした。。

ひとまずidはこの様に取得できるので、それ使ってユーザ情報を更新します。
こんな感じになります。

```shell:test.sh
#!/bin/bash

# content-type
ContentType="Content-type:application/json"

# generate post data
sed -e "s/{name}/skanehira/" -e "s/{password}/neko/" -e "s/{description}/test/" template_user.json > user.json

# create user
echo "##### create user"
response=$(curl -s -X POST -H $ContentType -d @user.json http://localhost:8080/users)
echo $response | jq

# get id
id=`echo $response | jq . | grep id | awk '{print $2}' | cut -c 2-37`

# generate new post data
sed -e "s/{name}/PP/" -e "s/{password}/AP/" -e "s/{description}/PPAP/" template_user.json > user.json

# update user
echo "##### update user"
curl -s -X PUT -H $ContentType -d @user.json http://localhost:8080/users?id=$id | jq
```

- 実行結果

```shell
[skanehira@MacBook shell]$ bash test.sh
##### create user
{
  "id": "eaf4ab27-2103-43e9-8bb8-7ccc18461754",
  "name": "skanehira",
  "password": "neko",
  "description": "test",
  "create_at": "2018-08-09T06:25:28.680607454+09:00",
  "update_at": "2018-08-09T06:25:28.680607454+09:00"
}
##### update user
{
  "id": "eaf4ab27-2103-43e9-8bb8-7ccc18461754",
  "name": "PP",
  "password": "AP",
  "description": "PPAP",
  "create_at": "2018-08-09T06:25:28.738708898+09:00",
  "update_at": "2018-08-09T06:25:28.738708898+09:00"
}
[skanehira@MacBook shell]$
```

----

登録、更新と来たら次は削除です。
１行追加するだけですが、こんな感じになります。

```shell:test.sh
#!/bin/bash

# content-type
ContentType="Content-type:application/json"

# generate post data
sed -e "s/{name}/skanehira/" -e "s/{password}/neko/" -e "s/{description}/test/" template_user.json > user.json

# create user
echo "##### create user"
response=$(curl -s -X POST -H $ContentType -d @user.json http://localhost:8080/users)
echo $response | jq

# get id
id=`echo $response | jq . | grep id | awk '{print $2}' | cut -c 2-37`

# generate new post data
sed -e "s/{name}/PP/" -e "s/{password}/AP/" -e "s/{description}/PPAP/" template_user.json > user.json

# update user
echo "##### update user"
curl -s -X PUT -H $ContentType -d @user.json http://localhost:8080/users?id=$id | jq

# delete user
echo "##### delete user"
curl -s -X DELETE http://localhost:8080/users?id=$id | jq

# get user
echo "##### get user id=[$id]"
curl -s http://localhost:8080/users?id=$id | jq
```

- 実行結果

```shell
[skanehira@MacBook shell]$ bash test.sh
##### create user
{
  "id": "4baf95ec-57a8-46ff-954e-e502029d4ea3",
  "name": "skanehira",
  "password": "neko",
  "description": "test",
  "create_at": "2018-08-09T08:22:00.629723699+09:00",
  "update_at": "2018-08-09T08:22:00.629723699+09:00"
}
##### update user
{
  "id": "4baf95ec-57a8-46ff-954e-e502029d4ea3",
  "name": "PP",
  "password": "AP",
  "description": "PPAP",
  "create_at": "2018-08-09T08:22:00.68009226+09:00",
  "update_at": "2018-08-09T08:22:00.68009226+09:00"
}
##### delete user
{
  "code": 200,
  "message": "Success delete user=[4baf95ec-57a8-46ff-954e-e502029d4ea3]"
}
##### get user id=[4baf95ec-57a8-46ff-954e-e502029d4ea3]
{
  "code": 404,
  "message": "not found user"
}
[skanehira@MacBook shell]$
```

削除後、idを指定して取得すると404が帰ってきているのは確認できますね。

# **まとめ**
改めてまとめるとこんな感じになります。　

| コマンド | 用途 |
|:-:|:-:|
| curl  | API試験の主観となるコマンド。リクエスト発行を行う |
| sed  | 主にPOST，PUTデータの加工時に使用する  |
| awk  | 主にレスポンスデータの行・列のデータを取得時に使用する  |
| grep  | 主にレスポンスデータの特定文字列を含む行を取得時に使用する  |
| cut  | 主にレスポンスデータの特定文字取得時に使用する  |

それぞれ大したことはしていないですが、
これらのコマンドを使用すれば、主にシナリオ試験で効率をあげられると思います。（試験内容などによる）

# **最後に**
開発とテスト・試験はセットなので、
こういった小技で少しでも試験を効率よくできればと思います。
この記事で紹介した内容はを知らないって方の役に立てればと思っています。

自分も、まだまだ精進しないといけないことが多々あるので、
これからも頑張って吸収していきますっ