Powershell:如何将从foreach返回的结果赋给变量

时间:2013-02-12 15:41:49

标签: powershell variable-assignment

我正在使用Windows 2003 Server x86,Powershell 1.0并且遇到了令人头疼的问题。当我尝试执行下面的脚本时,我收到一个错误:

Error "=" operator: System error. (Fehler beim "="-Operator: Systemfehler.)
+ $data =  <<<< Get-Content $log | % {

这是这个脚本:

$log = "C:\log file.txt"
$linePrefix = "PROCESS THIS LINE"
$trimStart = 25 #chars to ignore in the beginning
$started = 0

$data = Get-Content $log | % { 
    if ($_.length -ge $trimstart) { 
        $linedata = $_.substring($trimstart);
        $timesline = $linedata.startswith($lineprefix); 
        if ($started -eq 0 -and $timesline) { 
            $started = 1; 
        }
        if ($started) { 
            if ($timesline) { 
                $line = $linedata.substring($lineprefix.length).trimstart();
                if ($line.contains(": ")) {
                    write-output $line 
                } 
            } else { 
                break; 
            }
        }
    }
}

但是,如果我在没有$data =赋值的情况下执行它,它会完美运行并返回我预期的值。我也尝试过以下陈述

$data = (...)
$data = $(...)

并使用表达式声明函数但没有成功。

你能不能给我一个提示,为什么会这样?

更新:我尝试在分配符号前后删除空格并出现类似错误,但现在powershell不喜欢$data=G字符串

Error "=" operator: System error (Fehler beim "="-Operator: Systemfehler.)
At C:\kosmo\scripts\ps\test.ps1:60 Symbol:7
+ $data=G <<<< et-Content $log | % {

3 个答案:

答案 0 :(得分:0)

我已将您的代码转换为将返回String对象的函数。所以你可以按如下方式运行它:

$data = Parse-LogFile

您需要先将该功能导入到shell中,您只需复制并粘贴它即可。

Function Parse-LogFile
{
   $log = gc "C:\log file.txt"
   $linePrefix = "PROCESS THIS LINE"
   $trimStart = 25 #chars to ignore in the beginning
   $started = 0
   $ReturnText = New-Object System.Text.StringBuilder
   foreach ( $line in $log )
   {
      if ($_.length -ge $trimstart)
      {
         $linedata = $_.substring($trimstart);
         $timesline = $linedata.startswith($lineprefix);

         if ($started -eq 0 -and $timesline) 
         { 
            $started = 1; 
         }

         if ($started) 
         { 
            if ($timesline)
            {
               $line = $linedata.substring($lineprefix.length).trimstart();
               if ($line.contains(": "))
               {
                  $ReturnText.Append($line)
                  $ReturnText.AppendLine()
               }
            }
            else
            {
               break;
            }
         }
      }
   }

   Retrun $ReturnText.ToString()
}

答案 1 :(得分:0)

虽然我无法在我的计算机上重现您的问题,但我认为问题可能与您在分配期间在管道中使用break有关。作为第一个测试,如果您删除break,它会改变一些事情吗?

假设这确实是问题,您可以稍微重构以避免直接进行分配。相反,您可以在循环内部构建$data变量。

$log = ".\logfile.log"
$linePrefix = "PROCESS THIS LINE"
$trimStart = 25 #chars to ignore in the beginning
$started = 0
$data = @()  # initialize blank data array

Get-Content $log | % { 
    if ($_.length -ge $trimstart) { 
        $linedata = $_.substring($trimstart);
        $timesline = $linedata.startswith($lineprefix); 
        if ($started -eq 0 -and $timesline) { 
            $started = 1; 
        }
        if ($started) { 
            if ($timesline) { 
                $line = $linedata.substring($lineprefix.length).trimstart();
                if ($line.contains(": ")) {
                    # add to your result here
                    $data += $line 
                } 
            } else { 
                break; 
            }
        }
    }
}

现在$data包含您想要的内容。这可能不是性能最佳的代码,具体取决于您期望的结果数量,但它可能会给您一个开始。

答案 2 :(得分:0)

非常感谢您的提示!可惜,但没有任何建议确实解决了我的问题。 最有帮助的是提示拉特金关于休息行为。经过一些reading我发现,在Foreach-Object cmdlet中,由%,别名或中断语句停止整个脚本

function getFilteredResult {

    1..50 | % { 
        if ($_ -le 20) { 
            if (($_ % 2) -eq 0) {
                write-output $_;
            }
        } else {
            break; #terminates whole script, you don't even get "after got result"
            #continue; #save result as break
        }
    }   
}

Write-Host "before getting result"

$result = getFilteredResult

$result | % {$i=0}{ ++$i; Write-Host "$i) $_" }

Write-Host "after got result"

输出仅显示before getting result。从那里我得出结论,没有办法在Powershell 1.0中做我想要的!所以我发现a way在我旧的2003服务器上安装Powershell 2.0(tip如何卸载Powershell 1.0之前;顺便说一句,我的Windows更新名称为KB926140-v5)。

好的,错误消失了。但我仍然没有想要的结果?!在旅程结束时,我发现非常useful post,并在下面写了

function getFilteredResult {
    Try {
        1..50 | % { 
            if ($_ -le 20) { 
                if (($_ % 2) -eq 0) {
                    write-output $_;
                }
            } else {
                #break; #terminates whole script, you don't even get "after got result"
                #continue; #terminates whole script, you don't even get "after got result"
                throw "terminated pipeline!"
            }
        }   
    } Catch {
        #ignore terminated pipeline exception
    }
}

Write-Host "before getting result"

$result = getFilteredResult

$result | % {$i=0}{ ++$i; Write-Host "$i) $_" }

Write-Host "after got result"

得到了答案:

before getting result
1) 2
2) 4
3) 6
4) 8
5) 10
6) 12
7) 14
8) 16
9) 18
10) 20
after got result

解释 如果我使用foreach语句,我可以使用break来估计行为,但是我正在处理大型日志文件,而foreach语句将整个文件读入内存,而ForEach-Object cmdlet则逐个处理行。我不确定Get-Content cmdlet,也许它还会读取整个文件以返回它,但我已经写下了我自己的cmdlet Get-Content-Reversed来读取文件末尾的行,这就是为什么我很漂亮确定它没有