Powershell invoke-пәрменінен SQL Server агентіне мәнді қайтару

Бірнеше 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 басқа нұсқаулар болмаған жағдайда екі ағынды (Хост және Шығару) араластыратын және CMD-ге беретін powershell-ді іске қосқандықтан болады, ол алынғанның барлығын файлға жібереді, ал 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 шақыру кезінде құбырларды екі ағынға (хост және шығыс) бөлу сақталады дегенді білдіреді, бұл бізге сәттілікке үміт береді. Шығару ағынында тек бір мән қалдыруға тырысайық, ол үшін қашықтан іске қосылатын ең бірінші сценарийді өзгертеміз:

$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 арқылы қатені қолмен өңдеуді орындаңыз және оқиғалардың кез келген дамуында бұл сценарий дәл бір қарабайыр түр мәнін жіберетініне көз жеткізіңіз. . Егер сіз классикалық қате деңгейін алғыңыз келсе, бұл мән сандық болуы керек.

Ақпарат көзі: www.habr.com

пікір қалдыру