来自 Bash 的 SQLPlus 错误处理 - 不工作

Error Handling of SQLPlus from Bash - not working

我已经阅读了关于这个问题的所有相关 link 和类似的 questions/answers,preveland 答案是首先设置 whenever SQLERROR EXIT SQL.SQLCODE; 然后才进行查询,然后检查 SQL Plus return 代码使用:ERRORCODE=$?

这是一个示例脚本:

GetAmountOfChunks()
{
 export CHUNK_AMOUNT=`sqlplus -s $CONSTR<<SQL
    set heading off;
     set trim on;
     set feed off;
     whenever SQLERROR EXIT SQL.SQLCODE;
     select 1/0 from dual;
     --SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
     /
SQL`

当运行处于调试模式时,它给出:

++ sqlplus -s USER/PASS@HOST/DB
+ export 'CHUNK_AMOUNT=     select 1/0 from dual
                      *
ERROR at line 1:
ORA-01476: divisor is equal to zero'
+ CHUNK_AMOUNT='     select 1/0 from dual
                      *
ERROR at line 1:
ORA-01476: divisor is equal to zero'
+ ERRORCODE=0
+ '[' 0 -ne 0 ']'

如您所见,returned 代码为 0! 我预计如果不是 1476,则至少为 196(最右边的 8 个字节),但不是 0,表示成功!

请帮忙...

谢谢。

您的 ERRORCODE 被设置为零,因为那是子程序的退出代码shell 您正在 运行ning SQL*此外,viq 反引号. SQL*Plus 进程的退出代码是 196,但您没有捕获它,并且在 heredoc 中做到这一点并不容易。来自进程的标准输出——这是你正在捕获的——不是那个退出代码,它是正在打印的查询和错误消息。即使您可以捕获它,我也不确定您如何区分 196 是来自错误还是来自您的实际查询。

您可以执行类似 运行 在隐藏错误并打印默认值或实际计算值的块中执行查询,或者只查看输出的最后一行并尝试解释那;但你仍然会反对它显示命令是 运行。 SQL*Plus 有 set echo off 但它对交互式会话没有任何作用,这仍然与输入重定向有关。

另一种方法是创建脚本文件并临时存储输出:

echo "
    set pages 0
    set trim on
    set feed off
    set echo off
    whenever SQLERROR EXIT FAILURE
    select 1/0 from dual;
    --SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
    exit 0;
" > /tmp/GetAmountOfChunks_$$.sql
sqlplus -s -l $CONSTR @/tmp/GetAmountOfChunks_$$.sql > /tmp/GetAmountOfChunks_$$.out
if [[ $? -eq 0 ]]; then
  export CHUNK_AMOUNT=`cat /tmp/GetAmountOfChunks_$$.out`
else
  # whatever you want to do on error; show output file? set default?
  cat /tmp/GetAmountOfChunks_$$.out
fi

rm -f /tmp/GetAmountOfChunks_$$.sql /tmp/GetAmountOfChunks_$$.out

这会创建一个(特定于进程的).sql 文件;执行将输出(减去语句,通过 set echo off)写入 .out 文件;检查 SQL*Plus 退出代码;如果该值为零,则从文件中获取结果。

正如您暗示的那样,依靠 SQL.SQLCODE 来检测 shell 脚本中的错误是危险的,因为您可能会得到一个回绕为零的错误,所以我使用了通用的 FAILURE.如果您需要真正的错误代码,您可以从输出文件中获取它。


另一种使用 PL/SQL 块的方法:

set -f
CHUNK_AMOUNT=`sqlplus -s $CONSTR <<SQL
    set heading off;
    set trim on;
    set feed off;
    whenever SQLERROR EXIT FAILURE;
    set serveroutput on;
    declare
      chunk_amount number;
    begin
      select 1/0 into chunk_amount from dual;
      --SELECT COUNT(*) INTO chunk_amount FROM CNV_CHUNKS_PROC_STATUS;
      dbms_output.put_line(chunk_amount);
    exception
      when others then
        dbms_output.put_line(sqlcode);
    end;
    /
    exit 0
SQL
exit $?`
ERRORCODE=$?

如果 PL/SQL 块 运行 则 ERRORCODE 将为零,如果成功则 CHUNK_AMOUNT 将是计算值,或者 SQL抛出异常时的代码;因为这将是负数(在您的示例中为 -1476),如果您只期望正值,您可以测试它是否符合预期。

如果该块由于语法错误或凭据无效而无法 运行(请注意我偷偷插入的 -l 标志),那么 ERRORCODE 将为 1 并且 CHUNK_AMOUNT 将有错误文本,例如ERROR: ORA-12154: TNS:could not resolve the connect identifier... 或任何实际出错的地方。 set -f 停止错误消息中的 * 从当前目录扩展到文件列表。

或者更简单、更接近您的原作:

set -f
CHUNK_AMOUNT=`sqlplus -s $CONSTR <<SQL
    set heading off;
    set trim on;
    set feed off;
    whenever SQLERROR EXIT FAILURE;
    select 1/0 from dual;
    --SELECT COUNT(*) FROM CNV_CHUNKS_PROC_STATUS;
    exit 0
SQL
exit $?`
ERRORCODE=$?

现在ERRORCODE成功是0,CHUNK_AMOUNT任何错误的计算值ERRORCODE是1,你可以直接测试,实际错误总是在CHUNK_AMOUNT - 但仅作为字符串,您不会以这种方式获得 -1476。