【问题标题】:How to use a text file as input for a scriptblock - working directory in background jobs如何使用文本文件作为脚本块的输入 - 后台作业中的工作目录
【发布时间】:2019-03-31 14:52:07
【问题描述】:

我的任务是编写一个 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

感谢您提供的任何帮助。

干杯。

~大卫M~

编辑

感谢所有帮助。设置工作目录有效,但我现在收到一个新错误。它没有行参考,所以我不确定问题可能出在哪里。新代码如下。我已将 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”上的参数。参数为 null 或空。提供一个论据 不为 null 或为空,然后重试该命令。 + CategoryInfo : InvalidData: (:) [Test-Connection], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand + PSComputerName : 本地主机

【问题讨论】:

  • 乍一看,您没有使用您在-ArgumentLIst 中发送的$wrkngdir 参数。 Get-Content (Join-Path -Path $wrkngdir -ChildPath 'Hostnames.txt') 怎么样。另外,如果我可以建议,请对您的代码缩进做一些事情,因为现在它真的很难阅读..
  • 可能有助于开始在 PowerShell 高级功能的概念中思考和设计脚本。使用 Begin、Process、End 块。在 PowerShell ISE 中,CTRL + J 打开 sn-ps 并插入以查看示例。

标签: powershell working-directory start-job


【解决方案1】:

正如Theo 所观察到的那样,通过尝试通过-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 ,而在类 Unix 平台上它只是 $HOME(在 PowerShell Core 中)。

    • 设置工作目录。通过Start-Job-InitializationScript 脚本块参数通过$using: 引用进行后台作业 - 例如,Start-Job -InitializationScript { $using:PWD } { ... } 应该 工作,但在 Windows PowerShell v5.1 / PowerShell [Core] 6.x,由于 bug(该错误仍然存​​在于 PowerShell 7.0 中,但您可以在那里使用 -WorkingDirectory)。
  • PowerShell (Core) 7+ 中,Start-Job 现在明智地默认为调用者的工作目录,并且还支持 -WorkingDirectory 参数来简化指定工作目录。

  • PowerShell (Core) 6+ 中,您可以选择使用后置位 & 启动后台作业 - 与类似 POSIX 的 shell(如 @)的方式相同987654344@ 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
    

【讨论】:

    【解决方案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 是空的。

    【讨论】:

      猜你喜欢
      • 2020-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 1970-01-01
      • 1970-01-01
      • 2013-07-10
      • 1970-01-01
      相关资源
      最近更新 更多