TextQL 2.0.3 常用 CLI 命令与工具详解

TextQL 2.0.3 的 CLI 命令全景实测:文件名即表名的即席查询、短 SQL、多文件 JOIN、stdin 管道、regexp 过滤、pretty/TSV 导出、save-to 持久化续查、console 打开 SQLite REPL、sqlfile 多语句执行,含 9 类报错实录与返回码

TextQLTextQLSQLCLI工具CSV2026/11/03

TextQL 2.0.3 常用 CLI 命令与工具详解

目标:把 TextQL 2.0.3 在服务器上最常用的查询、导出、持久化、REPL、管道协作命令按场景逐一实测展开——每条命令给真实回显,每个报错给现象与规避。TextQL 没有子命令体系(无 textql xxx subcmd),全部能力由"参数 + 位置文件"组合驱动,本文即这套组合的实战手册。以下命令与输出均已在 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 构建,含 -sqlfile),位于 /usr/local/bin/textql
  • 配套工具:sqlite3 3.37.2(-save-to 续查与 -console 需要)
$ textql -version
2.0.3
$ sqlite3 --version
3.37.2 2022-01-06 13:25:41 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5dalt1

二、工具概览

TextQL 的执行模型固定为四步:载入(文件/目录/stdin → 内存 SQLite 表)→ 执行(-sql/-sqlfile 的语句按分号顺序跑)→ 输出(stdout/文件,CSV 或框线表)→ 收尾(-save-to 落盘 / -console 开 REPL)。没有交互壳、没有子命令、没有配置文件;表名=文件基名,管道表名=stdin。

textql [-console] [-save-to path] [-output-file path] [-output-dlm delimter]
       [-output-header] [-pretty] [-quiet] [-header] [-dlm delimter]
       [-sql sql_statements | -sqlfile path] [path ...]

三、前置条件

安装过程见《Ubuntu 22.04 安装 TextQL 2.0.3 指南》。准备测试数据(本文全部示例基于此):

$ mkdir -p /root/textql-lab/multi && cd /root/textql-lab
$ cat > sales.csv <<'EOF'
id,date,region,product,amount,qty
1,2026-01-03,east,WidgetA,120.50,3
2,2026-01-04,east,WidgetB,89.90,1
3,2026-01-05,west,WidgetA,210.00,5
4,2026-02-01,north,WidgetC,55.25,2
5,2026-02-07,west,WidgetB,310.75,7
6,2026-02-11,east,WidgetC,42.00,1
7,2026-03-02,north,WidgetA,180.40,4
8,2026-03-15,west,WidgetC,99.99,3
9,2026-03-21,east,WidgetA,76.20,2
10,2026-03-30,north,WidgetB,145.60,3
EOF
$ cat > regions.csv <<'EOF'
region,country,manager
east,China,Li
west,China,Wang
north,China,Zhao
EOF
$ printf 'ip\tmethod\tpath\tstatus\n10.0.0.5\tGET\t/health\t200\n10.0.0.6\tPOST\t/api/order\t201\n10.0.0.5\tGET\t/index.html\t404\n' > nginx.tsv
$ printf 'name;age;city\nTom;28;SH\nAmy;31;BJ\n' > semi.csv
$ printf '1,apple,3.5\n2,banana,7.25\n3,cherry,12.0\n' > nohdr.csv
$ cp sales.csv multi/t1.csv
$ printf 'a,b\n1,2\n3,4\n' > multi/t2.csv
$ cat > query.sql <<'EOF'
select region, count(*) as cnt, round(sum(amount),2) as total from sales group by region order by total desc;
select 'ALL_DONE' as mark
EOF
$ ls
multi  nginx.tsv  nohdr.csv  query.sql  regions.csv  sales.csv  semi.csv

四、查询命令

4.1 基础查询与限行

-header 让首行作列名,表名取文件基名 sales:

$ textql -sql "select * from sales limit 3" -header sales.csv
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.2 短 SQL:省略 SELECT / FROM

单表时 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

⚠️ 注意:第二条没带 -header,首行表头也算数据,所以是 11 而不是 10。列名规则与 -header 的关系见《TextQL 2.0.3 配置参数详解》5.1 节。

