SQL Agent-PowerShell步骤“语法错误”


10

我在SQL Agent Job步骤中未运行的以下代码设置。收到的消息很简单:

无法开始执行步骤1(原因:第46行:语法错误)。该步骤失败。

工作时间为: 00:00:00

该脚本的第46行是here-string


    ,@DriveLetter = '$($c.DriveLetter)'

该脚本可以在SQL Server代理之外完美运行。

ServerNames表:


CREATE TABLE ServerTable (
ServerName varchar(50)
)

磁盘空间表将类似于:


CREATE TABLE DiskSpace (
ID int IDENTITY(1,1),
ServerName varchar(50),
DriveLetter varchar(2),
DiskSpaceCapacityGB decimal(12,5),
DiskSpaceFreeGB decimal(12,5)
)

PowerShell脚本:


This command is to allow SQL Agent job to show failure in event PowerShell script errors
Default behavior in SQL Agent for PowerShell steps it 'Continue'
$erroractionpreference = "Stop"

$sqlInstance = 'server1' $sqlDatabase = 'database1'

$qServerList = @" Select ServerName From ServerTable "@

$srvList = Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $sqlDatabase -Query $qServerList | Where-Object {$_ -ne '' -or $_ -ne $null} | Select-Object -ExpandProperty ServerName

foreach ($s in $srvList) { if (Test-Connection -ComputerName $s -Count 1 -ErrorAction 'SilentlyContinue') { try { $cServer = Get-WmiObject Win32_Volume -ComputerName $s -ErrorAction 'Stop' | where {$.DriveType -eq 3 -and $.DriveLetter } | Select-Object @{Label="ServerName";Expression={$s}}, @{Label="DriveLetter";Expression={$.DriveLetter}}, @{Label="DiskSpaceCapacityGB";Expression={"{0:N0}" -f($.Capacity/1GB)}}, @{Label="DiskFreeSpaceGB";Expression={"{0:N2}" -f($_.FreeSpace/1GB)}}

        foreach ($c in $cServer) 
        {
            $qAddServerDiskSpace = @"

EXEC [dbo].[StoredProcInsert] @ServerName = '$($c.ServerName)' ,@DriveLetter = '$($c.DriveLetter)' ,@DiskSpaceCapacityGB = $($c.DiskSpaceCapacityGB) ,@DiskFreeSpaceGB = $($c.DiskFreeSpaceGB) "@

            try
            {
                Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $sqlDatabase -Query $qAddServerDiskSpace -ErrorAction 'Stop'
            }
            catch
            {
                $ErrorMsg = $_.Exception.Message
                $fullMsg = "Error writing executing dbo.StoredProcInsert for $s - $ErrorMsg"
                Return $fullMsg
            }           
        }
    }
    catch
    {
        $ErrorMsg = $_.Exception.Message
        $qAddPSErrorLog = @"

EXEC [dbo].[StoredProcInsert_Error] @ServerName = '$($cServer.ServerName)' , @ErrorText = '$($ErrorMsg)' "@ try { Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $sqlDatabase -Query $qAddPSErrorLog -ErrorAction 'Stop' } catch { $ErrorMsg = $_.Exception.Message $fullMsg = "Error writing executing dbo.StoredProcInsert_Error for $s - $ErrorMsg" Return $fullMsg } } } else { $qAddPSErrorLog = @" EXEC [dbo].[StoredProcInsert_Error] @ServerName = '$($cServer.ServerName)' , @ErrorText = 'Unable to ping server' "@

    try 
    {
        Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $sqlDatabase -Query $qAddPSErrorLog  -ErrorAction 'Stop'
    }
    catch 
    {
        $ErrorMsg = $_.Exception.Message
        $fullMsg = "Error writing executing dbo.StoredProcInsert_Error for $s - $ErrorMsg"
        Return $fullMsg
    }
}

}

编辑

设置其他步骤以尝试将CmdExec类型用作PowerShell -Command "& { }"。执行此脚本后,将运行脚本,并为相应的catch块填充错误日志,但不会将磁盘空间数据写入表中。

此运行的代理历史记录显示警告消息:

以用户身份执行的消息:NT Service \ SQLAgent $ myInstance。警告:某些导入的命令名称包含未经批准的动词,可能会使它们难以被发现。使用Verbose参数获取更多详细信息,或键入Get-Verb以查看批准的动词列表。进程退出代码0。该步骤成功。

如果我使用相同的类型步骤但调用了文件:PowerShell -File MyScript.ps1 语法错误,我将收到与上述PowerShell步骤类型相同的消息。

Answers:


11

这不是一个很直观的方法,我从中找不到任何具体的解释(例如,找不到确切的BOL或白皮书)。

SQL Agent作业中的语法错误是基于用户令牌的T-SQL语法错误。因此,这基本上意味着将PowerShell子表达式运算符视为SQL Server代理的令牌。因此,在PowerShell中,这$( )似乎被视为SQL Server代理的保留字符序列。因此,SQL Agent将从参考的BOL文章中寻找类似T-SQL示例的内容 PRINT N'Current database name is $(A-DBN)' ;

因此在我的脚本中,到达,@DriveLetter = '$($c.DriveLetter)'$ 时,“ $ c.DriveLetter”不是允许的令牌之一。

解决方法是不使用PowerShell中的子表达式语句。我将在脚本中进行调整,这样:


$qAddServerDiskSpace = @"
EXEC [dbo].[StoredProcInsert]
    @ServerName = '$($c.ServerName)'
    ,@DriveLetter = '$($c.DriveLetter)'
    ,@DiskSpaceCapacityGB = $($c.DiskSpaceCapacityGB)
    ,@DiskFreeSpaceGB = $($c.DiskFreeSpaceGB)
"@

将必须修改为以下内容:


$severName = $c.ServerName
$driveLetter = $c.DriveLetter
$capacityGB = $c.DiskSpaceCapacityGB
$freeGB = $c.DiskFreeSpaceGB

$qAddServerDiskSpace = @"
EXEC [dbo].[StoredProcInsert]
   @ServerName = '$serverName'
   ,@DriveLetter = '$driveLetter'
   ,@DiskSpaceCapacityGB = $CapacityGB
   ,@DiskFreeSpaceGB = $freeGB
"@

对我的脚本进行了上述调整,现在可以完美运行。


1

单引号(')是Powershell的特殊字符,用于分隔字符串。双引号和单引号之间的区别在于它如何解释字符串。由于它是一个特殊字符,因此您将需要使用grave-accent(`)对其进行转义。此语法应为您工作:

 @ServerName = `'$($c.ServerName)`'
,@DriveLetter = `'$($c.DriveLetter)`'

它在here-string中,该字符串不将其视为特殊字符,而是直接使用。尽管添加重音符没有影响,但是对于相同的行号,SQL Agent仍然会收到相同的语法消息。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.