TextQL 2.0.3 配置参数详解
TextQL 没有任何配置文件与持久化配置体系,全部行为由 13 个命令行参数与内置行为规则(表名映射、NUMERIC 类型推断、短 SQL、多语句拆分)决定,本文以源码与全历史检索为证,逐参数详解输入、SQL、输出、持久化四组配置与实测行为
TextQL 2.0.3 配置参数详解
目标:把 TextQL 的全部可配置面——13 个命令行参数、4 条内置行为规则(表名映射、类型推断、短 SQL、多语句拆分)与 1 个可选外部扩展——逐项讲清默认值、实测效果与坑。特别说明:TextQL 不存在配置文件,本文第二章给出源码级与全 Git 历史级论证。以下所有命令与输出均已在 Ubuntu 22.04(x86_64)+ TextQL 2.0.3(master 源码构建)环境实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(jammy),内核 7.0.0-38-generic,架构 x86_64
- TextQL 版本:2.0.3(master 提交
e6545d5构建,-ldflags "-X main.VERSION=2.0.3 -s"注入版本串,故含-sqlfile;2015 年的 2.0.3 release 源码无此参数) - 安装位置:
/usr/local/bin/textql(4,408,672 字节)
$ textql -version
2.0.3
- 测试数据:
/root/textql-lab下sales.csv(带表头 10 行)、nohdr.csv(无表头)、nginx.tsv(制表符)、semi.csv(分号)、regions.csv、multi/目录、query.sql(数据内容见《TextQL 2.0.3 常用 CLI 命令与工具详解》第三章)
二、配置模型:无配置文件,参数即配置
很多同类工具(如 csvq 的 csvq_env.json)有配置文件分层,TextQL 没有——"配置"一词在它这里只等于命令行参数 + 内置行为规则。三点源码级证据:
- 入口代码无配置文件逻辑:
textql/main.go的commandLineOptions结构体集中定义全部 13 个 flag,newCommandLineOptions()直接flag.Parse(),全文没有任何路径拼接、HOME 查找或 JSON/YAML 解析代码; - 全 Git 历史检索为空:在源码仓库执行
git log --all -S "config.json"无任何提交命中——"读取.textql/config.json配置"这一特性从未在本项目任何分支存在过,网上部分笔记属于以讹传讹; - man 手册佐证:
man/textql.1.ronn的 FILES 节只定义"结构化文本输入文件",OPTIONS 节与flag.PrintDefaults()输出一致,无配置文件条目。
💡 技术拓展:CLI 工具的配置来源常见四种形态——
形态 例子 TextQL 是否具备 命令行 flag textql -sql ...✅ 唯一形态 环境变量 LS_COLORS、GOPROXY❌ 不读任何环境变量(仅 exec.LookPath用 PATH 找 sqlite3)用户级配置文件 ~/.gitconfig、csvq_env.json❌ 无 可选外部组件 nginx 动态模块 ✅ 仅一项: libgo-sqlite3-extension-functions.so自动加载(见 5.5 节)
因此使用 TextQL 的正确心智是:把参数组合当配置管理——用 shell 函数/别名固化常用组合(见第六章模板),而不是找配置文件。
三、参数查看方式
无参运行即打印全部参数与默认值(这是唯一"查看当前可用参数"的手段,TextQL 没有 --show-config 类命令):
$ textql
Usage of textql:
textql [-console] [-save-to path path] [-output-file path] [-output-dlm delimter] [-output-header] [-pretty] [-quiet] [-header] [-dlm delimter] [-sql sql_statements] [path ...]
-console
After all statements are run, open SQLite3 REPL with this data
-dlm string
Input delimiter character between fields -dlm=tab for tab, -dlm=0x## to specify a character code in hex (default ",")
-header
Treat input files as having the first row as a header row
-output-dlm string
Output delimiter character between fields -output-dlm=tab for tab, -dlm=0x## to specify a character code in hex (default ",")
-output-file string
Filename to write output to, if empty no output is written (default "stdout")
-output-header
Display column names in output
-pretty
Output pretty formatting
-quiet
Surpress logging
-save-to string
SQLite3 db is left on disk at this file
-sql string
SQL Statement(s) to run on the data
-sqlfile string
SQL filepath to run on the data
-version
Print version and exit
2026-11-03 04:49:28 0xc0000120f0
⚠️ 注意最后一行:Usage 打印后程序以
log.Fatalln(err)退出,而传入的是*error指针,于是把内存地址0xc0000120f0打了出来——这是未修复缺陷(停更项目,无补丁),不影响功能但脚本解析 stderr 时要容忍这行。
四、参数总览
| 参数 | 类型 | 默认值 | 作用 | 备注 |
|---|---|---|---|---|
-sql | string | "" | 要执行的 SQL 语句(可多条,分号分隔) | 与 -sqlfile 二选一,-sql 优先 |
-sqlfile | string | "" | 从文件读取 SQL | 2023-10 加入 master;2015 release 无此参数 |
-dlm | string | , | 输入分隔符 | 支持 -dlm=tab、-dlm=0x3b(十六进制码点) |
-header | bool | false | 首行作列名 | 不加则列名为 c0..cN 且首行成为数据 |
-output-header | bool | false | 输出显示列名 | 只影响 stdout/文件,不影响 -save-to 库内列名(见 5.4) |
-output-dlm | string | , | 输出分隔符 | 同样支持 tab/0x## |
-output-file | string | stdout | 结果写文件 | 传空串 "" 表示不输出任何结果(源码判空) |
-pretty | bool | false | 框线表输出 | 生效时忽略 -output-dlm |
-save-to | string | "" | 把内存库落盘为 SQLite 文件 | 产物可被 sqlite3 直接打开 |
-console | bool | false | 结束后 fork 系统 sqlite3 REPL | PATH 需有 sqlite3;拒绝管道输入 |
-quiet | bool | false | 抑制日志输出 | 只关 log 输出,Usage 打印不受影响 |
-version | bool | false | 打印版本并退出 | 版本串靠构建时 ldflags 注入,go install 产物打印空行 |
位置参数 path ... | — | — | 输入文件/目录,任意多个 | 名字:文件 可覆写表名;目录非递归 |
五、核心参数分类详解
5.1 输入读取组:-dlm / -header / 表名规则
-dlm 决定字段切分字符。除默认逗号外,tab 与 0x## 十六进制写法均实测可用:
$ textql -sql "select * from nginx" -header -dlm tab nginx.tsv
10.0.0.5,GET,/health,200
10.0.0.6,POST,/api/order,201
10.0.0.5,GET,/index.html,404
$ textql -sql "select * from semi" -header -dlm 0x3b semi.csv
Tom,28,SH
Amy,31,BJ
-header 决定列名来源:加上则首行作列名(数据 10 行);不加则列名自动编为 c0..c5、首行也作为数据(所以 count(*) 会多 1):
$ textql -sql "select * from nohdr" nohdr.csv
1,apple,3.5
2,banana,7.25
3,cherry,12
表名规则(内置行为,无参数可调):
- 文件基名即表名:
sales.csv→sales(扩展名剥离); - stdin 管道载入的表名固定为
stdin:
$ cat sales.csv | textql -sql "select region, round(sum(amount),2) from stdin group by region" -header
east,328.6
north,381.25
west,620.74
名字:文件语法覆写表名(README v2 曾声称移除该能力,实测 master 仍支持):
$ textql -sql "select count(*) from orders" "orders:sales.csv"
11
- 非法表名直接拒绝:
$ textql -sql "select 1" "bad[]name:sales.csv"
2026-11-03 04:50:53 Invalid table name bad[]name
- 目录参数非递归展开,目录下每个文件各建一表:
$ textql -sql "select count(*) from t1" multi
11
$ textql -sql "select count(*) from t2" multi
3
类型推断规则(内置行为):所有列一律建为 NUMERIC,由 SQLite 亲和性逐值推断,同一列可混返 real/text/integer:
$ textql -sql "select typeof(amount) amt_t, typeof(date) date_t, typeof(id) id_t from sales limit 1" -header sales.csv
real,text,integer
5.2 SQL 行为组:-sql / -sqlfile / 短 SQL / 多语句
-sql 与 -sqlfile 互斥优先级:源码 GetStatements() 中 -sql 非空即用 -sql,-sqlfile 被忽略;两者都为空时打印 Usage 后输出指针退出(见第三章 ⚠️)。-sqlfile 指向不存在的文件同样只输出指针(0xc000099f40)而非错误信息,属同源缺陷。
短 SQL 语法(内置行为):单表场景可省略 FROM 表,简单查询可省略 SELECT:
$ textql -sql "*" -header sales.csv | head -4
1,2026-01-03,east,WidgetA,120.5,3
2,2026-01-04,east,WidgetB,89.9,1
3,2026-01-05,west,WidgetA,210,5
4,2026-02-01,north,WidgetC,55.25,2
$ textql -sql "count(*)" sales.csv
11
无 FROM 的语句自动注入首表:select 'x' 不带 FROM 时会对首表逐行执行(10 行数据出 10 个 x):
$ textql -sql "select 'x' as m" -header sales.csv | head -4
x
x
x
x
多语句拆分:按分号拆分顺序执行;handleSemiColon() 对引号不配对的分号做合并保护,因此字符串里的分号不会被误拆:
$ textql -sql "select 'a;b' as s; select count(*) as n from sales" sales.csv
a;b
a;b
...(每行数据一次,共 11 行)
11
(输出节选(实测首尾):a;b 重复 11 次后接第二条语句结果 11。)
⚠️ 最危险的坑(实测复现):
-sql的语句以"分号 + 换行"结尾(如 shell 里-sql "select count(*) from sales; "后带换行符)会触发拆分逻辑死循环,程序无限刷输出——实测 8 秒写出 73 MB 后靠timeout杀掉。规避:SQL 结尾不要带分号,或确保分号后没有换行;用-sqlfile时文件末尾留空行反而安全(文件内容结尾无换行分号组合)。
单引号奇偶校验:整条 SQL 的单引号个数为奇数时启动即拒:
$ textql -sql "select 'unbalanced from sales" sales.csv
2026-11-03 04:49:28 String contains odd number of "'(Single Quotes)"
5.3 输出组:-output-header / -output-dlm / -output-file / -pretty
-output-header 与 -output-dlm 控制结果行的列名与分隔符(与输入的 -header/-dlm 完全独立):
$ textql -sql "select product, sum(qty) q from sales group by product" -header -output-header -output-dlm tab sales.csv
product q
WidgetA 14
WidgetB 11
WidgetC 6
-pretty 切换为 tablewriter 框线表,此时 -output-dlm 被忽略(源码二选一分支):
$ textql -sql "select product, sum(qty) q from sales group by product" -header -pretty sales.csv
+---------+----+
| WidgetA | 14 |
| WidgetB | 11 |
| WidgetC | 6 |
+---------+----+
-output-file 把结果写文件(-pretty 的框线表同样可落盘):
$ textql -sql "select region, round(sum(amount),2) total from sales group by region" -header -output-file out_region.csv sales.csv
$ cat out_region.csv
east,328.6
north,381.25
west,620.74
⚠️ 注意:
-output-file的默认值是字符串stdout(映射到标准输出),而不是空;源码以!= ""判断是否创建输出器,所以显式传-output-file ""会静默丢弃全部结果——想"只落库不出结果"时这是合法技巧,想输出时千万别传空串。
5.4 持久化与交互组:-save-to / -console / -quiet / -version
-save-to 把内存库落盘为标准 SQLite 文件,列名沿用 -header 规则、列类型全 NUMERIC:
$ textql -sql "select count(*) from sales" -header -save-to salesdb2.sqlite sales.csv
10
$ sqlite3 salesdb2.sqlite ".schema sales"
CREATE TABLE [sales] ([id] NUMERIC, [date] NUMERIC, [region] NUMERIC, [product] NUMERIC, [amount] NUMERIC, [qty] NUMERIC);
$ sqlite3 salesdb2.sqlite "select region, round(sum(amount),2) from sales group by region order by 2 desc;"
west|620.74
north|381.25
east|328.6
⚠️ 注意:忘带
-header时落盘的表列名是c0..c5,后续用 sqlite3 按业务列名查询会报no such column: amount(实测)。要落盘就务必带-header。
-console 在所有语句执行完后 fork 系统 sqlite3 打开数据:未给 -save-to 时写临时库文件(退出即删),给了则直接打开该库;-output-header 会透传为 sqlite3 的 -header。两个硬性前置:PATH 必须有 sqlite3(否则报 Console requested but unable to locate \sqlite3` program on $PATH`),且拒绝管道输入:
$ cat sales.csv | textql -console -sql "select 1"
2026-11-03 04:49:28 Can not open console with pipe input, read a file instead
交互会话实测(文件输入 + REPL 续查)见《TextQL 2.0.3 常用 CLI 命令与工具详解》第六章。
-quiet 把 log 输出重定向到丢弃(进度/错误日志消失,结果输出不受影响):
$ textql -sql "select count(*) from sales" -quiet sales.csv
11
-version 打印构建时注入的 VERSION。该值不是代码常量:go install 产物未注入,打印空行(实测);只有 make/手工 ldflags 构建才有值。
5.5 函数扩展配置:内置 regexp 与可选扩展库
- 内置
regexp函数:无需任何配置,WHERE 列 regexp '模式'直接可用:
$ textql -sql "select id, product from sales where product regexp 'Widget[AB]' and qty > 2" -header sales.csv
1,WidgetA
3,WidgetA
5,WidgetB
7,WidgetA
10,WidgetB
- 可选扩展函数库:安装 dinedal/go-sqlite3-extension-functions 生成
libgo-sqlite3-extension-functions.so后,TextQL 启动时自动加载,提供 Math(acos/sqrt/power/…)、String(replicate/reverse/padc/…)、Aggregate(stdev/variance/mode/median/lower_quartile/upper_quartile)函数。本文实验环境未安装该库,stdev等函数实测不可用;函数清单照抄官方 README。
💡 技术拓展:平面文件 SQL 工具的函数扩展路线有三类——SQLite 系(TextQL/trdsql 可加载
.so扩展或换后端)、自研引擎系(csvq 用DECLARE FUNCTION在会话内定义)、内建全家桶系(dsq 走 Calcite/Postgres 方言自带函数)。TextQL 选的是最省事的"自动探测 .so"路线:零参数、零配置,装了就有。
六、常用参数组合模板
按场景把参数组合固化成 shell 别名/函数即可当"配置文件"用(示例放 ~/.bashrc,root 账号):
| 场景 | 参数组合 |
|---|---|
| 即席查 CSV | textql -header -sql "..." data.csv |
| TSV 日志 | textql -header -dlm tab -sql "..." access.tsv |
| 管道统计 | ... | textql -header -sql "select ... from stdin" |
| 报表导出 CSV | textql -header -output-header -output-file rpt.csv -sql "..." data.csv |
| 管道下游友好 | 追加 -output-dlm tab 导出 TSV |
| 人读结果 | 追加 -pretty |
| 落 SQLite 续用 | textql -header -save-to x.sqlite -sql "..." data.csv(务必带 -header) |
| 进 REPL 探索 | textql -header -console -sql "select 1" data.csv(需 sqlite3) |
| 脚本静默 | 追加 -quiet |
七、常见问题
7.1 有没有 .textql/config.json 或环境变量配置?
没有。第二章已用源码 + git log --all -S "config.json" 全历史检索证明该特性从未存在;TextQL 也不读任何业务环境变量。所有配置面就是第四章的 13 个参数。
7.2 SQL 结尾带分号(再加换行)为什么刷屏停不下来?
触发 handleSemiColon 死循环,输出无限增长(实测 8 秒 73 MB)。立即 Ctrl+C/timeout 终止,去掉结尾分号即可。长期方案:用 -sqlfile 从文件读 SQL。
7.3 报错信息怎么是一串 0xc000045ef0?
无 -sql/-sqlfile、或 -sqlfile 文件不存在时,log.Fatalln(*error) 打印的是指针而非错误文本(未修复缺陷)。检查:① 是否给了 -sql/-sqlfile;② -sqlfile 路径是否存在。
7.4 为什么 README 里 from pipe 查不到表?
管道表名实际是 stdin(实测 no such table: pipe)。README 该示例与代码不符。
7.5 -save-to 出来的库列名是 c0..c5?
建库时没带 -header。补带后重新生成,或 sqlite3 里 ALTER TABLE ... RENAME COLUMN(SQLite 3.25+)补救。
7.6 -sqlfile 报 flag provided but not defined?
你的二进制是从 2015 年 2.0.3 release 源码构建的;-sqlfile 是 2023-10 master 新增。按《Ubuntu 22.04 安装 TextQL 2.0.3 指南》用 master 源码重新构建即可。
7.7 结果一个都不输出?
检查是否传了 -output-file ""(空串=丢弃输出,见 5.3 节 ⚠️);默认应省略该参数或给真实文件名。
八、参考链接
- 官方仓库:https://github.com/dinedal/textql
- 入口源码(参数定义):https://github.com/dinedal/textql/blob/master/textql/main.go
- man 手册:https://github.com/dinedal/textql/blob/master/man/textql.1.ronn
- SQLite NUMERIC 亲和性:https://www.sqlite.org/datatype3.html
- 扩展函数库:https://github.com/dinedal/go-sqlite3-extension-functions
- 安装与构建:《Ubuntu 22.04 安装 TextQL 2.0.3 指南》
- 命令实战:《TextQL 2.0.3 常用 CLI 命令与工具详解》