【问题标题】:Prevent output from writing multiple times in Powershell script防止输出在 Powershell 脚本中多次写入
【发布时间】:2022-01-10 13:47:55
【问题描述】:

如果文件夹中的一个或多个文件与今天或昨天的日期匹配,我正在尝试输出 $message。在这种情况下,如果文件匹配日期,它应该为每个文件输出一条消息,例如,“文件 $filename 匹配”,其中 $filename 是匹配日期的文件的名称。

另一方面,如果文件与当前日期或昨天的日期不匹配,它也会返回一条消息,但只会返回一条文件名与日期不匹配的消息。

以下代码不会发生这种情况:

    $in = $false
    foreach ($file in Get-ChildItem -Path $Directory) {
        $today = $file.LastWriteTime.Date -eq [datetime]::Today
        $yesterday = $file.LastWriteTime.Date -eq [datetime]::Today.AddDays(-1)
        
        if ($today -or $yesterday) {
            $message = "file exists"
            $in = $true
            Write-Output $message
        }
    }
    

    if ($in -eq $false) {
        $message_error = "file does not exist" 
        Write-Output $message_error
    }

当一个文件匹配日期(昨天或今天的日期)时,它会多次输出消息,如果像上面那样缩进超过 20 倍。

如果不匹配,它只输出1x,但应该是2x,因为有2个文件不匹配日期。

谁能给我一个提示?


输出应如下所示

如果文件夹中的一个或多个文件与日期匹配:

"File example.txt matches date
and
File example2.txt matches date"

如果文件夹中的文件与日期不匹配,则否则:

"File example.txt, example2.txt don't match dates"

编辑

