19

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

来自专家交流:

不,xp_cmdshell 不会从 exe 中返回任何信息。如果您不在主数据库中运行它,则必须使用以下语法。大师..xp_cmdshell。您必须授予用户权限才能在 master 数据库中执行此过程。您必须让您的 exe 自行插入信息,因为它无法将信息返回给调用它的进程。

和...

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

嗯嗯...

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
4

2 回答 2

28

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

顺便说一句,上面发布的脚本中至少有一个错误。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
于 2012-02-29T19:11:56.340 回答
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
于 2012-03-01T17:01:34.867 回答