【问题标题】:Powershell script exits ForEach-Object loop prematurely [duplicate]Powershell脚本过早退出ForEach-Object循环[重复]
【发布时间】:2018-02-12 03:18:09
【问题描述】:

所以我一直在编写一个脚本,它将获取存储在 238 个电子表格中的所有数据并将其复制到一个主工作表以及 9 个高级报告工作表中。我真的不知道为什么,但是在特定文档之后,脚本过早结束而没有发布任何错误。这很奇怪。我将在下面发布一些匿名代码,所以也许有人可以帮助我在这里找到我的方式的错误。

据我所知,它退出后的文档很好。我没有看到任何数据错误,并且在powershell完全调用脚本之前,信息已成功复制到主文档。

我尝试通过仅限制包含问题文件的文件夹来更改数据集的大小。它仍然在同一个文件之后结束,没有错误输出。由于公司政策,我无法上传文件,但与任何其他相同性质的文件相比,我真的看不出该文件上的数据有什么不同。

另外,提前为糟糕的代码道歉。我不是开发人员,并且一直在重新学习 powershell,因为它是我现在唯一可用的工具。

$StartTime = Get-Date -Format g
Write-Host $StartTime
pushd "Z:\Shared Documents\IO"
$TrackTemplate = "C:\Users\USERNAME\Desktop\IODATA\MasterTemplate.xlsx"

# Initialize the Master Spreadsheet
$xlMaster = New-Object -ComObject Excel.Application
$xlMaster.Visible = $False
$xlMaster.DisplayAlerts = $False
$MasterFilePath = "C:\Users\USERNAME\Desktop\IODATA\Master.xlsx"
Copy-Item $TrackTemplate $MasterFilePath
$wbMaster = $xlMaster.Workbooks.Open($MasterFilePath)
$wsMaster = $wbMaster.Worksheets.Item(2)
$wsMaster.Unprotect("PASSWORD")
$wsMasterRow = 3

# Initialize L4 Document Object
$xlL4 = New-Object -ComObject Excel.Application
$xlL4.Visible = $False
$xlL4.DisplayAlerts = $False

# Initialize object for input documents
$xlInput = New-Object -ComObject Excel.Application
$xlInput.Visible = $False
$xlInput.DisplayAlerts = $False

# Arrays used to create folder path names
$ArrayRoot = @("FOLDER1","FOLDER2","FOLDER3","FOLDER4","FOLDER5","FOLDER6","FOLDER7","FOLDER8","FOLDER9")
$ArrayShort = @("SUB1","SUB2","SUB3","SUB4","SUB5","SUB6","SUB7","SUB8","SUB9")
# $counter is used to iterate inside the loop over the short name array.
$counter = 0
$FileNumber = 0
$TotalFiles = 238
$ArrayRoot | ForEach-Object {
    $FilePathL4 = "C:\Users\USERNAME\Desktop\IODATA\ROLLUP\" + $ArrayShort[$counter] + "_DOC_ROLLUP.xlsx"
    Copy-Item $TrackTemplate $FilePathL4
    $wbL4 = $xlL4.Workbooks.Open($FilePathL4)
    $wsL4 = $wbL4.Worksheets.Item(2)
    $wsL4.Unprotect("PASSWORD")
    $wsL4Row = 3
    If ($ArrayShort[$counter] -eq "SUB7") {$FilePath = "Z:\Shared Documents\IO\" + $_ + "\" + $ArrayShort[$counter] + " - DOC v2\"}
    Else {$FilePath = "Z:\Shared Documents\IO\" + $_ + "\!" + $ArrayShort[$counter] + " - DOC v2\"}
    Get-ChildItem -Path $FilePath | ForEach-Object {
        If ($_.Name -eq "SPECIFIC_DOC.xlsx") {Continue}
        $FileNumber += 1
        Write-Host "$FileNumber / $TotalFiles $_"
        $wbInput = $xlInput.Workbooks.Open($_.FullName)
        $wsInput = $wbInput.Worksheets.Item(2)
        $wsInputLastRow = 0

        #Find the last row in the Input document
        For ($i = 3; $i -le 10000; $i++) {
            If ([string]::IsNullOrEmpty($wsInput.Cells.Item($i,1).Value2)) {
                $wsInputLastRow = $i - 1
                Break
            }
            Else { Continue }
        }

        [void]$wsInput.Range("A3:AC$wsInputLastRow").Copy()
        Start-Sleep -Seconds 1
        [void]$wsMaster.Range("A$wsMasterRow").PasteSpecial(-4163)
        Start-Sleep -Seconds 1
        $wsMasterRow += $wsInputLastRow - 2
        [void]$wsL4.Range("A$wsL4Row").PasteSpecial(-4163)
        Start-Sleep -Seconds 1
        $wsL4Row += $wsInputLastRow - 2

        $wbInput.Close()
        $wbMaster.Save()
    }
    $counter += 1
    $wsL4.Protect("PASSWORD")
    $wbL4.Save()
    $wbL4.Close()
}
$wsMaster.Protect("PASSWORD")
$wbMaster.Save()
$wbMaster.Close()
$xlMaster.Quit()

$EndTime = Get-Date -Format g
$TimeTotal = New-Timespan -Start $StartTime -End $EndTime
Write-Host $TimeTotal

【问题讨论】:

  • 你可以下载Notepad++,然后去View > Show Symbol > Show All Characters。比较加载正常的文档和有问题的文档。我猜这可能是 End of Line (EOL) 转换。如果是这种情况,请转到Edit > EOL Conversion 修改问题文件并重试。
  • 很遗憾,由于我们工作电脑的限制,我无法下载 Notepad++。 EOL 问题会导致这种情况吗?所有这些文件都是由同一进程同时生成的。我还尝试重建问题文档并重新运行脚本,现在它在以前运行良好的文档上退出而没有错误。
  • 我找到了解决方案。 Continue 命令不能在 ForEach-Object 循环内使用。我用 Return 替换了它,现在它工作正常。
  • 为了公平对待他人:您找到了自己的答案,将其发布为答案,以便其他人在遇到类似问题时也能找到答案。 (我认为它也会为您赢得积分。)

标签: powershell foreach


【解决方案1】:

要继续使用下一个输入对象进行管道处理,请在传递给ForEach-Object cmdlet 的脚本块中使用return - 而不是continue

以下简单示例跳过Get-ChildItem 输出的第一个对象并传递其余对象:

$i = 0; Get-ChildItem | ForEach-Object{ if ($i++ -eq 0) { return }; $_ }

目前 (PSv5.1) 没有直接的方法停止进一步输入对象的处理 - 有关解决方法,请参阅我的 this answer


相比之下,正如您所发现的,breakcontinue 只能在 for / foreach 语句的脚本块中按预期工作,而不是直接在脚本中块传递给ForeEach-Object cmdlet:

例如,以下会产生 no 输出(使用 break 会产生相同的效果):

$i = 0; Get-ChildItem | ForEach-Object{ if ($i++ -eq 0) { continue }; $_ }

原因是continuebreak 寻找一个封闭的for / foreach 语句 来继续/中断,因为没有,整个命令是退出;在脚本中,如果调用堆栈上没有封闭的for / foreach / switch 语句,则退出整个脚本。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 2022-10-17
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多