0

这是我调用 sqlplus 的 shell 脚本:

#!/usr/bin/ksh

sqlplus $USER/$PASSWD@$DB<<!!>./logFile.log

SELECT 'IN sqlplus' FROM dual;

DECLARE tbl_xst integer;

SELECT COUNT(*) INTO tbl_xst FROM SYS.ALL_TABLES
WHERE table_name = 'SOME_TABLE'
AND OWNER = 'SOME_OWNER';

IF tbl_xst = 1 THEN
BEGIN
    DROP TABLE SOME_OWNER.SOME_TABLE;
END
END IF

SELECT 'The end' FROM dual;
!!

当我运行 shell 脚本并查看日志文件 logFile.log 时,会出现以下内容:

SQL*Plus: Release 10.2.0.3.0 - Production on Mon Oct 22 12:33:05 2012

Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, Automatic Storage Management, OLAP, Data Mining
and Real Application Testing options

SQL> SQL>
'INSQLPLUS
----------
IN sqlplus

SQL> SQL>   2    3    4    5    6    7    8    9   10   11   12   13   14   15  Disconnected from Oracle Database 11g Enterprise Edition Releas
e 11.2.0.2.0 - 64bit Production
With the Partitioning, Automatic Storage Management, OLAP, Data Mining
and Real Application Testing options

如您所见,第一个记录 SELECT 语句 ('IN sqlplus') 使其进入,而底部的第二个 on 则没有,即执行没有到达它,因为两者之间存在错误。为什么 sqlplus 不在日志文件中写入此错误消息?

一些评论后编辑: 我们已经确定它很可能是由于 sqlplus 没有将 STDERR 转储到指定的日志文件中,其中只有 STDOUT 。如何格式化 sqlplus 命令以将两个流(STDOUT 和 ERR)组合到指定的日志文件中?我怀疑<<!!>这里很关键。

更多实验后编辑: 奇怪的是,一些错误消息被转储到 STDOUT 中,我猜想那些试图引用坏 DB 对象的语法正确。如果有一个 SQL 编译错误,它显然是在 STDERR 中。我所做的是在上面的第一个 SELECT 和 DECLARE 之后插入以下行:

DROP TABLE FAKE_NONEXISTING;

该语句是合法的并且可以编译,但 FAKE_NONEXISTING 不存在,我在日志文件中得到以下内容:

SQL> SQL> DROP TABLE FAKE_NONEXISTING
           *
ERROR at line 1:
ORA-00942: table or view does not exist

但是,我在日志文件中没有得到任何似乎是 SQL 编译错误的信息

4

2 回答 2

1

SQL*Plus 不会将输出与stdout和分开stderr。这实际上与重定向或外壳无关;如果您将该代码粘贴到交互式 SQL*Plus 会话中,您也会遇到同样的问题。您没有看到编译错误,因为您没有告诉它您已经提供了需要编译和执行的完整块。

一旦你启动了一个 PL/SQL 块——在这种情况下是DECLARE——之后出现的所有东西都被视为同一块的一部分,直到你提供一个终止符.,或者/,它告诉 SQL*Plus 执行当前缓冲区,这里是整个匿名块。select您希望打印的第二个纯文本The end被视为不完整块的一部分,因此它也永远不会运行。您的脚本在!!遇到/,因为你没有,所以 SQL*Plus 不知道要执行什么。如果您确实将其粘贴到 SQL*Plus 中,您将在第 15 行看到相同的输出和等待输入的光标;脚本版本在此时被终止,大致相当于在 shell 脚本等待输入时按 ctrl-C。

但是您还缺少该块的其他几个关键部分。如果您在程序部分和终止处添加BEGINEND周围,/您将开始看到错误,其中一些只是缺少分号;这应该让你开始:

#!/usr/bin/ksh

sqlplus $USER/$PASSWD@$DB<<!!>./logFile.log

SELECT 'IN sqlplus' FROM dual;

DECLARE -- start of PL/SQL block declaration
    tbl_xst integer;
BEGIN -- start of the actual block
    SELECT COUNT(*) INTO tbl_xst FROM SYS.ALL_TABLES
    WHERE table_name = 'SOME_TABLE'
    AND OWNER = 'SOME_OWNER';

    IF tbl_xst = 1 THEN
    BEGIN
        DROP TABLE SOME_OWNER.SOME_TABLE; -- this will error
    END; -- missing semi-colon, but separate BEGIN/END wrapper isn't really needed
    END IF; -- missing semi-colon
END; -- missing end to the block
/ - missing PL/SQL terminator/executor

-- now back in plain SQL mode
SELECT 'The end' FROM dual;
!!

那现在应该报告一个错误DROP。那是一个 DDL 命令,不能像在 PL/SQL 中那样运行;您必须使用动态 SQL,例如:

    IF tbl_xst = 1 THEN
        EXECUTE IMMEDIATE 'DROP TABLE SOME_OWNER.SOME_TABLE';
    END IF;

(这就是 William Stearns 提到的,尽管我强烈建议您不要使用WHEN OTHERSand no异常块,RAISE因为它会吞下尝试删除表时发生的任何错误。)

顺便说一句,如果您不想在输出中看到 SQL*Plus 版本、连接和提示/行号信息,您可以使用-s[ilent]选项来抑制它。

于 2012-10-22T22:37:45.987 回答
-2

要将错误输出也重定向到您的日志文件,您需要在此处将 2>&1 附加到您的行:

sqlplus $USER/$PASSWD@$DB<<!!>./logFile.log 2>&1

根据我自己的评论更新......我可能会添加 >> 而不是 > 来附加现有文件,以便您的日志可以随着时间的推移使用。

#!/usr/bin/ksh

sqlplus $USER/$PASSWD@$DB<<+EOF>>./logFile.log 2>&1

SELECT 'IN sqlplus' FROM dual;

BEGIN
    EXECUTE IMMEDIATE 'DROP TABLE SOME_OWNER.SOME_TABLE';
    EXCEPTION WHEN OTHERS THEN NULL;
END;

SELECT 'The end' FROM dual;
+EOF
于 2012-10-22T17:16:09.063 回答