You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Perl脚本调用psql执行DO块时未终止美元引号错误求助

问题描述

我在Perl脚本中通过以下方式执行SQL语句:

my $cmd = "PGPASSWORD=$pw  /usr/bin/psql  -h ${host} -p ${port} -d ${dbname} -U ${user} -c '${dropTblsSql}' -q  >> logfile.log"
my $out = `$cmd`;

其中SQL语句存储在$dropTblsSql变量中:

my $dropTblsSql = "
DO $$
 DECLARE 
  r record;
BEGIN
   FOR r IN SELECT quote_ident(tablename) AS tablename, quote_ident(schemaname) AS schemaname FROM pg_tables WHERE schemaname = 'rbm_pu7_mdt'
   LOOP
     EXECUTE format('DROP TABLE IF EXISTS %I.%I CASCADE', r.schemaname, r.tablename);
   END LOOP;
END;
$$ LANGUAGE plpgsql;
";

执行时出现Unterminated dollar-quoted string at or near "$$"错误。我尝试过转义美元符号(\$\$)、将其替换为单引号(并转义其他单引号),但均无效。仅通过文件执行(-f path_to_file)可成功,但我希望避免该方式。请问如何解决双美元引号字符串的问题?

解决方案

方法1:调整psql参数的引号包裹方式

将psql的-c参数改用双引号包裹SQL内容,同时转义SQL中的$$为\$\$,避免Perl和shell解析冲突。修改后代码如下:

my $dropTblsSql = "
DO \$\$
 DECLARE 
  r record;
BEGIN
   FOR r IN SELECT quote_ident(tablename) AS tablename, quote_ident(schemaname) AS schemaname FROM pg_tables WHERE schemaname = 'rbm_pu7_mdt'
   LOOP
     EXECUTE format('DROP TABLE IF EXISTS %I.%I CASCADE', r.schemaname, r.tablename);
   END LOOP;
END;
\$\$ LANGUAGE plpgsql;
";

my $cmd = "PGPASSWORD=$pw  /usr/bin/psql  -h ${host} -p ${port} -d ${dbname} -U ${user} -c \"${dropTblsSql}\" -q  >> logfile.log";
my $out = `$cmd`;

原理:双引号包裹的shell参数不会将内部单引号视为边界,转义后的\$\$会被shell正确传递给psql,识别为PL/pgSQL的美元引号。

方法2:使用shell转义工具处理SQL字符串

借助Perl的String::ShellQuote模块自动转义所有shell特殊字符,无需手动处理符号:

use String::ShellQuote;

my $dropTblsSql = "
DO $$
 DECLARE 
  r record;
BEGIN
   FOR r IN SELECT quote_ident(tablename) AS tablename, quote_ident(schemaname) AS schemaname FROM pg_tables WHERE schemaname = 'rbm_pu7_mdt'
   LOOP
     EXECUTE format('DROP TABLE IF EXISTS %I.%I CASCADE', r.schemaname, r.tablename);
   END LOOP;
END;
$$ LANGUAGE plpgsql;
";

my $escaped_sql = shell_quote($dropTblsSql);
my $cmd = "PGPASSWORD=$pw  /usr/bin/psql  -h ${host} -p ${port} -d ${dbname} -U ${user} -c $escaped_sql -q  >> logfile.log";
my $out = `$cmd`;

该模块会自动处理所有可能导致shell解析错误的字符,确保SQL完整传递给psql。

方法3:改用Perl PostgreSQL驱动执行(推荐)

直接使用DBD::Pg模块连接数据库执行SQL,彻底绕开shell转义问题,代码更安全可靠:

use DBI;
use DBD::Pg;

# 建立数据库连接
my $dbh = DBI->connect(
    "dbi:Pg:dbname=$dbname;host=$host;port=$port",
    $user,
    $pw,
    {AutoCommit => 1}
) or die DBI->errstr;

my $dropTblsSql = "
DO $$
 DECLARE 
  r record;
BEGIN
   FOR r IN SELECT quote_ident(tablename) AS tablename, quote_ident(schemaname) AS schemaname FROM pg_tables WHERE schemaname = 'rbm_pu7_mdt'
   LOOP
     EXECUTE format('DROP TABLE IF EXISTS %I.%I CASCADE', r.schemaname, r.tablename);
   END LOOP;
END;
$$ LANGUAGE plpgsql;
";

# 执行SQL
$dbh->do($dropTblsSql) or die $dbh->errstr;

# 断开连接
$dbh->disconnect;

这种方式直接与数据库交互,无需处理任何shell转义,同时避免了命令行执行可能带来的安全风险。

内容的提问来源于stack exchange,提问作者JanFi86

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:17:23