Oma MS-SQL varundamisstrategia loomisel kulutasin ma palju aega PowerShelli väärtuste edastamise mehhanismi õppimisele kaugkutsede puhul, seega kirjutan endale märkmeid, äkki läheb see kellelegi muulegi vaja.
Alustame kõige lihtsamast skriptist ja käivitame selle kohalikult:
$exitcode = $args[0]
Write-Host 'Väljund hostile.'
Write-Output 'Väljund väljundisse.'
Write-Host ('Lahkumiskood: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)Skriptide käivitamiseks kasutan ma järgmist CMD-faili, millest ma iga kord ei räägi:
@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%Kraanil näeme järgmist:
Väljund hostile.
Väljund väljundisse.
Lahkumiskood: 1
1
ERRORLEVEL=1
Nüüd käivitame sama skripti WSMAN kaudu (kaugelt):
Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Ja siin on tulemus:
Väljund hostile.
Väljund väljundisse.
Lahkumiskood: 2
2
ERRORLEVEL=0Imeline, Errorlevel on kuhugi kadunud, aga me peame ju skriptist väärtust saama! Proovime järgmist konstruktsiooni:
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Siin on veel huvitavam. Kõik väljund väljundisse on kuhugi kadunud:
Väljund hostile.
Lahkumiskood: 2
ERRORLEVEL=0Nüüd, kui lühiülevaateks märkida, et kui Powershelli funktsiooni sees kirjutate Write-Output või lihtsalt väljendi, millele te ei määranud mingit muutujat (mis eeldab, et see suunatakse väljundkanalisse), siis ei kuvata isegi kohalikult käivitamisel ekraanile midagi! See tuleneb powershelli torustiku arhitektuurist - igal funktsioonil on oma väljundkanal, mille jaoks luuakse massiiv, ja kõik, mis sinna satub, loetakse funktsiooni täitmise tulemuseks; Return-lause lisab tagastatava väärtuse sellesse kanali viimase elemendina ja edastab kontrolli kutsuvasse funktsiooni. Illustratsiooniks käivitage järgmine skript kohalikult:
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 tema tulemus:
Main: ParameterValue
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
2
Main: Function: ParameterValue
Main: ReturnValuePeamine funktsioon (skripti keha) omab ka 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
Väljund hosdile.
Väljund väljundisse.
Väljundkood: 1
1
kui me teeme sarnase väljakutse powershellist
PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txtsiis ekraanil kuvatakse
Väljund hosdile.
Väljundkood: 1ja failis
Väljund väljundisse.
1See juhtub, kuna CMD käivitab powershelli, mis segab kahte voogu (Host ja Output) ilma muude juhisteta ja edastab need CMD-le, mis saadab faili kõik, mis ta sai, kuid powershellist käivitamisel on need kaks voogu eraldi ning suunamissümbol mõjutab ainult Outputi.
Tagasi põhipunkti juurde, meenutame, et .NET objekti mudel powershellis eksisteerib täielikult ühe arvuti (ühe OS) ulatuses; kaugkoodi käivitamisel WSMAN-i kaudu toimub objektide edastamine XML-serialiseerimise kaudu, mis toob meie uurimisse palju lisahuvi. Jätkame katsetuste tegemist järgmise koodiga:
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)
Ja siin on, mida me ekraanil näeme:
Väline hostimise.
Väljutuskood: 3
IsPublic IsSerial Nimi Baastüüp
-------- -------- ---- --------
True True Object[] System.Array
Argumenti "exitCode" väärtuse "System.Object[]" teisendamine "SetShouldExit" tüübiks "System.Int32" ebaõnnestus: "Ei ole võimalik teisendada väärtust "System.Object[]" tüübist "System.Object[]" tüübiks "System.Int32".
D:sqlagentTestOutput3.ps1:3 rida:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ KategooriaInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument
ERRORLEVEL=0Suurepärane tulemus! See tähistab, et Invoke-Commandi kutsumisel jaguneb torustik kaheks vooluks (Host ja Output), mis annab meile lootust eduks. Proovime jätta Output voolu ainult ühe väärtuse, muutes kõigepealt skripti, mida käivitasime kaugjuhtimise teel:
$exitcode = $args[0]
Write-Host 'Väline hostimise.'
#Write-Output 'Väline väljund.'
Write-Host ('Väljutuskood: ' + $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!
Väline hostimise.
Väljutuskood: 4
IsPublic IsSerial Nimi Baastüüp
-------- -------- ---- --------
True True Int32 System.ValueType
ERRORLEVEL=4Püüame välja selgitada, mis meiega juhtus. Käivitasime kohalikult PowerShelli, mis omakorda kutsus esile PowerShelli kaugarvutis ja exécutis seal meie skripti. Kaks voogu (Host ja Output) kaugarvutitest serialiseeriti ja edastati tagasi, kusjuures Outputi voog, kui see sisaldas ühte numbrilist väärtust, muudeti tyybiks Int32 ja saadeti vastuvõtjale, kes kasutas seda, et lõpukoodina PowerShelli jaoks.
Ja viimase kontrollina loome serveril SQL ülesande ühe sammu jaotisega, mille tüüp on «Operatsioonisüsteem (cmdexec)» ja mille tekst on:
PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {&'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"TÄHELEPANU! Ülesanne lõpetati veaga, logis tekst:
Käib kasutaja nimel: DOMAINagentuser. Väljund hostile. ExitCode: 6. Protsessi lõpetamiskood 6. Samm lõppes veaga.
Järeldused:
- Vältige Write-Outputi kasutamist ja väljendite määramist ilma omistamiseta. Pidage meeles, et selle koodi teisaldamine teise kohta skripti võib põhjustada ettenägematuid tulemusi.
- Automatiseerimismehhanismides, mitte käsitsi käivitamiseks mõeldud skriptides, eriti WINRM kaudu kaugkutsumiseks, tehke vigade käsitlemine läbi Try/Catch ning veenduge, et igas olukorras saadetakse Output voogu täpselt üks primitiivne tüübi väärtus. Kui soovite klassikalist Errorleveli, peab see väärtus olema numbriline.
Allikas: habr.com
