2017-03-02 49 views
2

可以说我想将Powershell命令的输出保存到文件中。我会这样做ls | out-file "path.txt"。我每天都会拨打这个电话几次,并担心函数调用(在这种情况下为ls)会产生损坏我的文件的错误数据。我觉得我需要备份!如何在Powershell中修饰现有函数或传递函数对象?

对我而言,下一步将是装饰out-file调用,以便它自动备份单独文件中的数据。每天备份一次就足够了。这可以通过按照下面的定制out-bak函数来实现。突然间我用ls | Out-Bak "path.txt"进行了自动备份。

function Out-Bak { 
    [cmdletbinding()] 

    Param (
    [parameter(ValueFromPipeline)] 
    [string]$inputObject, 

    [parameter(Mandatory=$false)] 
    [string]$myPath 
) 

    Begin { 
    $backupPath = [System.IO.Path]::ChangeExtension($myPath,".bak_$([DateTime]::Now.ToShortDateString())") 
    Remove-Item $myPath 
    Remove-Item $backupPath 
    } 

    Process { 
    Out-File -InputObject $input -FilePath $myPath -Append 
    Out-File -InputObject $input -FilePath $backupPath -Append 
    } 
} 

这解决我的问题很好,但我希望能够准确地使用相同的模式为Out-csv和类似filewriting温控功能。有没有办法将Out-File命令作为参数传递给Out-Bak,以便我可以将该函数用作输出命令的某种通用装饰器?

+0

@wOxxOm因为我希望备份功能使用与out-command相同的输出方法,这可能不会......备份功能如何知道管道中的下一步是什么? – LudvigH

+0

我不明白。由于备份方法不知道管道中的下一步,它不知道是否使用正确的方法写入更新文件? – LudvigH

+0

目前我的'Process'-block有两个'out-file'语句。我理解你的建议,因为我应该用管道传递写入声明来替换后者(非备份声明)。我担心的是,一个写入语句将留在进程块中,我希望这个语句与另一个语句完全相同。你明白我不明白的是什么吗? – LudvigH

回答

2

让备份功能只做它的名字:备份文件。

  • ls | Out-File $path | Backup 
    
  • ........ | Out-File foo.txt | Backup 
    
  • ........ | Out-File -FilePath "$path\$name" | Backup 
    
  • ........ | Export-Csv -NoTypeInformation bar.csv | Backup 
    

backup cmdlet将简单地复制,一旦管道完成的文件。
要找到从先前的管道命令,我们将不得不使用晦涩难懂的东西,如AST解析器的文件路径:

function Backup { 
    end { 
     $bakCmdText = (Get-PSCallStack)[1].Position.text 
     $bakCmd = [ScriptBlock]::Create($bakCmdText). 
      Ast.EndBlock.Statements[0].PipelineElements[-2].CommandElements 
     $bakParamInfo = if (!$bakCmd) { @{} } 
      else { @{} + (Get-Command ($bakCmd[0].value)).Parameters } 
     $bakSource = ''; $bakLiteral = $false; $bakPos = 0 
     while (!$bakSource -and ++$bakPos -lt $bakCmd.count) { 
      $bakToken = $bakCmd[$bakPos] 
      if ($bakToken.ParameterName) { 
       if ($bakToken.ParameterName -match '^(File|Literal)?Path$') { 
        $bakLiteral = $bakToken.ParameterName -eq 'LiteralPath' 
       } elseif (!$bakParamInfo[$bakToken.ParameterName].SwitchParameter) { 
        $bakPos++ 
       } 
       continue 
      } 
      $bakSource = if ($bakToken.StringConstantType -in 'SingleQuoted', 'BareWord') { 
       $bakToken.value 
      } else { 
       [ScriptBlock]::Create($bakToken.extent.text). 
        InvokeWithContext(@{}, (Get-Variable bak* -scope 1)) 
      } 
     } 
     if (!$bakSource) { 
      Write-Warning "Could not find file path in pipeline emitter: $bakCmdText" 
      return 
     } 
     $backupTarget = "$bakSource" + '.' + [DateTime]::Now.ToShortDateString() + '.bak' 
     $bakParams = @{ $(if ($bakLiteral) {'LiteralPath'} else {'Path'}) = "$bakSource" } 
     copy @bakParams -destination $backupTarget -Force 
    } 
} 

警告:它失败$()... | out-file "$($path)" | backup因为GET-PSCallStack出于某种原因返回表达式内容作为被调用者,现在我不知道获取父调用上下文的其他方法。

+0

令人惊讶的复杂,但它很好地解决了我的确切问题!谢谢! – LudvigH