我正在尝试从一堆服务器获取磁盘空间报告并将它们插入到 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
}
"';
现在我尝试将变量传递给 xp_cmdshell,如下所示
execute xp_cmdshell @ps;
在 SSMS 中调用时,上面返回 null,但在 powershell 中有效。
任何想法为什么?
以下是我尝试过的一些事情
1.Shell 和 ssms 都使用了相同的帐户(管理员) 2.
尝试了多种方法,例如导入模块
3.XP_CMDshell 有效,但这仅针对此查询返回 null
4.我尝试添加 -nowait,但事实并非如此也有帮助
我已经尝试从一天多的时间完成这项工作,但这不起作用..我必须使用 xp_cmdshell 因为不允许编写 ac# 应用程序。Bat 文件无济于事,因为服务器名称作为逗号传递单独的列表。
我正在修改代码,并在被问到时被告知不要重写
如果您需要任何进一步的信息,请告诉我
Repro:
下面是 print 的整个命令,如果你删除 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
}
"
这里有几个问题:
主要问题是 CMD shell 通过返回执行每一行。它不会等待“命令结束”指示符,
;
例如return不是。通常,您可以使用^
续行,但这似乎不适用于输入参数(至少不是带有未闭合引号的参数)。因此,为此,您需要将所有内容减少到一行。这很简单,因为您可以将回车(字符 13)替换为空字符串,将换行符/换行符(字符 10)替换为空格。但是,仅此一项是行不通的,因为 PowerShell 语句需要用换行符或分号分隔。目前正在使用换行符,但如果我们用空格替换它们,则需要分号。
因此,第 1 步是将分号附加到每个实际的 PowerShell 命令。这可能是一般的好格式,与 T-SQL 相同。
第 2 步将对变量进行两次
REPLACE
调用,@ps
以将其转换为单行脚本。您嵌入了双引号,用于创建
INSERT
语句。这些需要用反斜杠 (\
) 转义。Get-WmiObject
当您尝试在此处使用它时,它似乎不起作用。我必须添加-ComputerName
以传递系统的名称。如果我将其留空,那么它会返回本地系统信息,但如果您想返回远程服务器的信息,那么您可能需要使用-ComputerName
开关并一次传入一台服务器。这很可能需要一个额外的外部循环来处理多个服务器名称(并且您将在那里传递@ServerNames
以获取拆分以遍历列表)。“容量”和“可用空间”真的作为字符串存储在数据库中吗?如果不是,您可能希望删除语句中这些“值”周围的单引号
INSERT
。以下至少执行。您可以从那里进行调试。我在找不到“Invoke-SQLcmd”模块或其他东西时遇到错误。
问题可能是您尝试执行的命令中的换行符。考虑以下情况之间的区别。
要从 执行多行
powershell
脚本xp_cmdshell
,请尝试将整个命令保存到文件中,然后在单行调用中引用该文件。例如,鉴于此文本......保存为文件
C:\temp\foo.ps1
,以下应该成功...有很多方法可以使用 T-SQL 将任意文本写入文件系统,但更聪明的做法可能是拥有一个静态文件并
@servernames
从另一个调用 DB 中获取 - 例如改变这个.........在一个 sql 脚本中...
...在powershell脚本中。
强烈推荐使用很棒的 dbatools Powershell 模块。
只需安装此模块:
然后使用Get-DbaDatabaseSpace函数:
或者对于多个实例:
您也可以为此任务使用非常有用的 PowerBI 模板:https ://sqljana.wordpress.com/2018/04/30/sql-server-quick-space-file-layout-analysis-with-powershell-and-powerbi/