Oma varukoopiate haldamise meetodite loomisel mitu MS-SQL serverit kulutasin ma palju aega PowerShellis kaugkutses väärtuste edastamise mehhanismi õppimisele, seetõttu kirjutan endale märkmed, ehk on kellelegi veel kasu.
Alustame kõige lihtsamast skriptist ja käivitame selle kohapeal:
$exitcode = $args[0]
Write-Host 'Väljund hostile.'
Write-Output 'Väljund väljundisse.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)Skripti käitamiseks kasutan järgmise CMD-faili, ma ei hakka seda iga kord kordama:
@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%Küljel näeme järgmist:
Väljund hostile.
Väljund väljundisse.
ExitCode: 1
1
ERRORLEVEL=1
Nüüd jooksutame sama skripti WSMANi kaudu (eemalt):
Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Ja siin on tulemus:
Väljund hostile.
Väljund väljundisse.
ExitCode: 2
2
ERRORLEVEL=0Üllatav, Errorlevel on kadunud, kuid meil on vaja skriptist väärtust saada! Proovime järgmist konstruktsiooni:
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Siin on veel huvitavam. Kogu väljund väljundisse on kuhugi kadunud:
Väljund hostile.
ExitCode: 2
ERRORLEVEL=0Nüüd märkuseks, et kui kirjutate Powershelli funktsiooni sees Write-Output või lihtsalt väljendi, ilma et see oleks määratud mõnele muutujale (mis eeldab väljundit väljundkanalisse), siis isegi kohaliku käivitamise korral ei kuvata midagi ekraanile! See on PowerShelli torustiku arhitektuuri tagajärg - igal funktsioonil on oma väljundkanal, mille jaoks luuakse massiiv, ja kõik, mis sinna satub, loetakse funktsiooni täitmise tulemuseks, operaatort Return lisab tagastatava väärtuse sellele kanali viimase elemendina ja edastab juhtimise kutsuvasse funktsiooni. Näiteks käivitame kohapeal järgmise skripti:
Function Write-Log {
Param( [Parameter(Mandatory=$false, ValueFromPipeline=$true)] [String[]] $OutString = "`r`n" )
Write-Output ("Funktsioon: "+$OutString)
Return "TagastatavVäärtus"
}
Write-Output ("Peamine: "+"ParameetriVäärtus")
$res = Write-Log "ParameetriVäärtus"
$res.GetType()
$res.Length
$res | Foreach-Object { Write-Host ("Peamine: "+$_) }
Ja siin on selle tulemus:
Peamine: ParameetriVäärtus
IsPublic IsSerial Nimi PõhiTüüp
-------- -------- ---- --------
True True Objekt[] System.Array
2
Peamine: Funktsioon: ParameetriVäärtus
Peamine: TagastatavVäärtusPeamine funktsioon (skripti keha) omab samuti oma väljundi toru, ja kui me käivitame esimese skripti CMD-st, suunates väljundi faili,
PowerShell .TestOutput1.ps1 1 > TestOutput1.txt
siis näeme ekraanil
ERRORLEVEL=1ja failis
Käib hostile.
Käib väljundisse.
Väljundi kood: 1
1
kui aga teeme sarnase väljakutse PowerShellist
PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txtsiis ekraanil on
Käib hostile.
Väljundi kood: 1ja failis
Käib väljundisse.
1See juhtub, kuna CMD käivitab PowerShelli, mis ilma teiste näidisteta segab kahte voogu (Host ja Output) ja annab need edasi CMD-le, mis saadab failisse kõik, mis sai, ja PowerShellist käivitamisel eksisteerivad need kaks voogu eraldi ning suunamis sümbol mõjutab ainult Outputi.
Tagasi põhiteemasse, tuletame meelde, et .NET objekti mudel PowerShellis eksisteerib täielikult ühe arvuti (ühe OS) raames. Koodide kaugkäitamisel WSMAN kaudu toimub objektide edastamine XML-sarjanduse kaudu, mis toob meie uuringutesse palju lisahuvi. Jätkame eksperimente järgmise koode käivitamisega:
$res=Invoke-Command -ComputerName . -ScriptBlock { 'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)
Ja siin on meile ekraanil:
Käib hostile.
Väljundi kood: 3
IsPublic IsSerial Nimi AlusTüüp
-------- -------- ---- --------
True True Object[] System.Array
Argumenti "exitCode" väärtusega: "System.Object[]" ei saa konverteerida "SetShouldExit" jaoks tüübiks "System.Int32": "System.Object[]" tüübi "System.Object[]" väärtust ei saa konverteerida tüübiks "System.Int32"."
D:sqlagentTestOutput3.ps1:3 märk:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ KategooriaInfo : Määratlemata: (:) [], MethodException
+ TäielikultMääratletudVigaId : MethodArgumentConversionInvalidCastArgument
ERRORLEVEL=0Imeline tulemus! See tähendab, et Invoke-Command’i kutsumisel säilivad konveierite jagunemised kahte voogu (Host ja Output), mis annab meile lootust edule. Proovime jätta väljundis ainult ühe väärtuse, selleks muudame kõige esimest skripti, mida kaugelt käivitame:
$exitcode = $args[0]
Write-Host 'Käib hostile.'
#Write-Output 'Käib väljundisse.'
Write-Host ('Väljundi kood: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)
Käivitame selle nii:
$res=Invoke-Command -ComputerName . -ScriptBlock { 'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$host.SetShouldExit($res)
ja… JAH, tundub, et see on võit!
Käib hostile.
Väljundi kood: 4
IsPublic IsSerial Nimi AlusTüüp
-------- -------- ---- --------
True True Int32 System.ValueType
ERRORLEVEL=4Üritame mõista, mis meiega juhtus. Me kutsusime kohalikult esile PowerShell'i, mis omakorda kutsus esile PowerShell'i eemaloleval arvutil ja käivitas seal meie skripti. Kaks voogu (Host ja Output) eemalt arvutist seriaaliti edastati tagasi, samas kui Output voog, kui see sisaldas ühte numbrilist väärtust, muundati Int32 tüübiks ja edastati vastuvõtjale, kes kasutas seda PowerShell'i väljakutsuja väljumiskoodina.
Ja viimase kontrollina loome serveris SQL ülesande ühe sammu tüüpi „Operatsioonisüsteem (cmdexec)“ sellise tekstiga:
PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {&'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"HURRAA! Ülesanne lõpetati veaga, ajaloos on tekst:
Teostab kasutaja nimega: DOMAINagentuser. Väljastab hostile. ExitCode: 6. Protsessi väljumiskood on 6. Samm lõppes veaga.
Kokkuvõtted:
- Vältige Write-Output kasutamist ja väljendite määramist ilma omistamiseta. Pidage meeles, et selle koodi viimine teise kohta skriptis võib põhjustada ootamatuid tulemusi.
- Skriptides, mis ei ole ette nähtud käsitsi käivitamiseks, vaid automaatika mehhanismides teie jaoks, eriti kaugkutsed WINRM kaudu, tehke vigade käsitlemine käsitsi läbi Try/Catch ja tagage, et iga olukorra korral saadaks see skript Output voogu täpselt ühe primitiivse tüübi väärtuse. Kui soovite klassikalist Errorlevel'i — peab see väärtus olema numbriline.
Allikas: habr.com
