从XP_CMDSHELL获取结果

时间:2012-02-29 14:47:13

标签: sql-server stored-procedures xp-cmdshell

我一直在网上搜索一下,似乎从XP_CMDSHELL获取结果的唯一方法是将它们存储到临时表中。真的没有更简单的方法吗?

来自专家交流:

  

不,xp_cmdshell不会从exe返回任何信息。你必须使用以下内容   语法,如果您不在master数据库中运行它。 master..xp_cmdshell。你不得不   授予您的用户在master数据库中执行此过程的权限。你必须拥有   你的exe将信息插入其自身,因为它无法将信息返回给进程   叫它。

和...

虽然@result只从xp_cmdshell获取返回值,但您可以捕获结果 直接插入表中的命令......如下所示:

... YMMV

set nocount on
declare  @filepath   varchar(255),
         @cmd        varchar(255),
         @rc         int

select   @filepath = 'c:\temp\'         
select   @cmd      = 'dir ' + @filepath + '~*.tmp'

create table #output (output varchar(255) null)
insert #output exec @rc = master..xp_cmdshell @cmd
select * from #output where output is not null
drop table #output

2 个答案:

答案 0 :(得分:20)

没有更简单的方法可以从xp_cmdshell捕获STDOUT / STDERR反馈;至少有一种选择,但它不能被归类为更容易:
可以将命令的输出重定向到文本文件作为命令的一部分,然后使用OPENROWSET读取文本文件。

BTW上面发布的脚本中至少有一个错误。 xp_cmdshell的文档声明它将命令输出返回为nvarchar(255) 此外,临时表应该有一个标识列,否则结果可能不会以正确的顺序显示:

...
create table #output (id int identity(1,1), output nvarchar(255) null)
insert #output (output) exec @rc = master..xp_cmdshell @cmd
select * from #output where output is not null order by id
drop table #output

答案 1 :(得分:1)

这就是我最终做的......我今天回来查看了你的回复。我昨天处于实时紧缩状态,所以开始向临时表的方向工作,因为它是一个确定的工作解决方案。我选择避免创建临时文件,因为在内部处理内容似乎很容易或更容易,因为我真的只是将它用作剪贴板。如果需要,我将进行的一个更改是为临时表名称添加一个唯一的数字,虽然我不认为我必须担心这些被同时处理(这意味着存储过程的第二次调用可能会转储临时表) cmd shell正在运行)。我们会看到......

我调用存储过程(向下看)来加密密码: 下面的代码已经过修改,以使其自给自足。我实际上并没有手动设置密码,因为这基本上是一个密码卸载/同步解决方案。

DECLARE @password       VARCHAR(64)
DECLARE @encryptedpass  VARCHAR(128);

SET @password = '1234'

BEGIN TRY
    EXEC pass_encrypt @password, @encryptedpass = @encryptedpass OUTPUT
END TRY
BEGIN CATCH
    PRINT 'ERROR'
    RETURN
END CATCH
SELECT @encryptedpass

以下是加密存储过程: 要检查并确保程序正确执行而不必猜测返回代码指示失败的原因,我有额外的代码(此处未列出)检查@@ rowset。如果它超过1,我知道出现了问题,我可以捕获/返回实际错误(如果需要),而不仅仅是编写我自己的消息,说明它失败而没有给出任何原因。实际检查这种方式对于调试或将错误记录到另一个表中以供将来检查更有用 - 而不是实时解释,因为我不会将这样的错误发送回最终用户。

USE [**my_database**]
GO

SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO


CREATE procedure [dbo].[pass_encrypt]
(   @password       VARCHAR(64),
    @encryptedpass  VARCHAR(128) OUTPUT
)
AS
BEGIN
    DECLARE @command        VARCHAR(200)
    SET @command = **'C:\encrypt_pwd.exe**' + ' "' + @password + '"'

    BEGIN
        IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[#temppass]') AND type in (N'U')) 
            DROP TABLE [dbo].[#temppass]
        BEGIN TRY
            CREATE TABLE #temppass(encrypted varchar(1000))
            INSERT INTO #temppass execute xp_cmdshell @command
            IF (@@ROWCOUNT > 1)
                BEGIN
                    SET @encryptedpass = NULL
                    DROP TABLE #temppass
                    RETURN
                END
            ELSE
                BEGIN
                    SELECT @encryptedpass = encrypted FROM #temppass
                END
            --SELECT @encryptedpass
        END TRY
        BEGIN CATCH
            SET @encryptedpass = NULL
            DROP TABLE #temppass
            RETURN
        END CATCH
        --SELECT encrypted FROM #temppass
        DROP TABLE #temppass
    END
END