如何不使用临时文件,通过Heredoc传递参数给带参sqlplus脚本?
解决SQLPlus统一配置与脚本参数传递的冲突问题
我想给每次sqlplus调用添加统一的默认配置,将待执行脚本包裹在“头部”配置代码和“尾部”退出代码中,保证执行一致性。但实现Bash函数时发现,部分SQL脚本包含&1、&2这类替换参数,用Heredoc包裹并传递参数时,sqlplus会产生混淆,将脚本的替换参数误认为是自身的命令行参数。除了创建临时文件的方案外,有没有办法用Heredoc或标准输入流配合脚本参数完成调用?
现有尝试的Bash函数
function exec_sql_file(){ local sql_file=${1:?You must supply and sql_file file to execute} local script_dir username password script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) source "${script_dir}/omc.env" local script="${script_dir}/sql/${sql_file}" sqlplus -s "$username/$password@sredb1_high" <<-EOF "${@:2}" | tail -n "+$#" set heading off; set feedback off; set linesize 10000; set newpage NONE; set colsep '|' $(cat "${script}") exit; EOF } && export -f exec_sql_file
示例SQL脚本(example.sql)
select &1 + &2 from dual
预期用法
exec_sql_file queries/example.sql 1 3 4
目标等效命令
sqlplus -s "$username/$password@sredb1_high" @sql/queries/example.sql 1 3
临时文件方案(非首选)
function exec_sql_file(){ local sql_file=${1:?You must supply and sql_file file to execute} local script_dir username password script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) local env_file="${script_dir}/omc.env" source "${script_dir}/omc.env" local script="${script_dir}/sql/${sql_file}" local tmpFile tmpFile=$(mktemp) cat <<-EOF > "${tmpFile}" set heading off; set feedback off; set linesize 10000; set newpage NONE; set colsep '|' $(cat "${script}") exit; EOF echo exit | sqlplus -s "$username/$password@sredb1_high" "@${tmpFile}" "${@:2}" | tail -n "+$#" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' } && export -f exec_sql_file
无需临时文件的解决方案
可以通过在Heredoc内容开头动态生成DEFINE命令,将传递的参数预先赋值给SQL脚本中的&1、&2等替换变量,让sqlplus直接使用预定义的值,避免混淆命令行参数。修改后的函数如下:
function exec_sql_file(){ local sql_file=${1:?必须提供要执行的SQL文件} local script_dir username password script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) source "${script_dir}/omc.env" local script="${script_dir}/sql/${sql_file}" local arg_index=1 local define_statements="" # 为每个传递的参数生成DEFINE语句 for arg in "${@:2}"; do define_statements+="define &${arg_index}='${arg}';\n" ((arg_index++)) done # 将DEFINE语句、统一配置和脚本内容拼接后传给sqlplus printf "%b\n%s" "$define_statements" "$(cat <<-EOF set heading off; set feedback off; set linesize 10000; set newpage NONE; set colsep '|' $(cat "${script}") exit; EOF )" | sqlplus -s "$username/$password@sredb1_high" | tail -n "+$#" } && export -f exec_sql_file
方案说明
- 遍历函数接收的第2个及以后的参数,生成
define &N='参数值';格式的语句,其中N对应SQL脚本中的&1、&2等占位符。 - 用
printf将这些DEFINE语句、统一配置代码、目标SQL脚本内容拼接成完整的SQL执行流,通过管道传给sqlplus的标准输入。 - sqlplus读取标准输入时,会先执行DEFINE语句完成参数赋值,再执行配置和脚本,此时脚本中的
&N会使用预定义的参数值,不会再尝试读取命令行参数。
内容的提问来源于stack exchange,提问作者Christian Bongiorno
相关产品推荐
相关产品推荐

