Perl脚本嵌入带连字符的PostgreSQL表名psql命令执行报错求助
看起来你碰到了Perl字符串转义和shell命令解析的双重坑!核心问题是你构造命令字符串时,双引号的转义方式不对,导致PostgreSQL最终拿到的SQL里,带连字符的表名vVMware-Vms没被正确的双引号包裹,触发了语法错误。
先分析下你的问题根源
你直接在Linux终端跑的命令是正常的——因为终端里的双引号正确包裹了SQL中的特殊标识符;但放到Perl的qq()里时,你用的"是HTML转义字符,Perl根本不认这个,会直接把它解析成普通的双引号。从Dumper的输出就能看出来:SQL语句里的"Nom"变成了完全未转义的双引号,这就导致shell执行命令时,会提前把-c参数的双引号闭合,最终传给psql的SQL变成了select Nom, Path from vVMware-Vms where Date = '2025-07-22';——这里的vVMware-Vms没了双引号,PostgreSQL会把它当成vVMware减Vms,语法当然炸了。
给你几个可行的解决方案
方案1:用Perl system的列表形式(最推荐,彻底规避转义问题)
这种方式不需要处理任何引号转义,每个命令参数都是独立传递的,不会经过shell解析,还能避免潜在的shell注入风险,简直是双赢:
# 单独设置PGPASSWORD环境变量,不用塞在命令字符串里 $ENV{PGPASSWORD} = '****'; # 把每个命令参数拆成列表元素 my @psql_cmd = ( 'psql', '--username=****', '-h', '*****', '-d', '*****', '-p', '****', '-P', 'pager=off', '-t', '--csv', '-c', 'select "Nom", "Path" from "vVMware-Vms" where "Date" = \'2025-07-22\';' ); # 执行命令 my $exit_code = system(@psql_cmd);
这样psql会直接拿到带正确双引号的SQL,表名vVMware-Vms能被PostgreSQL正常识别。
方案2:修复qq()里的双引号转义(如果坚持用字符串形式)
Perl的qq()上下文里,要用反斜杠\转义内部双引号,而不是HTML的"。调整后的写法:
my $Cmd = qq(PGPASSWORD=**** psql --username=**** -h ***** -d ***** -p **** -P pager=off -t --csv -c "select \\"Nom\\", \\"Path\\" from \\"vVMware-Vms\\" where \\"Date\\" = '2025-07-22';");
用这种方式构造的命令,Dumper输出后会看到SQL里的标识符被正确转义的双引号包裹,shell执行时就不会提前闭合引号了。
另外你也可以换用Perl的q()构造器,选一个和命令里字符不冲突的分隔符(比如!),这样内部的双引号、单引号都不需要转义:
my $Cmd = q!PGPASSWORD=**** psql --username=**** -h ***** -d ***** -p **** -P pager=off -t --csv -c "select \"Nom\", \"Path\" from \"vVMware-Vms\" where \"Date\" = '2025-07-22';"!;
内容来源于stack exchange

