2012-02-29 100 views
14

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

专家交流:

不,xp_cmdshell的将不会返回从exe文件的任何信息。如果您不在master数据库中运行它,则必须使用以下 语法。 master..xp_cmdshell。您必须 为您的用户授予在主数据库中执行此过程的权限。你必须有 你的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 

回答

18

没有从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 
1

这是我落得这样做......我今天检查回来,看到你的响应。我昨天真的很紧张,所以开始在临时表的方向工作,因为这是一个确定的工作解决方案。我选择避免创建临时文件,因为在内部处理事情似乎很容易或容易,因为我真的只是将它用作剪贴板。如果需要的话,我将做的一个更改是为临时表名称添加一个唯一编号,尽管我不认为我必须担心它们会被同时处理(这意味着存储过程的第二次调用可能会将临时表转储cmd外壳正在运行)。我们将看到...

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

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 

这里是加密存储过程: 要检查并确保程序正确执行,而不必去猜测为什么返回代码指示失败,我有额外的代码(此处未列出),检查@@行集。如果它超过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