sqlplus 不在指定的输出文件中记录编译错误
Posted
技术标签:
【中文标题】sqlplus 不在指定的输出文件中记录编译错误【英文标题】:sqlplus doesn't log compilation errors in designated out file 【发布时间】:2012-10-22 16:38:52 【问题描述】:这是我调用 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')进入,而底部的第二个没有,即执行没有到达它,因为两者之间有错误。为什么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 编译错误的信息
【问题讨论】:
如何更改调用 sqlplus 以将 STDERR 定向到定义文件的行?更好的是,我如何将 STDOUT 和 ERR 结合到同一个日志文件中? 其实sqlplus的摄入需要不同的格式 我实际上质疑“它没有被打印在 stdout 它被打印在 stderr”的假设,因为当你手动在 sqlplus 中(所以不从脚本调用)时,你会得到错误和常规输出在同一个流中 我认为这与 sqlplus 行中的 语法有关 【参考方案1】:SQL*Plus 不会将输出分开到stdout
和stderr
。这实际上与重定向或外壳无关;如果您将该代码粘贴到交互式 SQL*Plus 会话中,您也会遇到同样的问题。您没有看到编译错误,因为您没有告诉它您已经提供了需要编译和执行的完整块。
一旦你开始a PL/SQL block - 在这种情况下是DECLARE
- 之后的所有内容都被视为同一块的一部分,直到你提供terminating .
或/
,tells SQL*Plus to execute the current buffer,它这是整个匿名块。您希望打印 The end
的第二个普通 select
被视为不完整块的一部分,因此它也永远不会运行。您的脚本在遇到/
之前终止(在!!
),因为您没有脚本,所以SQL*Plus 不知道要执行什么。如果您确实将其粘贴到 SQL*Plus 中,您将在第 15 行看到相同的输出和等待输入的光标;脚本版本在此时被终止,大致相当于在 shell 脚本等待输入时按 ctrl-C。
但是您还缺少该块的其他几个关键部分。如果您在程序部分和终止的/
周围添加BEGIN
和END
,您将开始看到错误,其中一些只是缺少分号;这应该让你开始:
#!/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 OTHERS
和没有 RAISE
的异常块,因为它会吞下发生的任何错误它试图删除表。)
顺便说一句,如果您不想在输出中看到 SQL*Plus 版本、连接和提示/行号信息,可以使用-s[ilent] 选项来抑制它。
【讨论】:
【参考方案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
【讨论】:
我猜是 不幸的是,这并没有。输出是一样的:第一次打印有效,第二次从未到达,中间没有有用的错误信息。 检查上面最新的帖子修正,从 EDIT AFTER MORE EXPERIMENTATION 开始: 是的,你没有处理异常。让我缩短它以删除特定表(如果存在)。 我会看看***.com/questions/12058020/…以上是关于sqlplus 不在指定的输出文件中记录编译错误的主要内容,如果未能解决你的问题,请参考以下文章
sqlplus中怎么将你全部的操作和结果记录保存到你指定的文件里