Väärtuse tagastamine käsust powershell invoke-command SQL Serveri agendile

Oma metoodikat luues mitmes MS-SQL-serveris varukoopiate haldamiseks kulutasin palju aega Powershellis kaugkõnede ajal väärtuste edastamise mehhanismi uurimiseks, nii et kirjutan endale meeldetuletuse juhuks, kui see on kasulik. kellelegi teisele.

Niisiis, alustame lihtsa skriptiga ja käivitame selle kohapeal:

$exitcode = $args[0]
Write-Host 'Out to host.'
Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Skriptide käitamiseks kasutan järgmist CMD-faili, ma ei lisa seda iga kord:

@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%

Ekraanil näeme järgmist:

Out to host.
Out to output.
ExitCode: 1
1
ERRORLEVEL=1


Nüüd käivitame sama skripti WSMAN-i kaudu (kaugjuhtimisega):

Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]

Ja siin on tulemus:

Out to host.
Out to output.
ExitCode: 2
2
ERRORLEVEL=0

Suurepärane, Errorlevel on kuhugi kadunud, kuid me peame skriptist väärtuse saama! Proovime järgmist kujundust:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]

See on veelgi huvitavam. Väljundis olev teade on kuhugi kadunud:

Out to host.
ExitCode: 2
ERRORLEVEL=0

Nüüd märgin lüürilise kõrvalepõikena, et kui Powershelli funktsiooni sees kirjutate Write-Output või lihtsalt avaldise ilma seda ühelegi muutujale määramata (ja see tähendab kaudselt väljundit väljundkanalile), siis isegi lokaalselt käivitades, ekraanile ei kuvata midagi! See on powershelli konveieri arhitektuuri tagajärg – igal funktsioonil on oma Output konveier, selle jaoks luuakse massiiv ja kõike, mis sinna läheb, loetakse funktsiooni täitmise tulemuseks, operaator Return lisab samale tagastatava väärtuse torujuhtme viimase elemendina ja annab juhtimise üle kutsumisfunktsioonile. Illustreerimiseks käivitame järgmise skripti kohapeal:

Function Write-Log {
  Param( [Parameter(Mandatory=$false, ValueFromPipeline=$true)] [String[]] $OutString = "`r`n" )
  Write-Output ("Function: "+$OutString)
  Return "ReturnValue"
}
Write-Output ("Main: "+"ParameterValue")
$res = Write-Log "ParameterValue"
$res.GetType()
$res.Length
$res | Foreach-Object { Write-Host ("Main: "+$_) }

Ja siin on tulemus:

Main: ParameterValue

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
2
Main: Function: ParameterValue
Main: ReturnValue

Põhifunktsioonil (skripti kehal) on ka oma väljundkonveier ja kui käivitame CMD-st esimese skripti, suuname väljundi faili,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

siis näeme ekraanil

ERRORLEVEL=1

ja failis

Out to host.
Out to output.
ExitCode: 1
1

kui teeme sarnase kõne powershellilt

PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txt

siis on see ekraanil

Out to host.
ExitCode: 1

ja failis

Out to output.
1

See juhtub seetõttu, et CMD käivitab powershelli, mis muude juhiste puudumisel segab kaks lõime (Host ja Output) ja annab need CMD-le, mis saadab kõik saadud failid, ja Powershellist käivitamise korral need kaks lõime eksisteerivad eraldi ja sümboli ümbersuunamised mõjutavad ainult väljundit.

Põhiteema juurde tagasi tulles meenutagem, et powershelli sees olev .NET objektimudel eksisteerib täielikult ühes arvutis (ühes OS-is), WSMANi kaudu koodi kaugkäitamisel toimub objektide ülekandmine läbi XML-serialiseerimise, mis toob palju lisahuvi. meie uuringutele. Jätkame katseid, käivitades järgmise koodi:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)

Ja see on see, mis meil ekraanil on:

Out to host.

ExitCode: 3

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
Не удается преобразовать аргумент "exitCode", со значением: "System.Object[]", для "SetShouldExit" в тип "System.Int32": "Не удается преобразовать значение "System.Object[]" типа "System.Object[]" в тип "System
.Int32"."
D:sqlagentTestOutput3.ps1:3 знак:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

ERRORLEVEL=0

Suurepärane tulemus! See tähendab, et Invoke-Command kutsumisel säilib torujuhtmete jaotus kaheks lõimeks (Host ja Output), mis annab lootust edule. Proovime jätta väljundvoogu ainult ühe väärtuse, mille jaoks muudame esimest skripti, mida kaugkäivitame:

$exitcode = $args[0]
Write-Host 'Out to host.'
#Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)

Käitame seda nii:

$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$host.SetShouldExit($res)

ja... JAH, see tundub võiduna!

Out to host.
ExitCode: 4

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType


ERRORLEVEL=4

Proovime aru saada, mis juhtus. Kutsusime kohalikult powershelli, mis omakorda kutsus kaugarvutis Powershelli ja käivitas seal meie skripti. Kaks voogu (host ja väljund) kaugmasinast järjestati ja edastati tagasi, samas kui väljundvoog, millel oli üks digitaalne väärtus, teisendati tüübiks Int32 ja edastati sellisena vastuvõtvale poolele ning vastuvõttev pool kasutas seda. helistaja powershelli väljumiskoodina.

Viimase kontrollina loome SQL-serveris üheastmelise töö tüübiga „Operatsioonisüsteem (cmdexec)” järgmise tekstiga:

PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {&'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"

HURRA! Ülesanne lõpetati veaga, tekst logis:

Выполняется от имени пользователя: DOMAINagentuser. Out to host. ExitCode: 6.  Код завершения процесса 6.  Шаг завершился с ошибкой.

Järeldused:

  • Vältige Write-Output kasutamist ja avaldiste määramist ilma määramiseta. Pidage meeles, et selle koodi teisaldamine mujale skriptis võib anda ootamatuid tulemusi.
  • Skriptides, mis pole mõeldud käsitsi käivitamiseks, vaid teie automatiseerimismehhanismides kasutamiseks, eriti kaugkõnede puhul WINRM-i kaudu, tehke käsitsi vigade käsitlemine Try/Catch abil ja veenduge, et see skript saadaks sündmuste mis tahes arendamise korral täpselt ühe primitiivse tüübi väärtuse. . Kui soovite saada klassikalist veataset, peab see väärtus olema numbriline.

Allikas: www.habr.com

Lisa kommentaar