psql的\set命令无法存入JSON文件内容至变量的问题排查
为什么psql在bash脚本中用\set读取JSON文件只获取到第一个字符?
这个问题的核心是bash命令替换的分词行为和psql交互式执行的差异,我来帮你拆解清楚:
问题原因
当你在bash脚本的here-doc(<<EOF)里写\set var cat /tmp/jsonfile.json``时,bash会先对反引号里的命令输出做分词处理——它会把JSON内容里的空格、换行符都当作分隔符,把整个JSON拆成多个"字段"。比如你的JSON开头是{ "OT": [...] },bash会把它拆成{、"OT":、[...]等多个部分。
而psql的\set命令只会接受变量名和第一个值,所以最终var被设置成了第一个字段{,这就导致后续INSERT时JSON格式完全错误。
但你在psql提示符下手动执行时,是psql自己解析并执行命令替换,不会经过bash的分词步骤,所以能完整读取整个JSON文件的内容。
解决方案
这里有几个靠谱的解决办法,按推荐程度排序:
1. 使用psql参数化查询(最安全,避免SQL注入)
用psql的--variable参数传递JSON内容,完全绕开bash分词和引号转义的问题:
#!/bin/bash # 先创建表(如果不存在的话) psql -X -h localhost -U <dbuser> -d <dbname> -c "CREATE TABLE IF NOT EXISTS cardtable(id SERIAL PRIMARY KEY, charcolumn VARCHAR(20) NOT NULL, cardcolumn JSON NOT NULL);" # 用参数化方式插入JSON psql -X -h localhost -U <dbuser> -d <dbname> -c "INSERT INTO cardtable(charcolumn, cardcolumn) VALUES ('USA', :json_var);" --variable=json_var="$(cat /tmp/jsonfile.json)"
2. 在bash中先读取并转义JSON,再传入here-doc
如果坚持用here-doc,先把JSON内容读入bash变量,同时转义单引号(避免SQL语法错误),再传递给psql:
#!/bin/bash # 读取JSON并转义单引号(把'替换成'',符合PostgreSQL的字符串转义规则) json_content=$(sed "s/'/''/g" /tmp/jsonfile.json) psql -X -h localhost -U <dbuser> -d <dbname> <<EOF CREATE TABLE IF NOT EXISTS cardtable(id SERIAL PRIMARY KEY, charcolumn VARCHAR(20) NOT NULL, cardcolumn JSON NOT NULL); INSERT INTO cardtable(charcolumn, cardcolumn) VALUES ('USA', '$json_content'); EOF
3. 让psql直接读取文件内容(跳过bash命令替换)
你也可以用psql的内部命令读取文件,比如用\set结合psql的命令替换,关键是用单引号包裹让bash不提前处理:
#!/bin/bash psql -X -h localhost -U <dbuser> -d <dbname> <<EOF CREATE TABLE IF NOT EXISTS cardtable(id SERIAL PRIMARY KEY, charcolumn VARCHAR(20) NOT NULL, cardcolumn JSON NOT NULL); \set var '\`cat /tmp/jsonfile.json\`' INSERT INTO cardtable(charcolumn, cardcolumn) VALUES ('USA', :'var'); EOF
内容的提问来源于stack exchange,提问作者RoT
相关产品推荐
相关产品推荐

