2017-03-02 11 views
2

Powershellコマンドの出力をファイルに保存したいとします。私はこれをls | out-file "path.txt"のようにします。私はこの呼び出しを1日に数回行い、関数呼び出し(この場合はls)がファイルを破壊する悪いデータを生成することを心配しています。私はバックアップが必要なように感じる!Powershellで既存の関数をデコレートする方法や関数オブジェクトを渡す方法はありますか?

私の次のステップは、out-fileコールをデコレートすることで、データを別のファイルに自動的にバックアップします。 1日に1回のバックアップで十分です。これは、下記のようにカスタム機能によって達成することができる。突然、私は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のfucntionを使用できるようにしたいと思います。 Out-FileコマンドをパラメータとしてOut-Bakに渡す方法があります。そのため、出力コマンド用のやや汎用的なデコレータとして機能を使用できますか?

+0

@wOxxOmバックアップ機能がoutコマンドと同じ出力方法を使用するようにしたいので、それはおそらくないでしょう...バックアップ機能はパイプラインの次のステップをどのように知っていますか? – LudvigH

+0

私はそれを取得しません。 backupメソッドはパイプラインの次のステップを知らないので、正しいメソッドを使用して更新ファイルを書き込むかどうかを知ることができません。 – LudvigH

+0

現在、私の 'Process'ブロックには2つの' out-file'ステートメントがあります。私は後者(非バックアップステートメント)をパススルーでパイプの下の書き込みステートメントに置き換える必要があるので、あなたの提案を理解しています。私の懸念は、1つの書き込みステートメントが 'process'ブロックに残され、そのステートメントが他のステートメントとまったく同じ種類になることです。私は理解できないことを理解していますか? – 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コマンドレットは、単純にパイプラインの終了後にファイルをコピーします。

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 
    } 
} 

警告:
たちはASTパーサのような難解なものを使用する必要があります以前のパイプラインコマンドからのファイルのパスを見つけるには、それは... | out-file "$($path)" | backupのような$()で失敗します。Get-PSCallStackが何らかの理由で表現内容を返しますので、呼び出し先として、そして今私は親呼び出しコンテキストを取得する他の方法を知らない。

+0

驚くほど複雑ですが、それは私の正確な問題を非常にうまく解決します!ありがとうございました! – LudvigH

関連する問題