如何将 sqlplus 与 heredoc 和文件替换参数一起使用

How to use sqlplus with a heredoc and file substitution args

提问人:Christian Bongiorno 提问时间:9/6/2023 最后编辑:Christian Bongiorno 更新时间:9/6/2023 访问量:45

问:

因此,我试图通过将要执行的脚本包装在“页眉”和“页脚”中来将一些默认配置应用于每次调用,以便获得一致的执行。sqlplus

但是,当我实现这个 bash 函数时,我意识到某些脚本具有参数,当我尝试在使用 heredoc 进行“包装”时将它们传递给时,sqlplus 感到困惑并认为用于替换的参数对它来说是实际的 (sqlplus)。sqlplus

那么,除了创建临时文件之外,有没有办法仍然使用 heredoc(或任何 stdin 流)和脚本参数?envsubst

下面的代码(将其源在文件中,或者简单地将函数复制并粘贴到交互式 shell 中):


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

example.sql:

select &1 + &2 from dual

预期用途:

exec_sql_file queries/example.sql 1 3
         4

需要的是heredoc之后"${@:2}"

它尝试执行的名义 CLI 是:

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
bash sqlplus heredoc

评论

0赞 Cyrus 9/6/2023
请查看如何创建最小、完整和可验证的示例
0赞 Cyrus 9/6/2023
请在脚本中添加一个有效的 shebang (),然后将其粘贴到 shellcheck.net#!/bin/bash
0赞 Barmar 9/6/2023
你输入了命令行参数,为什么不把它当作命令的参数呢?应该如何使用它们?"${@:2}"
0赞 Cyrus 9/6/2023
删除最后一个 . 之前的所有空格。EOF
0赞 Barmar 9/6/2023
@Cyrus 他们用了.这意味着它可以用制表符缩进。<<-EOF

答:

1赞 2 revsCharles Duffy #1

heredoc 更改了 stdin 的定向位置。它不会充当命令行参数或变成命令行参数,因此将参数放在 heredoc 之后(或在此之前)的想法没有任何意义:heredocs 不参与命令行参数排序。

我怀疑你可能想要这样的东西:

buildArgs() {
  printf '%s\n' \
    'set heading off;' \
    'set feedback off;' \
    'set linesize 10000;' \
    'set newpage NONE;' \
    "set colsep '|'" \
  cat -- "$script"
  printf '%s\n' \
    'exit'
}


sqlplus -s "$username/$password@sredb1_high" "@"<(buildArgs) "${@:2}" |
  tail -n "+$#" |
  sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'

...where 是(在 Linux 上 - 这因平台而异)被 shell 替换为类似 的东西,其中 是可以读取输出的文件名。"@"<(buildArgs)@/dev/fd/63/dev/fd/63buildArgs

(请注意,这是一个 FIFO,所以它只能读取一次;如果你需要它是可查找的,这需要一个单独的解决方案;zsh 有 形式的内置语法,但据我所知,这个功能目前在 bash 中并不可用)。=(buildArgs)

评论

0赞 Christian Bongiorno 9/7/2023
这实际上与临时文件不同吗,因为它创建了?我的意思是,我不必管理它,但你确实给了我一些其他的想法/dev/fd/xx
0赞 Charles Duffy 9/7/2023
@ChristianBongiorno,不是真正的文件系统;所有打开的文件描述符都存在在那里。在实践中,在 Linux 上,它是一个链接,就像所有 procfs 一样,它是完全合成的。因此:指你的 stdin,指你的 stdout,指你的 stderr;如果您调用并返回 FD 3,则会自动出现 .事实上,在 Linux 上,如果你 ,你会引用 或 的东西,它们最终都是一回事。/dev/fd/proc/self/fd/dev/fd/0/dev/fd/1/dev/fd/2open()/dev/fd/3ls -l /dev/stdout/dev/fd/1/proc/self/fd/1