Как использовать текстовый файл в качестве входных данных для блока сценария - рабочий каталог в фоновых заданиях

Мне дали задание написать сценарий PS, который из списка машин в текстовом файле будет:

  1. Выведите IP-адрес машины
  2. Получите версию клиента SCCM на машине
  3. Создайте файл GPResult HTMl ИЛИ
  4. Укажите, что машина отключена

С окончательным условием запуска скрипта в фоновом режиме (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

На первый взгляд, вы не используете параметр $wrkngdir, который вы отправляете в -ArgumentLIst. Как насчет Get-Content (Join-Path -Path $wrkngdir -ChildPath 'Hostnames.txt'). Кроме того, если я могу предложить, сделайте что-нибудь с отступом в коде, потому что теперь его действительно трудно читать ...

Theo 26.10.2018 22:27

Может быть полезно начать обдумывать и разрабатывать сценарии в концепции расширенной функции PowerShell. Используйте блоки Begin, Process, End. В PowerShell ISE нажмите CTRL + J, чтобы открыть фрагменты, и вставьте, чтобы просмотреть пример.

user4317867 27.10.2018 05:46
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
174
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если я правильно понимаю, я думаю, что проблема, с которой вы столкнулись, заключается в том, что путь к рабочему каталогу отличается внутри выполнения блока сценария. Обычно это происходит, когда вы выполняете сценарии из запланированных задач или передаете сценарии в 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
    

Другие вопросы по теме