Мне дали задание написать сценарий PS, который из списка машин в текстовом файле будет:
С окончательным условием запуска скрипта в фоновом режиме (Job)
У меня есть блок сценария, который будет делать все эти вещи, и даже формат вывода будет таким, как я хочу. Кажется, что я, не мочь, заставляет блок сценария вызывать исходный файл из того же каталога, что и сценарий. Я понимаю, что могу просто жестко закодировать каталоги, но я хочу иметь возможность запускать это на любом компьютере, в любом каталоге, поскольку мне нужно будет использовать сценарий в нескольких местах.
Какие-либо предложения?
Код выглядит следующим образом (Примечание: я пытаюсь понять, что я собрал из других статей, поэтому в нем есть фрагмент или два [последняя попытка заключалась в указании рабочего каталога], но основной код все еще там. Я также пришла идея сначала объявить блок сценария, как вы делаете с переменными на других языках программирования, но больше для удобства чтения, чем что-либо еще):
# List of commands to process in job
$ScrptBlk = {
param($wrkngdir)
Get-Content Hostnames.txt | ForEach-Object {
# Check to see if Host is online
IF ( Test-Connection $_ -count 1 -Quiet) {
# Get IP address, extracting only IP value
$addr = (test-connection $_ -count 1).IPV4Address
# Get SCCM version
$sccm = (Get-WmiObject -NameSpace Root\CCM -Class Sms_Client).ClientVersion
# Generate GPResult HTML file
Get-GPResultantSetOfPolicy -computer $_.name -reporttype HTML -path ".\GPRes\$_ GPResults.html"}
ELSE {
$addr = "Offline"
$sccm = " "}
$tbl = New-Object psobject -Property @{
Computername = $_
IPV4Address = $addr
SCCM_Version = $sccm}}}
# Create (or clear) output file
Echo "" > OnlineCheckResults.txt
# Create subdirectory, if it does not exist
IF (-Not (Get-Item .\GPRes)) { New-Item -ItemType dir ".\GPRes" }
# Get current working directory
$wrkngdir = $PSScriptRoot
# Execute script
Start-Job -name "OnlineCheck" -ScriptBlock $ScrptBlk -ArgumentList $wrkngdir
# Let job run
Wait-Job OnlineCheck
# Get results of job
$results = Receive-Job OnlineCheck
# Output results to file
$results >> OnlineCheckResults.txt | FT Computername,IPV4Address,SCCM_Version
Я ценю любую помощь, которую вы можете предложить.
Ваше здоровье.
~ DavidM ~
РЕДАКТИРОВАТЬ
Спасибо за помощь. Настройка рабочего каталога работает, но теперь я получаю новую ошибку. У него нет ссылки на строку, поэтому я не уверен, в чем может быть проблема. Новый код ниже. Я переместил sriptblock вниз, так что он отделен от остальной части кода. Я подумал, что это может быть немного аккуратнее. Я прошу прощения за мое предыдущее форматирование кода. Я постараюсь сделать лучше с новым примером.
# Store working directory
$getwkdir = $PWD.Path
# Create (or clear) output file
Write-Output "" > OnlineCheckResults.txt
# Create subdirectory, if it does not exist. Delete and recreate if it does
IF (Get-Item .\GPRes) {
Remove-Item -ItemType dir "GPRes"
New-Item -ItemType dir "GPRes"}
ELSE{
New-Item -ItemType dir "GPRes"}
# Start the job
Start-Job -name "OnlineCheck" -ScriptBlock $ScrptBlk -ArgumentList $getwkdir
# Let job run
Wait-Job OnlineCheck
# Get results of job
$results = Receive-Job OnlineCheck
# Output results to file
$results >> OnlineCheckResults.txt | FT Computername,IPV4Address,SCCM_Version
$ScrptBlk = {
param($wrkngdir)
Set-Location $wrkngdir
Get-Content Hostnames.txt | ForEach-Object {
IF ( Test-Connection $_ -count 1 -Quiet) {
# Get IP address, extracting only IP value
$addr = (test-connection $_ -count 1).IPV4Address
# Get SCCM version
$sccm = (Get-WmiObject -NameSpace Root\CCM -Class Sms_Client).ClientVersion
Get-GPResultantSetOfPolicy -computer $_.name -reporttype HTML -path ".\GPRes\$_ GPResults.html"}
ELSE {
$addr = "Offline"
$sccm = " "}
$tbl = New-Object psobject -Property @{
Computername = $_
IPV4Address = $addr
SCCM_Version = $sccm}}}
Текст ошибки: Невозможно проверить аргумент параметра ComputerName. Аргумент равен нулю или пуст. Приведите аргумент, что не является нулевым или пустым, а затем попробуйте команду еще раз. + CategoryInfo: InvalidData: (:) [Test-Connection], ParameterBindingValidationException + FullyQualifiedErrorId: ParameterArgumentValidationError, Microsoft.PowerShell.Commands.TestConnectionCommand + PSComputerName: localhost
Может быть полезно начать обдумывать и разрабатывать сценарии в концепции расширенной функции PowerShell. Используйте блоки Begin, Process, End. В PowerShell ISE нажмите CTRL + J, чтобы открыть фрагменты, и вставьте, чтобы просмотреть пример.
Если я правильно понимаю, я думаю, что проблема, с которой вы столкнулись, заключается в том, что путь к рабочему каталогу отличается внутри выполнения блока сценария. Обычно это происходит, когда вы выполняете сценарии из запланированных задач или передаете сценарии в powershell.exe.
Чтобы доказать это, давайте сделаем простой код PowerShell:
#Change current directory to the root of C: illustrate what's going on
cd C:\
Get-Location
Path
----
C:\
#Execute Script Block
$ScriptBlock = { Get-Location }
$Job = Start-Job -ScriptBlock $ScriptBlock
Receive-Job $Job
Path
----
C:\Users\HAL9256\Documents
Как видите, текущий путь внутри выполнения блока скрипта отличается от того, где вы его выполнили. Я также видел внутри запланированных задач такие пути, как C:\Windows\System32
.
Поскольку вы пытаетесь ссылаться на все по относительным путям внутри блока скрипта, он ничего не найдет. Одно из решений - использовать переданный параметр, чтобы сначала изменить рабочий каталог на что-то известное.
Кроме того, я бы использовал $PWD.Path
для получения текущего рабочего каталога вместо $PSScriptRoot
, поскольку $PSScriptRoot
пуст, если вы запускаете код с консоли.
Как отмечает Тео, вы на правильном пути, пытаясь передать желаемый рабочий каталог блоку сценария через -ArgumentList $wrkngdir
, но тогда вы не использовать этот аргумент внутри вашего блока скрипта.
Все, что нужно, это используйте Set-Location
в начале блока скрипта, чтобы переключиться на переданный рабочий каталог:
$ScrptBlk = {
param($wrkngdir)
# Change to the specified working dir.
Set-Location $wrkngdir
# ... Get-Content Hostnames.txt | ...
}
# Start the job and pass the directory in which this script is located as the working dir.
Start-Job -name "OnlineCheck" -ScriptBlock $ScrptBlk -ArgumentList $PSScriptRoot
В PSv3 + вы можете упростить решение, используя прицел $using:
, который позволяет вам ссылаться на переменные в области звонящийнапрямую; вот упрощенный пример, который вы можете запустить прямо из приглашения (я использую $PWD
в качестве желаемого рабочего каталога, потому что $PSScriptRoot
не определен в приглашении (в глобальной области)):
Start-Job -ScriptBlock { Set-Location $using:PWD; Get-Location } |
Receive-Job -Wait -AutoRemove
Если вы вызовете указанную выше команду, скажем, из C:\tmp
, выходные данные также будут отражать этот путь, доказывая, что фоновое задание выполнялось в том же рабочем каталоге, что и вызывающий.
Рабочие каталоги в фоновых заданиях PowerShell:
До PowerShell 7.0, запуск фоновых заданий с Start-Job
использует каталог, возвращаемый [environment]::GetFolderPath('MyDocuments')
, в качестве исходного рабочего каталога, который в Windows обычно $HOME\Documents
, тогда как это просто $HOME
на Unix-подобных платформах (в PowerShell Основной).
Start-Job
-InitializationScript
через ссылку $using:
- например, Start-Job -InitializationScript { $using:PWD } { ... }
должен работает, но не работает в Windows PowerShell v5.1 / PowerShell [Core] 6.x из-за ошибка (ошибка все еще присутствует в PowerShell 7.0, но там можно использовать -WorkingDirectory
).В PowerShell (ядро) 7+Start-Job
теперь разумно по умолчанию используется рабочий каталог вызывающего абонента, а также поддерживает параметр -WorkingDirectory
, чтобы упростить определение рабочего каталога.
В PowerShell (Core) 6+ в качестве альтернативы вы можете запускать фоновые задания с пост-позиционным &
- так же, как это делают POSIX-подобные оболочки, такие как bash
, - в этом случае рабочий каталог вызывающего абонента наследуется; например.:
# PS Core only:
# Outputs the caller's working dir., proving that the background job
# inherited the caller's working dir.
(Get-Location &) | Receive-Job -Wait -AutoRemove
На первый взгляд, вы не используете параметр
$wrkngdir
, который вы отправляете в-ArgumentLIst
. Как насчетGet-Content (Join-Path -Path $wrkngdir -ChildPath 'Hostnames.txt')
. Кроме того, если я могу предложить, сделайте что-нибудь с отступом в коде, потому что теперь его действительно трудно читать ...