SQL Agent - Etapa do PowerShell “erro de sintaxe”

10

Eu tenho a seguinte configuração de código na etapa de trabalho do SQL Agent que não está sendo executada. A mensagem recebida é simplesmente:

Não foi possível iniciar a execução da etapa 1 (motivo: linha (46): erro de sintaxe). A etapa falhou.

A duração do trabalho é: 00:00:00

A linha 46 deste script é uma here-string:


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

Esse script é executado perfeitamente fora do SQL Server Agent.

Tabela ServerNames:


CREATE TABLE ServerTable (
ServerName varchar(50)
)

A tabela de espaço em disco seria algo semelhante a:


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

Script do 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
    }
}

}

EDITAR

Configure etapas adicionais para tentar usar o tipo CmdExec como PowerShell -Command "& { }". Com essa execução, o script é executado e o log de erros é preenchido para os blocos de captura apropriados, mas nenhum dado de espaço em disco é gravado na tabela.

O histórico do agente para esta execução mostra uma mensagem de aviso:

Mensagem executada como usuário: NT Service \ SQLAgent $ myInstance. AVISO: Alguns nomes de comandos importados incluem verbos não aprovados que podem torná-los menos detectáveis. Use o parâmetro Verbose para obter mais detalhes ou digite Get-Verb para ver a lista de verbos aprovados. Código de saída do processo 0. A etapa foi bem-sucedida.

Se eu usar a mesma etapa de tipo, mas chamar o arquivo: PowerShell -File MyScript.ps1 Recebo a mesma mensagem do tipo de etapa do PowerShell acima por erro de sintaxe.

RolandoMySQLDBA
fonte

Respostas:

11

Isso não é muito intuitivo e nunca consegui encontrar nada concreto sobre a explicação [por exemplo, nenhum BOL ou documento oficial exato foi encontrado].

O erro de sintaxe no trabalho do SQL Agent é um erro de sintaxe T-SQL baseado em tokens de usuário . Isso significa basicamente que um Operador de Subexpressão do PowerShell é tratado como um Token para o SQL Server Agent. Portanto, no PowerShell, isso $( )parece ser tratado como uma sequência de caracteres reservada para o SQL Server Agent. Então SQL Agent estaria procurando algo parecido com este exemplo T-SQL do artigo BOL referenciada: PRINT N'Current database name is $(A-DBN)' ; .

Portanto, no meu script, quando chegou ,@DriveLetter = '$($c.DriveLetter)', o "$ c.DriveLetter" não é um dos tokens permitidos.

A solução alternativa para isso é simplesmente não usar instruções de subexpressão no PowerShell. Eu faria os ajustes no meu script para isso:


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

teria que ser modificado para algo como isto:


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

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

Fiz os ajustes acima para o meu script e ele funciona perfeitamente agora.


fonte
1

A aspas simples (') é um caractere especial para o PowerShell e delimita as strings. A diferença entre aspas duplas e simples está na questão de como ela interpreta a sequência . Como é um caractere especial, você deve escapar usando sotaque grave (`) . Essa sintaxe deve funcionar para você:

 @ServerName = `'$($c.ServerName)`'
,@DriveLetter = `'$($c.DriveLetter)`'
Mike Fal
fonte
Está dentro de uma string here que não a trata como um caractere especial, a considera como é. A adição do acento grave, embora não tenha efeito, ainda recebe a mesma mensagem de sintaxe do SQL Agent para o mesmo número de linha.