SQLite3データベースのレコードをCSVファイル保存するシェルスクリプトを作る

#### Raspberry Pi zero上の気象データベース(SQLite3)のレコードをCSVファイルに保存するシェルスクリプトの作成方法を紹介します。

Raspberry Pi ZeroはヘッドレスOSで極めて性能が低いため、データベースに記録したデータを可視化することはできません。 

**いずれは記録した気象データをより性能の高いサーバー機にデータ移行する必要があります。**

今回の記事は下記システムの中の **【(1)新規インストールとデータベース作成】(3)のCSVファイル保存スクリプトになります。移行先のターゲットはより性能の高いRaspberry Pi 4を想定しています。**

![MigradeSQLite_systemOverview.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/92faf363-7414-c272-487b-48dbef49879e.jpeg)

Raspberry Pi 4へのインポート方法の詳細は下記GitHubリポジトリでご覧になれます
[GitHub(pipito-yukio) UDP Weather Sensor packet monitor for Raspberry pi 4](https://github.com/pipito-yukio/raspi4_apps "raspi4_apps")

### 参考書について  

![books_shell_script.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583120/21ed2c17-f804-7049-2ec1-6b98bffa233b.jpeg)

+ **「シェルスクリプト+データベース　活用テクニック」**  
[ISBN] 978-4-87783-385-5
[著者] 広瀬　雄二
[発行日] 2016年5月1日 初版第1刷 発行  
[出版社] 株式会社　カットシステム  
[私の書評]  
**SQLite3データベースに関しては和書では一番詳しいと思います。シェルスクリプトも内容が非常に濃い。どちらかというとマニアックに近いですが一冊持っていても損なしです。**  
+ **「新しいシェルプログラミングの教科書」**  
[ISBN] 978-4-7973-9310-1  
[著者] 三宅　英明  
[発行日] 2017年10月30日 初版第1刷 発行  
[出版社] SB クリエイティブ株式会社  
[私の書評]  
**定番のシェルスクリプトの良書。シェルスクリプトを勉強するならこの一冊。ラズパイ運用機で使っているシェルスクリプトはこの本をヒントに作成したものです。**

### 想定するスクリプトの実行環境
+ OS: Debian GNU/Linux **※Raspberry Pi 4 Model B に移行**
+ SQLite3ライブラリがシステムにインストールされていること

### スクリプトを作成した環境
+ OS: Ubuntu 22.04.3 LTS
+ shell: **bshスクリプトが実行できること**
+ SQLite3ライブラリインストール済み

```bash
$ sqlite3 -version
3.37.2 2022-01-06 13:25:41 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5dalt1
```

### テーブル定義
```SQL
CREATE TABLE IF NOT EXISTS t_device(
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name VARCHAR UNIQUE NOT NULL
);

CREATE TABLE IF NOT EXISTS t_weather(
    did INTEGER NOT NULL,
    measurement_time INTEGER NOT NULL,
    temp_out real,
    temp_in real,
    humid real,
    pressure real,
    PRIMARY KEY (did, measurement_time),
    FOREIGN KEY (did) REFERENCES t_devices (id) ON DELETE CASCADE
);
```


### 1. コマンドラインでCSV出力方法を確認する

#### 1-1. ヘルプを確認  
※必要な箇所のみ抜粋します

```bash
$ sqlite3 --help
Usage: sqlite3 [OPTIONS] FILENAME [SQL]
FILENAME is the name of an SQLite database. A new database is created
if the file does not previously exist.
OPTIONS include:
...中略...
   -cmd COMMAND         run "COMMAND" before reading stdin
   -csv                 set output mode to 'csv'
...中略...
   -[no]header          turn headers on or off
...中略...
   -quote               set output mode to 'quote'
...中略...
   -tabs                set output mode to 'tabs'
   -version             show SQLite version
...以下詳細...
```

#### 1-2. 標準のレコード出力  
**(A) パイプラインから"SELECT文"を受け取る ※今回はスクリプトが作りやすいこちらの方法を採用**
(B) 引数で"SELECT文"を受け取る
```bash
# (A)
$ echo "SELECT * FROM t_device" | sqlite3 weather.db
1|esp8266_1
# (B)
$ sqlite3 weather.db "SELECT * FROM t_device;"
1|esp8266_1
```

#### 1-3. 区切りを指定したレコード出力  
(A) カンマ区切り: **-csv**  
(B) TAB区切り: **-tabs**

```bash
# (A) カンマ区切り
$ echo "SELECT * FROM t_device" | sqlite3 weather.db -csv
1,esp8266_1
# (B) TAG区切り
$ echo "SELECT * FROM t_device" | sqlite3 weather.db -tabs
1	esp8266_1
```

#### 1-4. ヘッダー出力とカンマ区切り  

```bash
$ echo "SELECT * FROM t_device" | sqlite3 weather.db -header -csv
id,name
1,esp8266_1
```

#### 1-5. ヘッダーをクォートしカンマ区切り  
※ **-csv を一緒に指定すると -quote の効果が打ち消されます**
```bash
$ echo "SELECT * FROM t_device" | sqlite3 weather.db -header -quote
'id','name'
1,'esp8266_1'
```

**ヘッダーの出力がイマイチなのでsqlite3ではヘッダーを出力せずシェルスクリプトで出力します。**

#### 1-6. レコードの検索条件を指定する  
サンプルとして気象データ検索を１ライナーで実行  
検索条件: デバイス名="esp8266_1", 検索開始日="2023-11-05"
```bash
$ echo "SELECT did,datetime(measurement_time,'unixepoch','localtime'),temp_out,temp_in,humid,pressure FROM t_weather WHERE did=1 AND datetime(measurement_time,'unixepoch','localtime') >= '2023-11-05' ORDER BY measurement_time;" | sqlite3 weather.db -csv
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```
**※１ライナーのクエリーをシェリスクリプトにするのもメンテナンスの欠けます。**  

#### 1-7. クエリーファイルから実行する  
下記のように改行したクエリーを用意します。
```sql
-- output_weather_csv.sql
SELECT 
   did,datetime(measurement_time,'unixepoch','localtime')
   ,temp_out,temp_in,humid,pressure
FROM
   t_weather
WHERE 
   did=(SELECT id FROM t_device WHERE name='esp8266_1') 
   AND
   datetime(measurement_time,'unixepoch','localtime') >= '2023-11-05'
ORDER BY measurement_time
```

**catコマンド**で検索クエリーをパイプラインで sqlite3に渡します

```bash
$ cat output_weather_csv.sql | sqlite3 weather.db -csv
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```
シェルスクリプトでは上記の**クエリー部分をヒアドキュメントに置き換えます。**

### 2. シェルスクリプトを作成する

下記のようにシェルスクリプトファイルを作成し、スクリプトに実行権限を付与します

```bash
$ touch get_csv_from_weather_1.sh
$ chmod +x get_csv_from_weather_1.sh 
```
あとは **vim エディタ** か **gedit** コマンドでスクリプトを編集します


#### 2-1. 引数のない簡単なスクリプト  

**ファイルの cat がヒアドキュメントに置き換わっただけです**

[ファイル] get_csv_from_weather_1.sh

```bash
#!/bin/bash

cat<<-EOF | sqlite3 weather.db -csv
   SELECT 
      did,datetime(measurement_time,'unixepoch','localtime')
      ,temp_out,temp_in,humid,pressure
   FROM
      t_weather
   WHERE 
      did=(SELECT id FROM t_device WHERE name='esp8266_1') 
      AND
      datetime(measurement_time,'unixepoch','localtime') >= '2023-11-05'
ORDER BY measurement_time
EOF
```

実行結果  

```bash
$ ./get_csv_from_weather_1.sh 
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-2. 引数を取るスクリプト  

[ファイル] get_csv_from_weather_2.sh  
**[位置引数] デバイス名 (最初), 検索開始日 (２番目) ISO8601形式**

```bash
#!/bin/bash

cat<<-EOF | sqlite3 weather.db -csv
   SELECT 
      did,datetime(measurement_time,'unixepoch','localtime')
      ,temp_out,temp_in,humid,pressure
   FROM
      t_weather
   WHERE 
      did=(SELECT id FROM t_device WHERE name="${1}") 
      AND
      datetime(measurement_time,'unixepoch','localtime') >= "${2}"
ORDER BY measurement_time
EOF
```

実行結果

```bash
$ ./get_csv_from_weather_2.sh esp8266_1 2023-11-05
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-3. スクリプトの標準出力をファイルにリダイレクト  

標準出力をファイルにリダイレクトするとCSVファイルとして保存できます

```bash
$ ./get_csv_from_weather_2.sh esp8266_1 2023-11-05 > weather_2023-11-05.csv
$ tail weather_2023-11-05.csv 
1,"2023-11-05 10:19:31",9.3,16.9,47.2,1027.1
1,"2023-11-05 10:29:16",9.6,17.0,47.1,1027.0
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-4. CSV出力処理をシェル関数として定義する

複数のテーブルがあるといちいちその部分を書き換えるので大変なのでシェル関数にします。

[ファイル] get_csv_from_weather_3.sh  

```bash
#!/bin/bash

# ./get_csv_from_weather_3.sh esp8266_1 2023-11-05

# CSV形式の出力: get_csv "esp8266_1" "2023-11-05"
get_csv() {
   dev_name="${1}";
   from_date="${2}";
   cat<<-EOF | sqlite3 weather.db -csv
      SELECT 
         did,datetime(measurement_time,'unixepoch','localtime')
         ,temp_out,temp_in,humid,pressure
      FROM
         t_weather
      WHERE 
         did=(SELECT id FROM t_device WHERE name="${dev_name}") 
      AND
         datetime(measurement_time,'unixepoch','localtime') >= "${from_date}"
ORDER BY measurement_time
EOF
}

get_csv "${1}" "${2}"
```

実行結果

```bash
$ ./get_csv_from_weather_3.sh esp8266_1 2023-11-05
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-5. 行ヘッダーを出力する

[ファイル] get_csv_from_weather_3-1.sh  

```bash
#!/bin/bash

# ./get_csv_from_weather_3-1.sh esp8266_1 2023-11-05

# CSV形式の出力: get_csv "esp8266_1" "2023-11-05"
get_csv() {
   dev_name="${1}";
   from_date="${2}";
   cat<<-EOF | sqlite3 weather.db -csv
      SELECT 
         did,datetime(measurement_time,'unixepoch','localtime')
         ,temp_out,temp_in,humid,pressure
      FROM
         t_weather
      WHERE 
         did=(SELECT id FROM t_device WHERE name="${dev_name}") 
      AND
         datetime(measurement_time,'unixepoch','localtime') >= "${from_date}"
ORDER BY measurement_time
EOF
}

# ヘッダーを出力
header='"did","measurement_time","temp_out","temp_in","humid","pressure"'
echo $header
# レコードを出力
get_csv "${1}" "${2}"
```

実行結果

```bash
$ ./get_csv_from_weather_3-1.sh esp8266_1 2023-11-05
"did","measurement_time","temp_out","temp_in","humid","pressure"
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
# ...途中省略...
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-6. CSV出力をファイル保存する

**引数に出力先ディレクトリを追加**しCSV出力をファイルに保存する

[ファイル] get_csv_from_weather_4.sh  
[位置引数] デバイス名 (最初), 検索開始日 (2番目), **保存ディレクトリ(3番目)**

```bash
#!/bin/bash

# get_csv_from_weather_4.sh esp8266_1 2023-11-05 ~/Documents/csv

# CSV形式の出力: get_csv "esp8266_1" "2023-11-05"
get_csv() {
   dev_name="${1}";
   from_date="${2}";
   cat<<-EOF | sqlite3 weather.db -csv
      SELECT 
         did,datetime(measurement_time,'unixepoch','localtime')
         ,temp_out,temp_in,humid,pressure
      FROM
         t_weather
      WHERE 
         did=(SELECT id FROM t_device WHERE name="${dev_name}") 
      AND
         datetime(measurement_time,'unixepoch','localtime') >= "${from_date}"
ORDER BY measurement_time
EOF
}

# ./get_csv_from_weather_4.sh esp8266_1 2023-11-05 ~/Download/csv
echo "device_name: ${1}: from_date: ${2} > ${3}"

header='"did","measurement_time","temp_out","temp_in","humid","pressure"'
filename="t_weather.csv"
filepath="${3}/${filename}"

# ファイルにヘッダーを出力
echo $header > "${filepath}"
# ファイルにレコードを追記モードで出力
get_csv "${1}" "${2}" >> "${filepath}"
if [ $? = 0 ]; then
    # 結果OKならレコード件数出力
   echo "Output csv to ${filepath}"
   row_count=$(cat "${filepath}" | wc -l)
   # ヘッダー行分を差し引く
   row_count=$(( row_count - 1))
   echo "Record count: ${row_count}" 
else
   echo "Output error"
fi
```

実行結果

```bash
$ ./get_csv_from_weather_4.sh esp8266_1 2023-11-05 ~/Downloads/csv
device_name: esp8266_1: from_date: 2023-11-05 > /home/yukio/Downloads/csv
Output csv to /home/yukio/Downloads/csv/t_weather.csv
Record count: 73
$ # ファイル確認
$ head ~/Downloads/csv/t_weather.csv 
"did","measurement_time","temp_out","temp_in","humid","pressure"
1,"2023-11-05 00:06:08",3.1,16.9,46.9,1024.6
1,"2023-11-05 00:15:50",3.0,16.8,46.7,1024.7
1,"2023-11-05 00:25:34",3.0,16.7,46.8,1024.9
1,"2023-11-05 00:35:18",3.0,16.5,46.9,1024.9
1,"2023-11-05 00:45:02",2.8,16.4,46.6,1025.0
1,"2023-11-05 00:54:47",3.0,16.3,46.9,1025.0
1,"2023-11-05 01:04:30",2.8,16.2,46.9,1025.0
1,"2023-11-05 01:14:14",2.8,16.2,47.8,1025.1
1,"2023-11-05 01:23:59",2.9,16.1,46.5,1024.9
$ tail ~/Downloads/csv/t_weather.csv 
1,"2023-11-05 10:19:31",9.3,16.9,47.2,1027.1
1,"2023-11-05 10:29:16",9.6,17.0,47.1,1027.0
1,"2023-11-05 10:38:59",10.0,17.1,46.8,1027.3
1,"2023-11-05 10:48:43",10.2,17.2,46.5,1027.1
1,"2023-11-05 10:58:28",10.4,17.2,46.4,1026.9
1,"2023-11-05 11:08:12",10.5,17.3,46.1,1026.8
1,"2023-11-05 11:17:56",10.9,17.4,45.9,1026.8
1,"2023-11-05 11:27:41",11.6,17.5,45.6,1026.7
1,"2023-11-05 11:37:25",11.9,17.5,45.4,1026.4
1,"2023-11-05 11:47:09",12.3,17.6,45.5,1026.5
```

#### 2-7. 検索開始日と検索終了日を指定する

[ファイル] get_csv_from_weather_5.sh  
[位置引数] デバイス名 (最初), 検索開始日 (2番目), **検索終了日 (3番目)**, 保存ディレクトリ(4番目)  
**※位置引数はすべて必須となります。**

```bash
#!/bin/bash

# get_csv_from_weather_5.sh esp8266_1 2023-11-04 2023-11-05 ~/Documents/csv

# 引数の次の日を取得: next_to_date "${to_date}"
next_to_date() {
    retval=$(date -d "$1 1 days" +'%F');
    echo "$retval"
}

# CSV形式の出力: get_csv "${device_name}" "${where}"
get_csv() {
    dev_name="$1";
    where="$2";
cat<<-EOF | sqlite3 "$PATH_WEATHER_DB" -csv
    SELECT
      did,
      datetime(measurement_time, 'unixepoch', 'localtime'), 
      temp_out, temp_in, humid, pressure
    FROM
      t_weather
    WHERE
      did=(SELECT id FROM t_device WHERE name = '${dev_name}') AND (${where})
    ORDER BY measurement_time;
EOF
}

device_name="$1"
from_date="$2"
to_date="$3"
output_path="$4"

next_date=$(next_to_date "$to_date");
echo "next_date: ${next_date}"
cond_from="datetime(measurement_time, 'unixepoch', 'localtime') >= '"${from_date}"'"
cond_to_next="datetime(measurement_time, 'unixepoch', 'localtime') < '"${next_date}"'"
where=" ${cond_from} AND ${cond_to_next}"
echo "where: ${where}"
filename="weather.csv"

header='"did","measurement_time","temp_out","temp_in","humid","pressure"'
echo $header > "$output_path/$filename"
get_csv ${device_name} "${where}" >> "$output_path/$filename"
if [ $? = 0 ]; then
   echo "Output weather csv to $output_path/$filename"
   row_count=$(cat "${output_path}/${filename}" | wc -l)
   row_count=$(( row_count - 1))
   echo "Record count: ${row_count}" 
else
   echo "Output error"
fi
```

**実際にインストーラースクリプトから呼び出しする場合は以上の内容でOKです。**
※インストーラースクリプトは引数を予めプロクラムするため

実行結果

```bash
$ ./get_csv_from_weather_5.sh esp8266_1 2023-11-04 2023-11-05 ~/Documents/csv
next_date: 2023-11-06
where:  datetime(measurement_time, 'unixepoch', 'localtime') >= '2023-11-04' AND datetime(measurement_time, 'unixepoch', 'localtime') < '2023-11-06'
Output weather csv to /home/yukio/Documents/csv/weather.csv
Record count: 221
```


### 3. 気象データCSV出力スクリプト

以下のスクリプトは実際に Raspberry Pi 4 で利用するスクリプトになります

#### 3-1. デバイステーブルのCSVファイル保存スクリプト

[テーブル名] t_device  
[ファイル] get_csv_from_device.sh   
[位置引数] **保存ディレクトリ (必須)**
※**データ移行先のPostgreSQLでデバイステーブルに"description"列を追加しているため、当該スクリプトでも追加しています。**

```bash
#!/bin/bash

get_csv() {
cat<<-EOF | sqlite3 "$PATH_WEATHER_DB" -csv
    SELECT id,name,name FROM t_device ORDER BY id;
EOF
}

output_path="$1"
filename="device.csv"
csv_filepath="${output_path}/${filename}"

header='"id","name","description"'
echo $header > "${csv_filepath}"
get_csv >> "${csv_filepath}"
if [ $? = 0 ]; then
   echo "Output device csv to $output_path/$filename"
   row_count=$(cat "${output_path}/${filename}" | wc -l)
   row_count=$(( row_count - 1))
   echo "Record count: ${row_count}" 
else
   echo "Output error"
fi   
```

実行結果

```bash
$ export PATH_WEATHER_DB=~/db/weather.db
$ ./get_csv_from_device.sh ~/Documents/csv
Output device csv to /home/yukio/Documents/csv/device.csv
Record count: 1
$ cat ~/Documents/csv/device.csv 
"id","name","description"
1,esp8266_1,esp8266_1
```

#### 3-2. 気象データCSV出力スクリプト

[テーブル名] t_weather  
[ファイル] get_csv_from_weatger.sh   
[位置引数] 検索開始日(必須), 検索終了日(必須), **保存ディレクトリ(必須)**
**全デバイスを出力 ※運用しているラズパイゼロでは1デバイス分のデータしか記録していない**

```bash
#!/bin/bash

# 引数の次の日を取得: next_to_date "${to_date}"
next_to_date() {
    retval=$(date -d "$1 1 days" +'%F');
    echo "$retval"
}

# CSV形式の出力: get_csv "${device_name}" "${where}"
get_csv() {
    dev_name="$1";
    where="$2";
cat<<-EOF | sqlite3 "$PATH_WEATHER_DB" -csv
    SELECT
      did,
      datetime(measurement_time, 'unixepoch', 'localtime'), 
      temp_out, temp_in, humid, pressure
    FROM
      t_weather
    WHERE
      (${where})
    ORDER BY did,measurement_time;
EOF
}

from_date="$1"
to_date="$2"
output_path="$3"

next_date=$(next_to_date "$to_date");
echo "next_date: ${next_date}"
cond_from="(datetime(measurement_time, 'unixepoch', 'localtime') >= '"${from_date}"')"
cond_to_next="(datetime(measurement_time, 'unixepoch', 'localtime') < '"${next_date}"')"
where="${cond_from} AND ${cond_to_next}"
echo "${where}"

# CSV出力パス
filepath="${output_path}/weather.csv"

# ヘッダー出力
header='"did","measurement_time","temp_out","temp_in","humid","pressure"'
echo $header > "${filepath}"
# レコード追記
get_csv ${device_name} "${where}" >> "${filepath}"
if [ $? = 0 ]; then
   echo "Output weather csv to "${filepath}""
   row_count=$(cat "${filepath}" | wc -l)
   row_count=$(( row_count - 1))
   echo "Record count: ${row_count}" 
else
   echo "Output error"
fi
```

実行結果

```bash
$ ./get_csv_from_weather.sh 2022-01-01 2023-10-31 ~/Documents/csv
next_date: 2023-11-01
Output weather csv to /home/yukio/Documents/csv/weather.csv
Record count: 98888
$ head ~/Documents/csv/weather.csv 
"did","measurement_time","temp_out","temp_in","humid","pressure"
1,"2022-01-01 00:02:08",-8.6,13.6,41.9,1001.3
1,"2022-01-01 00:11:52",-8.6,12.9,39.2,1001.4
1,"2022-01-01 00:21:35",-8.5,12.8,43.2,1001.4
1,"2022-01-01 00:31:21",-8.4,13.0,41.7,1001.7
1,"2022-01-01 00:41:02",-8.6,12.4,39.5,1001.6
1,"2022-01-01 00:50:46",-8.6,12.0,43.1,1001.7
1,"2022-01-01 01:00:29",-8.8,11.6,41.8,1001.8
1,"2022-01-01 01:10:14",-8.8,11.9,42.6,1001.8
1,"2022-01-01 01:19:59",-8.8,12.2,41.7,1001.7
$ tail ~/Documents/csv/weather.csv 
1,"2023-10-31 22:24:20",10.3,18.3,58.5,1014.5
1,"2023-10-31 22:34:05",10.5,18.2,58.4,1014.4
1,"2023-10-31 22:43:49",10.8,18.2,58.2,1014.3
1,"2023-10-31 22:53:33",10.5,18.1,57.9,1014.2
1,"2023-10-31 23:03:17",10.4,18.1,57.9,1014.2
1,"2023-10-31 23:13:01",10.2,18.0,57.8,1014.1
1,"2023-10-31 23:22:45",10.2,17.9,57.6,1014.0
1,"2023-10-31 23:32:29",10.3,17.9,57.8,1013.9
1,"2023-10-31 23:42:13",10.2,17.8,57.7,1014.0
1,"2023-10-31 23:51:57",10.0,17.8,57.6,1014.0
```

### 4.結論

ネットではおびただしい量の結果が検索されるため、**どれが自分の環境にあった実装方法なのが判断するのに骨が折れます。**

**スクリプトを作成するに当ってはSQLite3のヘルプを見ながらコマンドを打ち込んで出力結果を見ながら試行錯誤するのが一番の近道と感じました。**


##### 参考までに運用機のラズパイ4のデータ移行に使用したスクリプトを示します。  
※ 必須のデバイス名以外は任意の入力パラメータとして扱うことができます。

```bash
#!/bin/bash

readonly SCRIPT_NAME=${0##*/}

PATH_EXPORT_CSV="$HOME/Downloads/csv"

print_error()
{
   cat << END 1>&2
$SCRIPT_NAME: $1
Try --help option
END
}

next_to_date() {
    retval=$(date -d "$1 1 days" +'%F');
    echo "$retval"
}


get_csv() {
    dev_name="$1";
    where="$2";
cat<<-EOF | sqlite3 "$PATH_WEATHER_DB" -csv
    SELECT
      did,
      datetime(measurement_time, 'unixepoch', 'localtime'), 
      temp_out, temp_in, humid, pressure
    FROM
      t_weather
    WHERE
      did=(SELECT id FROM t_device WHERE name = '${dev_name}') AND (${where})
    ORDER BY measurement_time;
EOF
}

params=$(getopt -n "$SCRIPT_NAME" \
       -o d:f:t:o: \
       -l device-name: -l from-date: -l to-date: -l output-path: -l help \
       -- "$@")

# Check command exit status
if [[ $? -ne 0 ]]; then
  echo 'Try --help option for more information' 1>&2
  exit 1
fi
eval set -- "$params"

# init option value
device_name=
from_date=
to_date=
output_path=$PATH_EXPORT_CSV

# Parse options
# Positional parameter count: $#
while [[ $# -gt 0 ]]
do
  case "$1" in
    -d | --device-name)
      device_name=$2
      shift 2
      ;;
    -f |--from-date)
      from_date=$2
      shift 2
      ;;
    -t | --to-date)
      to_date=$2
      shift 2
      ;;
    -o | --output-path)
      output_path=$2
      shift 2
      ;;
    --help)
      print_help
      exit 0
      ;;
    --)
      shift
      break
      ;;
  esac
