Բազմաթիվ MS-SQL սերվերների վրա պահուստավորումը կառավարելու իմ սեփական մեթոդաբանությունը ստեղծելիս ես շատ ժամանակ եմ ծախսել՝ ուսումնասիրելով Powershell-ում արժեքների փոխանցման մեխանիզմը հեռավոր զանգերի ժամանակ, ուստի ես հիշեցում եմ գրում ինձ, եթե դա օգտակար լինի: մեկ ուրիշին:
Այսպիսով, եկեք սկսենք պարզ սկրիպտից և գործարկենք այն տեղական մակարդակով.
$exitcode = $args[0]
Write-Host 'Out to host.'
Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)
Սկրիպտներ գործարկելու համար ես կօգտագործեմ հետևյալ CMD ֆայլը, ամեն անգամ չեմ ներառի այն.
@Echo OFF
PowerShell .TestOutput1.ps1 1
ECHO ERRORLEVEL=%ERRORLEVEL%
Էկրանի վրա մենք կտեսնենք հետևյալը.
Out to host.
Out to output.
ExitCode: 1
1
ERRORLEVEL=1
Հիմա եկեք գործարկենք նույն սցենարը WSMAN-ի միջոցով (հեռակա).
Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
Եվ ահա արդյունքը.
Out to host.
Out to output.
ExitCode: 2
2
ERRORLEVEL=0
Հիանալի է, Errorlevel-ը ինչ-որ տեղ անհետացել է, բայց մենք պետք է արժեքը ստանանք սցենարից: Փորձենք հետևյալ դիզայնը.
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
Սա էլ ավելի հետաքրքիր է։ Արդյունքի հաղորդագրությունը ինչ-որ տեղ անհետացել է.
Out to host.
ExitCode: 2
ERRORLEVEL=0
Այժմ, որպես լիրիկական շեղում, ես նկատեմ, որ եթե Powershell ֆունկցիայի ներսում դուք գրում եք Write-Output կամ պարզապես արտահայտություն՝ առանց այն որևէ փոփոխականի վերագրելու (և սա անուղղակիորեն ենթադրում է ելք դեպի Արդյունք ալիք), ապա նույնիսկ տեղական գործարկման ժամանակ, ոչինչ չի ցուցադրվի էկրանին: Սա powershell խողովակաշարի ճարտարապետության հետևանք է. յուրաքանչյուր ֆունկցիա ունի իր ելքային խողովակաշարը, դրա համար ստեղծվում է զանգված, և այն ամենը, ինչ մտնում է դրա մեջ, համարվում է ֆունկցիայի կատարման արդյունք, Return օպերատորը ավելացնում է վերադարձի արժեքը նույնին: խողովակաշարը որպես վերջին տարր և փոխանցում է կառավարումը կանչող ֆունկցիային: Պատկերացնելու համար եկեք գործարկենք հետևյալ սկրիպտը տեղական մակարդակով.
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: "+$_) }
Եվ ահա արդյունքը.
Main: ParameterValue
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
2
Main: Function: ParameterValue
Main: ReturnValue
Հիմնական գործառույթը (սկրիպտի մարմինը) ունի նաև իր ելքային խողովակաշարը, և եթե մենք գործարկենք առաջին սկրիպտը CMD-ից՝ ելքը վերահղելով դեպի ֆայլ,
PowerShell .TestOutput1.ps1 1 > TestOutput1.txt
ապա մենք կտեսնենք էկրանին
ERRORLEVEL=1
և ֆայլում
Out to host.
Out to output.
ExitCode: 1
1
եթե մենք նման զանգ անենք powershell-ից
PS D:sqlagent> .TestOutput1.ps1 1 > TestOutput1.txt
ապա այն կլինի էկրանին
Out to host.
ExitCode: 1
և ֆայլում
Out to output.
1
Դա տեղի է ունենում այն պատճառով, որ CMD-ն գործարկում է powershell-ը, որը, այլ հրահանգների բացակայության դեպքում, խառնում է երկու շարանը (Host և Output) և դրանք տալիս է CMD-ին, որն ուղարկում է այն ամենը, ինչ ստացել է ֆայլ, իսկ powershell-ից գործարկելու դեպքում, այս երկու շարանը գոյություն ունի առանձին, և խորհրդանիշի վերահղումները միայն ազդում են Արդյունքի վրա:
Վերադառնալով բուն թեմային, հիշենք, որ Powershell-ի ներսում .NET օբյեկտի մոդելը լիովին գոյություն ունի մեկ համակարգչի (մեկ ՕՀ) ներսում, WSMAN-ի միջոցով հեռակա կոդի գործարկման ժամանակ օբյեկտների փոխանցումը տեղի է ունենում XML սերիալիզացիայի միջոցով, ինչը մեծ լրացուցիչ հետաքրքրություն է առաջացնում։ մեր հետազոտությանը: Եկեք շարունակենք մեր փորձերը՝ գործարկելով հետևյալ կոդը.
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$res.GetType()
$host.SetShouldExit($res)
Եվ սա այն է, ինչ մենք ունենք էկրանին.
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
Հիանալի արդյունք: Դա նշանակում է, որ Invoke-Command կանչելիս պահպանվում է խողովակաշարերի բաժանումը երկու թելերի (Host և Output), ինչը մեզ հաջողության հույս է տալիս։ Եկեք փորձենք թողնել միայն մեկ արժեք Արդյունք հոսքում, որի համար մենք կփոխենք հեռակա գործարկվող առաջին սկրիպտը.
$exitcode = $args[0]
Write-Host 'Out to host.'
#Write-Output 'Out to output.'
Write-Host ('ExitCode: ' + $exitcode)
Write-Output $exitcode
$host.SetShouldExit($exitcode)
Եկեք գործարկենք այսպես.
$res=Invoke-Command -ComputerName . -ScriptBlock { &'D:sqlagentTestOutput1.ps1' $args[0] } -ArgumentList $args[0]
$host.SetShouldExit($res)
և... ԱՅՈ, կարծես հաղթանակ է։
Out to host.
ExitCode: 4
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
ERRORLEVEL=4
Փորձենք պարզել, թե ինչ է տեղի ունեցել: Մենք լոկալ զանգահարեցինք powershell, որն իր հերթին հեռակա համակարգչի վրա կանչեց powershell և այնտեղ գործարկեց մեր սցենարը: Հեռավոր մեքենայից երկու հոսքեր (Հոսթ և Ելք) սերիականացվել և հետ են փոխանցվել, մինչդեռ Ելքային հոսքը, ունենալով մեկ թվային արժեք, վերածվել է Int32 տեսակի և որպես այդպիսին փոխանցվել է ընդունող կողմին, և ստացող կողմն օգտագործել է այն։ որպես զանգահարողի Powershell-ի ելքի կոդը:
Եվ որպես վերջնական ստուգում, եկեք ստեղծենք մեկ քայլ աշխատանք SQL սերվերի վրա «Օպերացիոն համակարգ (cmdexec)» տիպով հետևյալ տեքստով.
PowerShell -NonInteractive -NoProfile "$res=Invoke-Command -ComputerName BACKUPSERVER -ConfigurationName SQLAgent -ScriptBlock {&'D:sqlagentTestOutput1.ps1' 6}; $host.SetShouldExit($res)"
ՀՈՒՌԱ Առաջադրանքն ավարտված է սխալով, մատյանում տեքստ.
Выполняется от имени пользователя: DOMAINagentuser. Out to host. ExitCode: 6. Код завершения процесса 6. Шаг завершился с ошибкой.
Եզրակացություններ:
- Խուսափեք օգտագործել Write-Output-ը և արտահայտություններ նշել առանց հանձնարարության: Ուշադիր եղեք, որ այս կոդը սկրիպտի մեկ այլ տեղ տեղափոխելը կարող է հանգեցնել անսպասելի արդյունքների:
- Սկրիպտներում, որոնք նախատեսված են ոչ թե ձեռքով գործարկելու, այլ ձեր ավտոմատացման մեխանիզմներում օգտագործելու համար, հատկապես WINRM-ի միջոցով հեռակա զանգերի համար, կատարեք սխալների ձեռքով մշակում Try/Catch-ի միջոցով և համոզվեք, որ իրադարձությունների ցանկացած զարգացման դեպքում այս սկրիպտը ուղարկում է ճիշտ մեկ պարզունակ տիպի արժեք: . Եթե ցանկանում եք ստանալ դասական Errorlevel, ապա այս արժեքը պետք է լինի թվային:
Source: www.habr.com