相反,我正在尝试这样:

 $in = $false
 foreach ($file in Get-ChildItem -Path $Directory) {
    $today = $file.LastWriteTime.Date -eq [datetime]::Today
    $yesterday = $file.LastWriteTime.Date -eq [datetime]::Today.AddDays(-1)
                
    if ($today -or $yesterday) {
        $msg_found = "file" + $file "exists" 
        $in = $true
               
    } else {
        $msg_notfound = "File" + $file + "Not found"
            
    }
      
 Write-Output $msg_notfound
 Write-Output $msg_found

这里的消息以正确的数量输出,但它们在任何情况下都会出现。如果我使用 if 循环会解决吗?

【问题讨论】:

  • 你能发布预期的输出应该是什么样子吗?如果您希望它每次都告诉您,则必须将消息放在循环中的 if 语句中。看起来并不复杂,只需要知道预期的输出应该是什么样子。
  • 我已经用预期的输出编辑了问题。
  • 为你想要的输出创建一个哈希表......
  • 仔细检查您发布的代码是否不止一次输出,因为目前我认为它不可能输出不止一次
  • @Otter 你说得对,我会编辑这个问题,在这种情况下,如果文件匹配实际上是可以的,但是如果它不匹配并且它进入第二个 if,它会打印$message_error 和 $message

标签: powershell foreach


【解决方案1】:

只需在变量中收集与参考日期不匹配的日期,并将匹配的文件直接写入控制台:

# set the reference date to midnight using .Date
$refDate = [datetime]::Today.AddDays(-1).Date
# loop through the files in the directory and test their LastWriteTime dates
$notMatch = foreach ($file in (Get-ChildItem -Path $Directory -File)) {
    if ($file.LastWriteTime -ge $refDate) { 
        # use Write-Host so this message doesn't end up in variable $notMatch
        Write-Host "File '$($file.Name)' matches the date"
    }
    else {
        # output the filename so it gets collected in variable $notMatch
        $file.Name
    }
}

# now output the list of files that didn't match to the console
if (@($notMatch).Count) {
    $message = "Files that don't match the date:`r`n{0}" -f ($notMatch -join [environment]::NewLine)
    Write-Host $message -ForegroundColor Red
}
else {
    Write-Host "All files matched the date!" -ForegroundColor Green
}

既然你问了,这里有两个可供选择的代码

1:捕获数组变量中的所有内容并稍后拆分

# set the reference date to midnight using .Date
$refDate = [datetime]::Today.AddDays(-1).Date
# loop through the files in the directory and test their LastWriteTime dates
$result = foreach ($file in (Get-ChildItem -Path $Directory -File)) {
    [PsCustomObject]@{
        File        = $file.Name
        MatchesDate = ($file.LastWriteTime -ge $refDate)  # --> either $true or $false
    }
}

# you now have a list of every file name found in the directory, together with a boolean
# value teling you if the LastWriteTime matched the $refDate or not.

# split out for console output
$matchedFiles = ($result | Where-Object { $_.MatchesDate }).File -join [environment]::NewLine
Write-Host "Files that were last modified yesterday or today:`r`n{0}" -f $matchedFiles -ForegroundColor Green

$notMatch = ($result | Where-Object {-not $_.MatchesDate }).File -join [environment]::NewLine
Write-Host "Files that were last modified before yesterday:`r`n{0}" -f $notMatch -ForegroundColor Red

2:在两个单独的列表中捕获所有内容

# set the reference date to midnight using .Date
$refDate = [datetime]::Today.AddDays(-1).Date

# create two lists
$matchedFiles = [System.Collections.Generic.List[string]]::new()
$notMatch     = [System.Collections.Generic.List[string]]::new()

# loop through the files in the directory and test their LastWriteTime dates
# we're not outputting anything inside the list, but add to the appropriate list instead
foreach ($file in (Get-ChildItem -Path $Directory -File)) {
    if ($file.LastWriteTime -ge $refDate) { 
        $matchedFiles.Add($file.Name)
    }
    else {
        $notMatch.Add($file.Name)
    }
}

# you now have separate lists of every iles that matched and files that did not match the date criterium

# console output
Write-Host "Files that were last modified yesterday or today:`r`n{0}" -f ($matchedFiles -join [environment]::NewLine) -ForegroundColor Green
Write-Host "Files that were last modified before yesterday:`r`n{0}" -f ($notMatch -join [environment]::NewLine) -ForegroundColor Red

【讨论】:

  • 嗨@Theo,我非常喜欢这种方法。非常感谢。你能检查一下我在帖子中的最后一次编辑吗?
  • @ack31 请查看我的编辑,我在其中为您发布了两种替代方法。
【解决方案2】:

从我的 cmets 继续...如果您要输出大于或等于昨天日期的文件的名称,您可以通过以下几种方式来解决此问题:

Get-ChildItem -Path $Directory | 
    ForEach-Object -Begin {
        $notMatch = [System.Collections.ArrayList]::new()
        $date     = [datetime]::Today.AddDays(-1)
    } -Process {
        if ($_.LastWriteTime -ge $date) {
            Write-Host -Object  "File: [$($_.Name)] - Matches Date"
        }
        else {
            $null = $notMatch.Add($_.Name)
        }
    } -End {
        ($notMatch -join ', ' ) + "don't match dates."
    }

幸运的是,对匹配的文件进行匹配是一个简单的解决方案。至于那些不是,您可以创建一个数组来保存名称的值,直到循环结束以显示由 (", ") 连接的值。

【讨论】:

    【解决方案3】:

    试试这个:

     foreach ($file in Get-ChildItem -Path $Directory) {
        $today = $file.LastWriteTime.Date -eq [datetime]::Today
        $yesterday = $file.LastWriteTime.Date -eq [datetime]::Today.AddDays(-1)
        
        # If file was written to today or yesterday, write $msg_found           
        if ($today -or $yesterday) {
            $msg_found = "File " + $file + " exists"
            Write-Output $msg_found         
        }
        
        # If not, write $msg_notfound 
        else {
            $msg_notfound = "File " + $file + " not found"
            Write-Output $msg_notfound
                
        }
    }
    

    如果我的理解正确,那么它同时编写 $msg_found 和 $msg_notfound 变量的原因是因为您将它们放在 If/Else 语句之外。

    将它们保留在 If/Else 中将使其仅在 If 或 Else 中写入 $msg。 p>

    编辑: 在上面,我删除了 $in 变量,但我不确定您是否需要它。所以我修改了你在编辑之前发布的脚本,以与我最初放置的脚本做同样的事情,以防你这样做。

    我离开 cmets 是为了告诉你我改变了什么,但如果你有任何问题,请告诉我:

    $in = $false
    foreach ($file in Get-ChildItem -Path $Directory) {
        $today = $file.LastWriteTime.Date -eq [datetime]::Today
        $yesterday = $file.LastWriteTime.Date -eq [datetime]::Today.AddDays(-1)
        
        # If file was made today or yesterday, $in is true, write $message 
        if ($today -or $yesterday) {
            $message = "$File exists"
            $in = $true
            Write-Output $message
        }
        
        # If not, $in is false for this file in the ForEach loop
        else{
            $in = $false
        }
        
        # Moved the second If statement into the ForEach loop 
        # If the If/Else set $in as false for this file, write $message_error
        if ($in -eq $false) {
        $message_error = "$File does not exist" 
        Write-Output $message_error
        }
    }
    

    【讨论】:

    • 谢谢乔希!实际上非常有帮助!
    【解决方案4】:

    使用.Where() 方法与Split 模式相结合的已提出答案的替代方案:

    $directory = '.'
    $targetDate = [datetime]::Today.AddDays(-1)
    
    $Match, $noMatch = (Get-ChildItem -Path $directory -File).Where({
        $_.LastWriteTime -ge $targetDate
    }, 'Split')
    
    if($Match)
    {
        '=' * 60
        [string]::Format(
            '{0} file{1} found with Date greater than or equal to {2}',
            $Match.Count,
            ($null, 's')[[int]$Match.Count -gt 1],
            $targetDate.ToShortDateString()
        )
        '=' * 60
        ''
        $Match.ForEach({
            '- File: {0} with LastWriteTime {1}' -f $_.Name, $_.LastWriteTime
        })
    }
    else
    {
        '=' * 60
        [string]::Format(
            'No files found with Date greater than or equal to {0}',
            $targetDate.ToShortDateString()
        )
        '=' * 60
        $noMatch | Format-Wide Name -AutoSize
    }
    

    【讨论】:

      【解决方案5】:
      $Directory="T:\temp2"
      $in = $false
      $message=""
      foreach ($file in Get-ChildItem -Path $Directory) {
          $today = $file.LastWriteTime.Date -eq [datetime]::Today
          $yesterday = $file.LastWriteTime.Date -eq [datetime]::Today.AddDays(-1)
          
          if ($today -or $yesterday) {
              $in = $true
              Write-Output ("File " + $file.Name +" matches date")
          } else {
              $message +=(" "+$file.Name+",")
          }
      }
      
      
      if ($in -eq $false) {
          $message_error= ("File" + $message.Substring(0,$message.Length-1) + " don't matches date")
          Write-Output $message_error
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-30
        • 2012-12-21
        • 1970-01-01
        • 2023-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多