やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

SQLite3のCLI文脈にて動的コマンドを実行する方法

 .outputしたコマンドを.readする。

成果物

方法

.output ./command.sql
select '(実行したいドットコマンドやSQL文テキスト)';
.output stdout
.read ./command.sql

 これが最も自在にコマンドを動的生成・実行できる方法と思われる。たとえば前回のような使い方がある。

 動的コマンド実行は以下コマンドを併用することで実現する。

コマンド 概要
.output/.read コマンド文字列を動的に作成する/ファイル読込してコマンドを実行する
SQL文,DotCommand SQLite3内の変数を取得・設定する
fsdir(),readfile(),writefile()
char(10),rtrim('',x'0A')
ファイル・SQL間インタフェース
.parameter(sqlite_parameters表) 変数をSQL内に埋め込む
.shell OS・SQLite3間インタフェース

動的コマンド作成

 コマンドやSQL文の実行結果を変数として、そのままファイル出力したり.param setしたりする。それが動的コマンドのキモ。select文やvalues()文、.printを使う。ただし.shell echoなどで出力するときは.outputでなくシェルのリダイレクトを使う。.shell echo 'A\\\nB' > ./output.txt

 なお、SQL文中でファイルの入出力はfsdir(), readfile(), writefile()で行う。読込後のデータにはなぜか末尾に\nが付与されてしまうためrtrim(readfile(), x'0A')とすることで省く。

シェルコマンドの結果をテーブルに挿入する

.param init
.shell echo 'A\\\nB' > ./output.txt
insert or replace 
  into sqlite_parameters 
  values('output', rtrim(cast(readfile('./output.txt') as text), x'0A'));
select * from sqlite_parameters;

本当は./output.txtも変数にしたいができない。

 それ自体も動的コマンドにするためにファイル出力するならば、そのファイルパスは固定で必要。どこまでいっても必ず固定ファイルパスが必要。

 かといって、以下のようにドットコマンド内でパラメータを参照することはできない。@pathという名前のファイルが作成されてしまう。

.param init
.param set @path './output.txt'
.shell echo 'A\\\nB' > @path

 また、ドットコマンド内でシェル変数・環境変数を参照することもできない。.shellコマンド完了後にプロセス終了して変数が解放されるのだろう。

.param init
.shell OUT_PATH='./output.txt'
.shell echo 'A\\\nB' > $OUT_PATH
sh: 1: cannot create : Directory nonexistent
System command returns 512

 絶対パスで指定しても同じ。

.param init
.shell OUT_PATH='/tmp/work/output.txt'
.shell echo 'A\\\nB' > $OUT_PATH
sh: 1: cannot create : Directory nonexistent
System command returns 512

ドットコマンドの結果を別のドットコマンドの一部にして実行する

及ばない方法

 以下コマンドは単独では限定的な動的コマンド実行しかできない。コマンドを文字列にして.readすることで動的実行できる。

他の方法 できる/できない
.parameter SQL文のみ。しかもwhere句の列にセットする値などごく一部のみ。
.shell シェルコマンドのみ。プロセス内反映不可(pragma foreign_keys=?等)。同SQLite3対話モード文脈内でドットコマンドを動的に生成・実行不可

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux

前回まで