【MacOS】シェルスクリプトでミリ秒単位まで取得する方法４種紹介 (gdate, python, perl, node.js)

# 概要

MacOSにてシェルスクリプトを使ってAPIコールを行うと以下のように出力されます（`curl`コマンドの進行状況）。
この時、TimeSpentがミリ秒で欲しいなと思い、シェルスクリプトで表示されるように実装しましたので紹介します。


```
$ sh ./api_request_xxx.sh http://xxxx/api/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 62083  100 60043  100  2040   4315    146  0:00:13  0:00:13 --:--:-- 16347
```

全部で4種類あります。


# 前提

以下みたいにすれば取得できるんじゃ？と思っていましたが、これではできませんした。

```sh
START_TIME=$(date +%s%3N)
```
↓

```
17095433963N: value too great for base (error token is "17095433963N")
```

MacOSの`date`コマンドでは、ミリ秒以下のタイムスタンプをサポートしていないようです。

```
date +'%H:%M:%S.%3N'
```
↓

```
18:13:16.3N: syntax error in expression (error token is ":13:16.3N")
```

こちらの記事にも同様のことが記載ありました。

https://dev.classmethod.jp/articles/macos-date-millisecond/

上記記事のように`coreutils`の`gdate`を利用する、という方法もあるので、
こちらも含めて4つ紹介します。

## 1. `coreutils`の`gdate`を利用する方法

上述の記事の通り、まずは`brew`を使用して`coreutils`をインストールします。

```
brew install coreutils
```

```sh
# リクエスト前の時間を取得（ミリ秒単位）
START_TIME=$(gdate +%s%3N)

# 
# APIリクエストのヘッダーやボディを記載
# 

# リクエスト後の時間を取得（ミリ秒単位）
END_TIME=$(gdate +%s%3N)

# 差分を計算して表示
TIME_SPENT=$((END_TIME - START_TIME))
echo "Time Spent: $TIME_SPENT ms"
```

結果、以下のように出力されます。

```
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 63341  100 61300  100  2041   4429    147  0:00:13  0:00:13 --:--:-- 13352
Time Spent: 13872 ms
```

ミリセカンドまで出たー！

`gdate +%s%3N`を使用して現在の時間をミリ秒単位で取得しています。
（`%s`はエポック秒、`%3N`はミリ秒）



## 2. Pythonを利用する方法

```sh
# リクエスト前の時間を取得（ミリ秒単位）
START_TIME=$(python -c 'import time; print(int(time.time() * 1000))')

# 
# APIリクエストのヘッダーやボディを記載
# 

# リクエスト後の時間を取得（ミリ秒単位）
END_TIME=$(python -c 'import time; print(int(time.time() * 1000))')

# 差分を計算して表示
TIME_SPENT=$((END_TIME - START_TIME))
echo "Time Spent: $TIME_SPENT ms"
```

結果、以下のように出力されます。

```
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 62083  100 60043  100  2040   4315    146  0:00:13  0:00:13 --:--:-- 16347
Time Spent: 13944 ms
```

こちらでもミリ秒単位まで出力することができました。


## 3. `perl`を利用する方法

https://pslabo.hatenablog.com/entry/2021/02/21/180358

こちらの記事には`perl`の`Time::HiRes`モジュールを使用して現在の時間をミリ秒単位で取得していました。

```sh
# リクエスト前の時間を取得（ミリ秒単位）
START_TIME=$(perl -MTime::HiRes -e 'printf("%.0f\n",Time::HiRes::time()*1000)')

# 
# APIリクエストのヘッダーやボディを記載
# 

# リクエスト後の時間を取得（ミリ秒単位）
END_TIME=$(perl -MTime::HiRes -e 'printf("%.0f\n",Time::HiRes::time()*1000)')

# 差分を計算して表示
TIME_SPENT=$((END_TIME - START_TIME))
echo "Time Spent: $TIME_SPENT ms"
```

私も上記のように実装しましたが、ミリ秒単位で出力されました。
Perlは多くのUnix系OSにデフォルトでインストールされているはずので、これも結構楽かも？

`Time::HiRes::time()`が現在のエポック秒。
（`printf("%.0f\n", ...)`を使用して、浮動小数点数を整数に丸めています）

## 4.`Node.js`を利用する方法

`Node.js`はサーバーサイドでのJavaScriptの実行を可能にするプラットフォーム。
以下の方法でも可能ですが`Node.js`がインストールされている必要があります（私は以前インストール済みだった）。

```sh
# リクエスト前の時間を取得（ミリ秒単位）
START_TIME=$(node -e "console.log(Date.now())")

# 
# APIリクエストのヘッダーやボディを記載
# 

# リクエスト後の時間を取得（ミリ秒単位）
END_TIME=$(node -e "console.log(Date.now())")

# 差分を計算して表示
TIME_SPENT=$((END_TIME - START_TIME))
echo "Time Spent: $TIME_SPENT ms"
```

`Node.js`の`Date.now()`メソッドを使用して現在の時間をミリ秒単位で取得しています。
`Date.now()`が現在のエポックミリ秒。