与 xp_cmdshell 一起使用时调用 SQLCMD 不起作用

The*_*war 5 sql-server powershell sql-server-2012 xp-cmdshell

我正在尝试从一堆服务器获取磁盘空间报告并将它们插入到 sql 表中..

下面是我正在尝试做的示例,服务器名称将从表中填充并作为逗号分隔列表传递。即使传递一台服务器也有相同的结果

set @ps = 'powershell.exe -noexit -c "

$computers=Get-WmiObject -Class Win32_Volume '+@servernames+'
foreach($computer in $computers) 
{ 
$pscomputername=$computer.pscomputername 
$name=$computer.name 
$capacity=$computer.capacity 
$freespace=$computer.freespace 
$Label=$computer.Label 

$insertquery=" 
INSERT INTO [dbo].[temp_disksdata] 
           (
           [servername] ,
           [DiskName] ,
           [Capacity(GB)] ,
           [FreeSpace(GB)],
           [label]
           )
     VALUES 
           (''$pscomputername'' ,
           ''$name'' ,
           ''$capacity'',
           ''$freespace'',
           ''$Label''
           ) 
GO
" 

Invoke-SQLcmd  -query $insertquery -ServerInstance ''someserver'' -Database dbname

}

"';
Run Code Online (Sandbox Code Playgroud)

现在我尝试将变量传递给 xp_cmdshell,如下所示

execute xp_cmdshell @ps;
Run Code Online (Sandbox Code Playgroud)

当在 SSMS 中调用时,上面返回 null,但在 powershell 中工作..

任何想法为什么?

以下是我尝试过的一些事情

1. shell 和 ssms 都使用相同的帐户(admin) 2.
尝试了多种方法,例如导入模块
3.XP_CMDshell 有效,但这仅针对此查询返回 null
4. 我尝试添加 -nowait,但这并没有也有帮助

我一直在尝试从一天多的时间完成这项工作,但这不起作用..我必须使用 xp_cmdshell,因为不允许编写 ac# 应用程序。bat 文件没有帮助,因为服务器名称作为逗号传递分开的名单。

我正在修改代码并被告知不要在被问到时重写

如果您需要更多信息,请告诉我

Repro:
下面是整个打印命令,如果你删除 powershell.exe 和 -c ,下面将在 powershell 中运行..

powershell.exe  -c "

$computers=Get-WmiObject -Class Win32_Volume 'servername'
foreach($computer in $computers) 
{ 
$pscomputername=$computer.pscomputername 
$name=$computer.name 
$capacity=$computer.capacity 
$freespace=$computer.freespace 
$Label=$computer.Label 

$insertquery=" 
INSERT INTO [dbo].[temp_disksdata] 
           (
           [servername] ,
           [DiskName] ,
           [Capacity(GB)] ,
           [FreeSpace(GB)],
           [label]
           )
     VALUES 
           ('$pscomputername' ,
           '$name' ,
           '$capacity',
           '$freespace',
           '$Label'
           ) 

" 

Invoke-SQLcmd  -query $insertquery -ServerInstance 'servername' -Database dbname 

}

"
Run Code Online (Sandbox Code Playgroud)

Sol*_*zky 5

这里有几个问题:

主要问题是 CMD shell 在每行通过 return 进入时执行;它不会等待诸如 的“命令结束”指示符;,也不会尝试通过解析来找出它(就像 SQL 在提交批处理时所做的那样),因为无法知道 a 是否return结束了“命令”或不是。通常你可以使用^用于行继续,但这似乎不适用于输入参数(至少不是带有未闭合引号的参数)。

因此,为此您需要将所有内容减少到一行。这很简单,因为您可以将回车符(字符 13)替换为空字符串,并将换行符/换行符(字符 10)替换为空格。但是,单独这样做是行不通的,因为 PowerShell 语句需要用换行符分号分隔。目前正在使用换行符,但如果我们用空格替换它们,则将需要分号。

  1. 因此,第 1 步是在每个实际的 PowerShell 命令后附加一个分号。一般来说,这可能是一种很好的形式,与 T-SQL 相同。

  2. 第 2 步将对变量进行两次REPLACE调用,@ps以将其转换为单行脚本。

  3. 您已嵌入双引号,用于创建INSERT语句。那些需要用反斜杠 ( \)转义。

  4. Get-WmiObject当您尝试在此处使用它时,它似乎不起作用。我不得不添加-ComputerName以传递系统的名称。如果我将其留空,则它返回本地系统信息,但如果您想返回远程服务器的信息,那么您可能需要使用-ComputerName开关并一次传入一台服务器。这很可能需要一个额外的外部循环来处理多个服务器名称(并且您将在那里传入@ServerNames以获取拆分以遍历列表)。

  5. “容量”和“自由空间”真的作为字符串存储在数据库中吗?如果没有,您可能希望删除INSERT语句中这些“值”周围的单引号。

以下至少执行。您可以从那里进行调试。我遇到了错误,没有找到“Invoke-SQLcmd”模块或其他东西。

DECLARE @ServerNames NVARCHAR(4000);
SET @ServerNames = N'ALBRIGHT';

DECLARE @ps NVARCHAR(4000);
SET @ps = N'powershell.exe -noexit -c "

$computers=Get-WmiObject -Class Win32_Volume -ComputerName ' + @ServerNames + N';
foreach($computer in $computers) 
{ 
$pscomputername=$computer.pscomputername;
$name=$computer.name;
$capacity=$computer.capacity;
$freespace=$computer.freespace;
$Label=$computer.Label;

$insertquery=\";
INSERT INTO [dbo].[temp_disksdata] 
           (
           [servername] ,
           [DiskName] ,
           [Capacity(GB)] ,
           [FreeSpace(GB)],
           [label]
           )
     VALUES 
           (''$pscomputername'' ,
           ''$name'' ,
           ''$capacity'',
           ''$freespace'',
           ''$Label''
           ) 
GO
\";

Invoke-SQLcmd  -query $insertquery -ServerInstance ''someserver'' -Database dbname;

}

"';

SET @ps = REPLACE(REPLACE(@ps, NCHAR(13), N''), NCHAR(10), N' ')
PRINT @ps;


EXEC xp_cmdshell @ps;
Run Code Online (Sandbox Code Playgroud)