Kur krijova metodologjinë time për menaxhimin e kopjimeve rezervë në shumë servera MS-SQL, kalova një sasi të madhe kohe duke studiuar mekanizmin e kalimit të vlerave në powershell gjatë thirrjeve të largëta, prandaj po shkruaj një shënim për veten time, ndoshta do t'i shërbejë dikujt tjetër.
Pra, le të marrim për fillim një skenar shumë të thjeshtë dhe ta ekzekutojmë lokal:
$exitcode = $args[0]
Write-Host 'Out to host.'
Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)Për të ekzekutuar skriptet, do të përdor këtë skedarin CMD, nuk do ta sjell çdo herë:
@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%Në ekran do të shohim këtë:
Out to host.
Out to output.
ExitCode: 1
1
ERRORLEVEL=1
Tani le të ekzekutojmë këtë skript përmes WSMAN (në distancë):
Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Dhe ja rezultati:
Out to host.
Out to output.
ExitCode: 2
2
ERRORLEVEL=0Mrekullisht, Errorlevel ka humbur diku, por na nevojitet të marrim vlerën nga skripti! Provojmë këtë konstrukcion:
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]Këtu është edhe më interesante. I tërë rezultati në Output është zhdukur diku:
Out to host.
ExitCode: 2
ERRORLEVEL=0Tani, si njĂ« shmangie poetike, do tĂ« theksoj se nĂ«se brenda funksionit Powershell shkruani Write-Output ose thjesht njĂ« shprehje pa e caktuar atĂ« ndonjĂ« variabli (dhe kjo nĂ«nkupton implicit qĂ« do tĂ« dalĂ« nĂ« kanal OutPut), atĂ«herĂ« madje edhe kur ekzekutohet lokal, nĂ« ekran nuk do tĂ« del asgjĂ«! Kjo Ă«shtĂ« njĂ« pasojĂ« e arkitekturĂ«s konvencionale tĂ« powershell â secili funksion ka kanalin e vet Output, pĂ«r tĂ« krijohet njĂ« masiv, dhe gjithçka qĂ« hyn nĂ« tĂ« konsiderohet si rezultat i ekzekutimit tĂ« funksionit, operatori Return e shton vlerĂ«n e kthyer nĂ« kĂ«tĂ« kanal si elementin e fundit dhe e kalon kontrollin nĂ« funksionin qĂ« bĂ«ri thirrjen. PĂ«r ilustrim, do tĂ« ekzekutojmĂ« lokalisht skriptin e mĂ«poshtĂ«m:
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: "+$_) }
Dhe ja rezultati i tijë:
Main: ParameterValue
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
2
Main: Function: ParameterValue
Main: ReturnValueFunksioni kryesor (trupi i skriptit) gjithashtu ka një konvejer Output, dhe nëse e ekzekutojmë skriptin e parë nga CMD, duke redirekcionuar daljen në një skedar,
PowerShell .TestOutput1.ps1 1 > TestOutput1.txt
atëherë në ekran do të shohim
ERRORLEVEL=1ndërsa në skedarin
Out to host.
Out to output.
ExitCode: 1
1
nëse bëjmë një thirrje të ngjashme nga powershell
PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txtatëherë në ekran do të ketë
Out to host.
ExitCode: 1ndërsa në skedarin
Out to output.
1Kjo ndodh sepse CMD ekzekuton powershell, i cili në mungesë të udhëzimeve të tjera përzien dy rrjedha (Host dhe Output) dhe ia jep ato CMD, e cila dërgon në skedar gjithçka që ka marrë, kurse në rastin e ekzekutimit nga powershell këto dy rrjedha ekzistojnë veçmas, dhe simbole redirektimi ndikon vetëm në Output.
Duke u kthyer në temën kryesore, le të kujtojmë se modeli objektiv .NET brenda powershell ekziston plotësisht në një kompjuter (një OS), gjatë ekzekutimit të kodit në distancë përmes WSMAN, transmetimi i objekteve ndodh përmes XML-serializimit, që sjell shumë interes shtesë në hulumtimet tona. Të vazhdojmë eksperimentet duke ekzekutuar kodin e ardhshëm:
$res=Invoke-Command -ComputerName . -ScriptBlock { & 'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)
Dhe ja çfarë kemi në ekran:
Deri te host.
Kod dalës: 3
IsPublic IsSerial Emri TipiBazë
-------- -------- ---- --------
True True Object[] System.Array
Nuk është e mundur të konvertohet argumenti "exitCode", me vlerë: "System.Object[]", për "SetShouldExit" në tipin "System.Int32": "Nuk është e mundur të konvertohet vlera "System.Object[]" tipin "System
.Int32"."
D:sqlagentTestOutput3.ps1:3 shenja:1
+ $host.SetShouldExit($res)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ KategoriaInfo : NotSpecified: (:) [], MethodException
+ Identifikimi i Plotë i Kategorisë : MethodArgumentConversionInvalidCastArgument
ERRORLEVEL=0Rezultat i shkëlqyer! Kjo do të thotë se gjatë thirrjes së Invoke-Command, ndarja e tubacioneve në dy rrjedha (Host dhe Output) ruhet, që na jep shpresë për sukses. Le të provonim të lëmë në rrjedhën Output vetëm një vlerë, për të cilën do të ndryshojmë skriptin e parë që ekzekutojmë nga distanca:
$exitcode = $args[0]
Write-Host 'Deri te host.'
#Write-Output 'Deri te output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)
Ta ekzekutojmë kështu:
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$host.SetShouldExit($res)
dhe⊠PO, duket se është fitorja!
Deri te host.
Kod dalës: 4
IsPublic IsSerial Emri TipiBazë
-------- -------- ---- --------
True True Int32 System.ValueType
ERRORLEVEL=4Le të shohim se çfarë ndodhi. Ne thirrëm lokalisht PowerShell, i cili nga ana e tij thirri PowerShell në kompjuterin e largët dhe ekzekutoi skenarin tonë atje. Dy rrjedha (Host dhe Output) nga makina e largët u serializuan dhe u dërguan përsëri, ku rrjedha Output, nëse kishte një vlerë numerike, u konvertua në tipin Int32 dhe u dërgua kështu palës marrëse, ndërsa pala marrëse e përdori atë si kod përfundimi për PowerShell-in e thirrur.
Dhe si një verifikim të fundit do të krijojmë në serveri një detyrë SQL me një hap të tipit 'Sistemi operativ (cmdexec)' me tekstin:
PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"URIME! Detyra përfundoi me gabim, teksti në regjistër:
Ekzekutohet në emër të përdoruesit: DOMAINagentuser. Jashtë për host. ExitCode: 6. Kodi përfundimi i procesit 6. Hapi përfundoi me gabim.
Konkluzione:
- Shmangni përdorimin e Write-Output dhe specifikimin e shprehjeve pa caktim. Kini parasysh se transferimi i këtij kodi në një vend tjetër të skenarit mund të çojë në rezultate të papritura.
- NĂ« skriptet qĂ« nuk janĂ« destinuar pĂ«r ekzekutim manual, por pĂ«r t'u pĂ«rdorur nĂ« mekanizmat tuaj tĂ« automatizimit, veçanĂ«risht pĂ«r thirrje tĂ« largĂ«ta pĂ«rmes WINRM, bĂ«ni pĂ«rpunim manual tĂ« gabimeve pĂ«rmes Try/Catch, dhe sigurohuni qĂ«, nĂ« çdo rast, ky skript tĂ« dĂ«rgojĂ« nĂ« stream-in Output saktĂ«sisht njĂ« vlerĂ« primitive. NĂ«se dĂ«shironi tĂ« merrni njĂ« Errorlevel klasik â kjo vlerĂ« duhet tĂ« jetĂ« numerike.
Burimi: habr.com