4.3 聚合与排序

$ textql -sql "select region, count(*) cnt, round(sum(amount),2) total from sales group by region order by total desc" -header sales.csv
west,3,620.74
north,3,381.25
east,4,328.6

4.4 跨文件 JOIN

命令行并列多个文件,各自成表即可 JOIN:

$ textql -sql "select s.region, r.country, r.manager, round(sum(s.amount),2) total from sales s join regions r on s.region = r.region group by 1,2,3" -header sales.csv regions.csv
east,China,Li,328.6
north,China,Zhao,381.25
west,China,Wang,620.74

文件与目录可以混着给:

$ textql -sql "select count(*) from t1" multi regions.csv
11

4.5 stdin 管道查询

管道数据的表名固定为 stdin(README 写的 pipe 实测报错):

$ 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

$ cat sales.csv | textql -sql "select region, sum(amount) from pipe group by region" -header
2026-11-03 04:49:28 no such table: pipe

4.6 表名覆写与非法名拒绝

名字:文件 语法给表起别名;非法字符直接拒绝:

$ 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

4.7 目录批量载入

目录参数非递归展开为其中每个文件:

$ textql -sql "select count(*) from t1" multi
11
$ textql -sql "select count(*) from t2" multi
3

4.8 类型推断与日期函数

列类型全为 NUMERIC,值类型逐值推断;文本日期列可直接上 strftime:

$ 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

$ textql -sql "select strftime('%Y-%m', date) month, round(sum(amount),2) total from sales group by month order by month" -header sales.csv
2026-01,420.4
2026-02,408
2026-03,502.19

4.9 regexp 过滤

内置 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

五、输出与导出命令

5.1 列名 + 自定义输出分隔符

-output-header 输出列名,-output-dlm tab 把结果切成 TSV:

$ 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

5.2 框线表(-pretty)

$ textql -sql "select product, sum(qty) q from sales group by product" -header -pretty sales.csv
+---------+----+
| WidgetA | 14 |
| WidgetB | 11 |
| WidgetC |  6 |
+---------+----+

5.3 结果落盘(-output-file)

$ 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

框线表同样可落盘:

$ textql -sql "select product, sum(qty) q from sales group by product" -header -pretty -output-file pretty_out.txt sales.csv
$ cat pretty_out.txt
+---------+----+
| WidgetA | 14 |
| WidgetB | 11 |
| WidgetC |  6 |
+---------+----+

5.4 导出 TSV 供下游管道

$ textql -sql "select region, round(sum(amount),2) total from sales group by region" -header -output-header -output-dlm tab -output-file region_total.tsv sales.csv
$ cat -A region_total.tsv | head -4
region^Itotal$
east^I328.6$
north^I381.25$
west^I620.74$

^I 即制表符,$ 为行尾标记(cat -A 显示),确认导出的是标准 TSV。

六、持久化与 REPL 命令

6.1 -save-to 落库 + sqlite3 续查

$ 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 按业务列名查询实测报 Error: in prepare, no such column: amount (1)。

6.2 -console 打开官方 SQLite REPL

-console 要求文件输入(拒绝管道)且 PATH 有 sqlite3。交互会话实测(输入行为用户键入):

$ textql -console -sql "select 1" sales.csv
1
1
...(10 行数据逐行输出 select 1 的结果)
SQLite version 3.37.2 2022-01-06 13:25:41
Enter ".help" for usage hints.
sqlite> .tables
sales
sqlite> select count(*) from sales;
11
sqlite> .quit

(输出节选(实测首尾):select 1 未带 -header,11 行数据各出一个 1。)

管道场景直接拒绝:

$ 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

6.3 -console + -save-to:REPL 直接打开持久库

带 -save-to 时 REPL 打开的就是落盘文件本身,退出后库还在:

$ textql -console -header -sql "select 1" -save-to condb2.sqlite sales.csv
(REPL 内)
sqlite> .tables
sales
sqlite> select count(*) from sales;
10
sqlite> .quit
$ ls -la condb2.sqlite
-rw-r--r-- 1 root root 8192 Nov  3 04:50 condb2.sqlite
$ sqlite3 condb2.sqlite "select count(*) from sales;"
10