done

# Check required option: --device-name
if [ -z "$device_name" ]; then
  print_error "Required --device-name xxxxx"
  exit 1
fi

where=
if [ -n "$from_date" ] && [ -n "$to_date" ]; then
   next_date=$(next_to_date "$to_date");
   where=" measurement_time >= strftime('%s','"$from_date"','-9 hours') AND measurement_time < strftime('%s','"$next_date"','-9 hours')"
fi
if [ -n "$from_date" ] && [ -z "$to_date" ]; then
   where=" measurement_time >= strftime('%s','"$from_date"','-9 hours')"
fi
if [ -z "$from_date" ] && [ -n "$to_date" ]; then
   next_date=$(next_to_date "$to_date");
   where=" measurement_time < strftime('%s','"$next_date"','-9 hours')"
fi
if [ -z "$from_date" ] && [ -z "$to_date" ]; then
   where=" 1=1"
fi
filename="weather.csv"

header='"did","measurement_time","temp_out","temp_in","humid","pressure"'
echo $header > "$output_path/$filename"
get_csv "$device_name" "$where" >> "$output_path/$filename"
if [ $? = 0 ]; then
   echo "Output weather csv to $output_path/$filename"
   row_count=$(cat "${output_path}/${filename}" | wc -l)
   row_count=$(( row_count - 1))
   echo "Record count: ${row_count}" 
fi   
```

**本番環境のRaspberry Pi 4で使用したCSV出力スクリプトは下記GitHubリポジトリでご覧になれます。**
[GitHub(pipito-yukio) UDP Weather Sensor packet monitor for Raspberry pi 4　src/installer/data/sql](https://github.com/pipito-yukio/raspi4_apps/tree/master/src/installer/data/sql "raspi4_apps")