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

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

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

С окончательным условием выполнения сценарияна заднем плане (задание)

У меня есть скрипт-блок, который будет выполнять все эти вещи, и даже форматировать выходные данные, как я хочу.То, что я не могу , похоже, сделать, это получить блок скриптов для вызова исходного файла из той же директории, что и скрипт.Я понимаю, что могу просто жестко закодировать каталоги, но я хочу иметь возможность запускать это на любом компьютере, в любом каталоге, так как мне потребуется использовать сценарий в нескольких местах.

Есть предложения?

Код выглядит следующим образом (Примечание: я пытаюсь найти материал из других статей, поэтому в нем есть фрагмент или два [самая последняя попытка была указать рабочий каталог], но ядрокод все еще там. У меня также была идея сначала объявить блок скриптов, как вы делаете это с переменными в других языках программирования, но больше для удобства чтения):

 # 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 ~

РЕДАКТИРОВАТЬ

Спасибо за всю помощь.Настройка рабочего каталога работает, но теперь я получаю новую ошибку.У него нет ссылки на строку, поэтому я не уверен, где может быть проблема.Новый код ниже.Я переместил блок скриптов в дно, так что он отделен от остальной части кода.Я думал, что это может быть немного аккуратнее.Я прошу прощения за мое раннее форматирование кода.Я попытаюсь сделать лучше с новым примером.

     # 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

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

Как заметил Тео , вы на правильном пути, пытаясь передать нужный рабочий каталог в блок скрипта через -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: scope , который позволяет ссылаться на переменные в вызывающем scope непосредственно ;Вот упрощенный пример, который вы можете запустить непосредственно из приглашения (я использую $PWD в качестве нужного рабочего каталога, потому что $PSScriptRoot не определено в приглашении (в глобальной области видимости)):

Start-Job -ScriptBlock { Set-Location $using:PWD; Get-Location } |
  Receive-Job -Wait -AutoRemove

Если вы вызовете указанную выше команду, скажем, из C:\tmp, выходные данные будут также отражать этот путь, доказывая, что фоновое задание выполнялось в том же рабочем каталоге, что и вызывающая сторона.


Как правило, обратите внимание, что:

  • При запуске фоновых заданий с Start-Job используется каталог, возвращаемый [environment]::GetFolderPath('MyDocuments'), в качестве исходного рабочего каталога , который в Windowsобычно $HOME\Documents, тогда как на Unix-подобных платформах это просто $HOME (в PowerShell Core ).

    • Настройка рабочего каталога.для фонового задания с помощью Start-Job -InitializationScript аргумента блока скрипта с помощью ссылки $using: - например, Start-Job -InitializationScript { $using:PWD } { ... } должно работать, но не с Windows PowerShell v5.1 /PowerShell Core 6.2.0-preview.1, из-за ошибки .

    • Есть одобренный , но пока не реализованный запрос функции заставить Start-Job поддерживать параметр -WorkingDirectory, чтобы упростить указание рабочего каталога.

  • In PowerShell Core вы можетеальтернативно запускайте фоновые задания с постпозиционным & - так же, как POSIX-подобные оболочки, такие как bash do - в этом случае рабочий каталог вызывающей стороны наследуется ;Например:

    # 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
    
0 голосов
/ 26 октября 2018

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

...