-output-header 会透传给 sqlite3(等价 sqlite3 -header)。

七、SQL 文件执行(-sqlfile)

7.1 多语句顺序执行

$ textql -sqlfile query.sql -header sales.csv regions.csv
west,3,620.74
north,3,381.25
east,4,328.6
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE
ALL_DONE

第二条语句 select 'ALL_DONE' as mark 无 FROM,自动注入首表逐行执行(10 行数据出 10 行 ALL_DONE)。

7.2 引号内分号不被误拆

-sql 按分号拆语句,但引号内的分号有专门合并处理:

$ 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。)

⚠️ 注意:-sqlfile 指向不存在的文件时,程序只打印内存指针 0xc000099f40 而非错误说明(未修复缺陷);-sql 与 -sqlfile 同时给时 -sql 优先。参数级细节见《TextQL 2.0.3 配置参数详解》5.2 节。

八、管道链实战

上游先过滤、TextQL 只做聚合,是它在 shell 管道中最典型的用法:

$ grep -v north sales.csv | textql -sql "select region, count(*) from stdin group by region" -header
east,4
west,3

九、返回码(实测)

场景命令退出码
查询成功textql -sql "count(*)" sales.csv0
表不存在textql -sql "select * from nosuch" sales.csv1
文件不存在textql -sql "select 1" /nonexistent.csv1
单引号不配对textql -sql "select 'unbalanced from sales" sales.csv1
-versiontextql -version0

脚本中用 if ! textql ...; then 即可捕获失败;错误信息走 stderr、带 2026-11-03 04:49:28 式时间戳前缀。

十、常用操作速查

需求命令骨架
查 CSVtextql -header -sql "select ... from 表名 [where/group by]" f.csv
最短全表textql -sql "*" -header f.csv
查 TSVtextql -header -dlm tab -sql "..." f.tsv
查分号分隔textql -header -dlm 0x3b -sql "..." f.csv
管道聚合... | textql -header -sql "select ... from stdin"
多表 JOINtextql -header -sql "select ... from a join b ..." a.csv b.csv
目录批载textql -sql "..." dir/(非递归,每文件一表)
起别名textql -sql "..." "orders:sales.csv"
导出报表加 -output-header -output-file rpt.csv
人读表格加 -pretty
落 SQLitetextql -header -save-to x.sqlite -sql "..." f.csv
进 REPLtextql -header -console -sql "select 1" f.csv(需 sqlite3)
跑 SQL 文件textql -header -sqlfile q.sql f.csv
静默加 -quiet

十一、常见问题

11.1 SQL 结尾"分号+换行"导致无限刷屏

实测 8 秒输出 73 MB,timeout 杀掉。规避:-sql 结尾不带分号(多语句中间的分号正常)。这是停更项目未修复的最危险缺陷。

11.2 报错只有一串 0xc000045ef0 指针

无 -sql/-sqlfile 或 -sqlfile 文件不存在时打印 *error 指针(log.Fatalln(*error) 缺陷)。补上 SQL 参数/检查文件路径。

11.3 管道里查不到 pipe 表

管道表名是 stdin(README 示例错误,实测 no such table: pipe)。

11.4 count(*) 比实际行数多 1

忘带 -header:首行表头被当数据、列名成 c0..cN。带上 -header 重跑。

11.5 -console 起不来

两种报错:Can not open console with pipe input, read a file instead(管道输入被拒,改用文件);Console requested but unable to locate \sqlite3` program on $PATH(装 sqlite3:apt-get install -y sqlite3`)。

11.6 -save-to 的库在 sqlite3 里查不到业务列名

建库时没带 -header,列名是 c0..c5(实测报 no such column: amount)。带 -header 重建。

11.7 表名覆写报 Invalid table name

名字:文件 的"名字"含 [] 等非法字符(实测 bad[]name 被拒)。改用字母/数字/下划线。

11.8 单引号不配对直接退出

String contains odd number of "'(Single Quotes)"——整条 SQL 引号计数为奇数即拒,检查转义。

11.9 文件不存在

open /nonexistent.csv: no such file or directory(带时间戳、退出码 1),目录路径同理。

十二、参考链接