Agent SQL - Étape PowerShell «erreur de syntaxe»

10

J'ai la configuration de code suivante dans l'étape de travail de l'agent SQL qui n'est pas en cours d'exécution. Le message reçu est simplement:

Impossible de démarrer l'exécution de l'étape 1 (raison: ligne (46): erreur de syntaxe). L'étape a échoué.

La durée de l'emploi est: 00:00:00

La ligne 46 de ce script est here-string:


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

Ce script s'exécute parfaitement en dehors de l'Agent SQL Server.

Table ServerNames:


CREATE TABLE ServerTable (
ServerName varchar(50)
)

La table d'espace disque serait quelque chose de similaire à:


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

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

}

ÉDITER

Configurez des étapes supplémentaires pour essayer d'utiliser le type CmdExec en tant que PowerShell -Command "& { }". Avec cette exécution, le script s'exécute et le journal des erreurs est rempli pour les blocs catch appropriés, mais aucune donnée d'espace disque n'est écrite dans la table.

L'historique de l'agent pour cette exécution affiche un message d'avertissement:

Message exécuté en tant qu'utilisateur: NT Service \ SQLAgent $ myInstance. AVERTISSEMENT: certains noms de commande importés incluent des verbes non approuvés qui peuvent les rendre moins détectables. Utilisez le paramètre Verbose pour plus de détails ou tapez Get-Verb pour voir la liste des verbes approuvés. Process Exit Code 0. L'étape a réussi.

Si j'utilise la même étape de type mais que j'appelle le fichier: PowerShell -File MyScript.ps1 j'obtiens le même message que le type d'étape PowerShell ci-dessus pour une erreur de syntaxe.

RolandoMySQLDBA
la source

Réponses:

11

Ceci n'est pas très intuitif et je n'ai jamais pu trouver quoi que ce soit de concret sur l'explication [par exemple, aucun BOL exact ou livre blanc n'a été trouvé].

L'erreur de syntaxe dans le travail de l'Agent SQL est une erreur de syntaxe T-SQL basée sur les jetons utilisateur . Cela signifie donc essentiellement qu'un opérateur de sous-expression PowerShell est traité comme un agent Token to SQL Server. Ainsi, dans PowerShell, cela $( )semble être traité comme une séquence de caractères réservés pour l'Agent SQL Server. Donc , l' agent SQL serait à la recherche de quelque chose comme cet exemple T-SQL de l'article BOL référencé: PRINT N'Current database name is $(A-DBN)' ; .

Ainsi, dans mon script lorsqu'il a atteint ,@DriveLetter = '$($c.DriveLetter)', le "$ c.DriveLetter" n'est pas l'un des jetons autorisés.

La solution de contournement consiste simplement à ne pas utiliser d'instructions de sous-expression dans PowerShell. Je ferais les ajustements dans mon script donc ceci:


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

devrait être modifié en quelque chose comme ceci:


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

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

A fait les ajustements ci-dessus à mon script et il fonctionne parfaitement maintenant.


la source
1

La citation simple (') est un caractère spécial pour Powershell et délimite les chaînes. La différence entre les guillemets doubles et simples réside dans la façon dont il interprète ensuite la chaîne . Puisqu'il s'agit d'un caractère spécial, vous voudrez y échapper en utilisant l' accent grave (`) . Cette syntaxe devrait fonctionner pour vous:

 @ServerName = `'$($c.ServerName)`'
,@DriveLetter = `'$($c.DriveLetter)`'
Mike Fal
la source
C'est dans une chaîne ici qui ne le traite pas comme un caractère spécial, le prend tel quel. L'ajout de l'accent grave n'a aucun effet, mais recevez toujours le même message de syntaxe par l'Agent SQL pour le même numéro de ligne.