导入OSM数据时psql命令使用bash变量报语法错误如何解决
问题原因
报错核心是多层命令嵌套下的引号转义逻辑完全错误,本地Bash提前解析到了SQL语句里的括号等特殊符号,把SQL片段当成Bash自身语法执行,根本没有把完整SQL传到PostgreSQL里:
- 命令一共要经过4层解析:本地主机Bash → Docker容器内Shell →
su切换postgres用户后的Shell → psql的SQL语法解析 - 原命令中
--command=后的SQL语句没有整体做引号包裹,SQL里的(、;、引号等特殊字符直接暴露给最外层Bash,触发语法错误 - 原命令中为
name:en字段加的多层转义写法混乱,最终传到psql时根本无法识别为合法的带双引号的字段名
解决方案
根据是否需要插入Bash变量,选对应写法即可:
1. 不需要插入Bash变量的固定SQL写法
用单引号包裹su -c后的整段命令,从根源减少转义层数:
sudo docker exec dc2b20d450 su -l postgres -c '/usr/lib/postgresql/14/bin/psql --dbname=postgres --username=postgres -c "UPDATE planet_osm_point SET name_display=name WHERE \"name:en\" IS NULL OR char_length(trim(both from \"name:en\"))<1 OR \"name:en\"=name;"'
2. 需要插入Bash变量的写法
如果要在SQL中嵌入Bash变量,用双引号包裹外层命令,按层级加转义即可,示例如下:
# 自定义的Bash变量,比如要动态指定判断的多语言字段 lang_col="name:en" sudo docker exec dc2b20d450 su -l postgres -c "/usr/lib/postgresql/14/bin/psql --dbname=postgres --username=postgres -c \"UPDATE planet_osm_point SET name_display=name WHERE \\\"${lang_col}\\\" IS NULL OR char_length(trim(both from \\\"${lang_col}\\\"))<1 OR \\\"${lang_col}\\\"=name;\""
3. 低维护成本的管道写法(推荐)
如果觉得多层转义容易出错,可以用Here Document把SQL通过管道传给psql,几乎不需要额外转义,加变量也更直观:
# 自定义Bash变量 lang_col="name:en" sudo docker exec -i dc2b20d450 su -l postgres -c '/usr/lib/postgresql/14/bin/psql --dbname=postgres --username=postgres' <<EOF UPDATE planet_osm_point SET name_display=name WHERE "${lang_col}" IS NULL OR char_length(trim(both from "${lang_col}")) < 1 OR "${lang_col}" = name; EOF
注意:如果嵌入的Bash变量来自不可信的用户输入,必须做内容校验,避免SQL注入风险。
内容的提问来源于stack exchange,提问作者Kishor
相关产品推荐
相关产品推荐

