Powershell invoke-komandotik balio bat itzultzea SQL Server agenteari

MS-SQL zerbitzari anitzetan babeskopiak kudeatzeko nire metodologia sortzean, denbora asko eman nuen Powershell-en balioak pasatzeko mekanismoa aztertzen urruneko deietan, beraz, abisu bat idazten ari naiz nire buruari baliagarria bada. beste norbaiti.

Beraz, has gaitezen script sinple batekin eta exekutatu lokalean:

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

Scriptak exekutatzeko, CMD fitxategi hau erabiliko dut, ez dut aldi bakoitzean sartuko:

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

Pantailan honako hau ikusiko dugu:

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


Orain exekutatu dezagun script bera WSMAN bidez (urrunetik):

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

Eta hona hemen emaitza:

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

Bikaina, Errorlevel desagertu egin da nonbait, baina gidoiaren balioa lortu behar dugu! Saia gaitezen eraikuntza hau:

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

Hau are interesgarriagoa da. Irteerako mezua nonbait desagertu da:

Out to host.
ExitCode: 2
ERRORLEVEL=0

Orain, digresio liriko gisa, ohartuko naiz Powershell funtzio baten barruan Write-Output edo adierazpen bat idazten baduzu inongo aldagai bati esleitu gabe (eta honek inplizituki Irteera kanalerako irteera dakar), orduan lokalean exekutatzen denean ere, ez da ezer agertuko pantailan! Hau powershell kanalizazio arkitekturaren ondorioa da - funtzio bakoitzak bere Irteera kanalizazio propioa du, horretarako array bat sortzen da eta bertan sartzen dena funtzioaren exekuzioaren emaitzatzat hartzen da, Return operadoreak itzulera-balioa berdinari gehitzen dio. kanalizazioa azken elementu gisa eta kontrola deitzen duen funtzioari transferitzen dio. Ilustratzeko, exekutatu dezagun script hau lokalean:

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: "+$_) }

Eta hona hemen emaitza:

Main: ParameterValue

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

Funtzio nagusiak (script-en gorputza) ere bere Irteera kanala du, eta lehen script-a CMDtik exekutatzen badugu, irteera fitxategi batera birbideratuz,

PowerShell .TestOutput1.ps1 1 > TestOutput1.txt

gero pantailan ikusiko dugu

ERRORLEVEL=1

eta fitxategian

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

powershell-etik antzeko deia egiten badugu

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

gero pantailan egongo da

Out to host.
ExitCode: 1

eta fitxategian

Out to output.
1

Hau gertatzen da CMD-k powershell-a abiarazten duelako, eta honek, beste instrukziorik ezean, bi hari nahasten ditu (Ostalaria eta Irteera) eta CMD-ri ematen dizkio, honek jasotako guztia fitxategi batera bidaltzen duelako, eta powershell-etik exekutatzen bada. bi hari hauek bereizita existitzen dira, eta ikurren birbideratzeek Irteera bakarrik eragiten dute.

Gai nagusira itzuliz, gogora dezagun powershell-en barruan dagoen .NET objektu-eredua guztiz existitzen dela ordenagailu batean (OS bakarrean), kodea urrunetik WSMAN bidez exekutatzen denean, objektuen transferentzia XML serializazioaren bidez gertatzen da, eta horrek interes gehigarri handia dakar. gure ikerketara. Jarrai dezagun gure esperimentuak kode hau exekutatuz:

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

Eta hau da pantailan daukaguna:

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

Emaitza bikaina! Esan nahi du Invoke-Command deitzean, kanalizazioen banaketa bi haritan (Ostalaria eta Irteera) mantentzen dela, eta horrek arrakasta izateko itxaropena ematen digu. Saia gaitezen Irteera korrontean balio bakarra uzten, eta horretarako urrunetik exekutatzen dugun lehen scripta aldatuko dugu:

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

Exekutatu dezagun honela:

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

eta... BAI, garaipen bat dirudi!

Out to host.
ExitCode: 4

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


ERRORLEVEL=4

Saia gaitezen zer gertatu den asmatzen. Powershell-i deitzen genion lokalean, eta horrek aldi berean urruneko ordenagailuan powershell-a deitu genion eta bertan exekutatu genuen gure scripta. Urruneko makinatik bi korronte (Ostalaria eta Irteera) serializatu eta itzuli egin ziren, eta Irteera korrontea, bertan balio digital bakarra zuena, Int32 motara bihurtu zen eta, beraz, hartzailera pasa zen eta hartzaileak erabili zuen. deitzailearen powershell-aren irteera-kode gisa.

Eta azken egiaztapen gisa, sor dezagun urrats bakarreko lana SQL zerbitzarian "Sistema eragilea (cmdexec)" motako testu honekin:

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

AUPA! Errore batekin osatu da zeregina, testua erregistroan:

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

Ondorioak:

  • Saihestu Write-Output erabiltzea eta esamoldeak esleipenik gabe zehaztea. Kontuan izan kode hau scripteko beste leku batera mugitzeak ustekabeko emaitzak sor ditzakeela.
  • Eskuz abiarazteko ez diren scriptetan, zure automatizazio-mekanismoetan erabiltzeko baizik, batez ere WINRM bidez urruneko deiak egiteko, egin eskuzko erroreen kudeaketa Try/Catch bidez, eta ziurtatu, gertaeren edozein garapenetan, script honek mota primitiboko balio bat bidaltzen duela. . Errorlevel klasikoa lortu nahi baduzu, balio honek zenbakizkoa izan behar du.

Iturria: www.habr.com

Gehitu iruzkin berria