SQLエージェント-PowerShellステップ「構文エラー」


10

実行されていないSQLエージェントジョブステップで次のコードを設定しています。受け取ったメッセージは単純です:

ステップ1の実行を開始できません(理由:line(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 "& { }"。この実行により、スクリプトが実行され、適切なキャッチブロックのエラーログが入力されますが、ディスク領域のデータはテーブルに書き込まれません。

この実行のエージェント履歴に警告メッセージが表示されます。

ユーザーとして実行されたメッセージ:NT Service \ SQLAgent $ myInstance。警告:一部のインポートされたコマンド名には未承認の動詞が含まれているため、検出されにくくなる可能性があります。詳細についてはVerboseパラメーターを使用するか、Get-Verbと入力して、承認された動詞の一覧を表示します。プロセス終了コード0。ステップは成功しました。

同じタイプのステップを使用してファイルを呼び出す場合:PowerShell -File MyScript.ps1 上記のPowerShellステップタイプと同じエラーメッセージが表示されます。

回答:


11

これはあまり直感的ではなく、説明で具体的なものを見つけることはできませんでした[たとえば、正確なBOLまたはホワイトペーパーが見つからなかった]。

SQLエージェントジョブの構文エラーは、ユーザートークンに基づくT-SQL構文エラーです。つまり、基本的には、PowerShellのサブ式演算子がSQL Serverエージェントへのトークンとして扱われるということです。したがって、PowerShellでは、これ$( )はSQL Serverエージェントの予約文字シーケンスとして扱われるようです。したがって、SQLエージェントは、参照されているBOLの記事からこのT-SQLの例のようなものを探します PRINT N'Current database name is $(A-DBN)' ;

そのため、スクリプトでに達した,@DriveLetter = '$($c.DriveLetter)'とき、「$ c.DriveLetter」は許可されたトークンの1つではありません。

これに対する回避策は、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の特殊文字であり、文字列を区切ります。二重引用符と単一引用符の違いは、文字列をどのように解釈するかという問題です。特殊文字なので、アクサングラーブ( `)を使用してエスケープする必要があります。この構文はあなたのために働くはずです:

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

特殊文字として扱わず、そのままヒア文字列内にあります。重大なアクセントを追加しても影響はありませんが、SQLエージェントは同じ行番号で同じ構文メッセージを受け取ります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.