none
【PowerShell】CSV形式のログ出力の、項目追加が出来ない。

    質問

  • 【内容更新致しました。申し訳ございません。】

    前提・実現したい事
    ---
    PowerShell で、「CSV形式のログ取得」を考えております。
    文字コードは、『Unicode』です。


    発生している問題・Error Message
    ---
    表示させたい項目は、

    【日時、処理名、エラーコード、エラーメッセージ、エラー詳細】の5つです。

    「処理名」以外は、

    $test = Get-EventLog Application -Newest 1 | Select-Object TimeWritten, CategoryNumber, Message, ReplacementStrings

    のコードで取得出来ました。(恐らく)

    処理名にはファイル名を追加しようと考えており、現在

    # 配列化して、追加出来るかテスト
    $test = @()
    $test = Get-EventLog Application -Newest 1 | Select-Object TimeWritten
    $test += "タイトル"
    $test += Get-EventLog Application -Newest 1 | Select-Object CategoryNumber, Message, ReplacementStrings
    $test | Export-Csv $filename -Encoding $Encode -append -NoTypeInformation

    の様に、変数を配列化し、3回に分けて取得をしようとしてますが、3行目でエラーになってしまいます。

    この原因が分からず、困っております。

    ------

    【ログの取得例】

    日時             処理を    コード  メッセージ   詳細

    2017-07-11  タイトル  0  例外処理発生  例外処理が発生した為、処理を終了します。

    ------

    文字コードを、"default"に指定すると、1行目の【#TYPE System.String】が消えました。
    出来れば、1行目の文字も消したいです。(表示させない)

    実際のソースコード
    ---

    # Function実行
    Log
    
    # Function
    Function Log($LogString) {
    
        # "処理名" 以外のログ出力 → 成功
    	$test = Get-EventLog Application -Newest 1 | Select-Object TimeWritten, CategoryNumber, Message, ReplacementStrings
    	$test | Export-Csv $filename -Encoding $Encode -append
    
    	# "処理名" 追加出来るかテスト → 失敗
    	$test = @()
    	$test = Get-EventLog Application -Newest 1 | Select-Object TimeWritten
    	$test += "タイトル"
    	$test += Get-EventLog Application -Newest 1 | Select-Object CategoryNumber, Message, ReplacementStrings
    	$test | Export-Csv $filename -Encoding $Encode -append -NoTypeInformation
    }


    試した事
    ---

    どなたか分かる方いれば、よろしくお願い致します。


    補足情報(言語/FW/ツール等のVersion)
    ---
    ・Window 7
    ・PowerShell Ver 5.0


    • 編集済み mie.8 2017年7月11日 9:40 本文訂正
    2017年7月11日 8:12

回答

  • の様に、変数を配列化し、3回に分けて取得をしようとしてますが、3行目でエラーになってしまいます。

    ご提示のコードの1行目で$test変数に空の配列が格納されていますが、2行目で、$testにGet-EventLogの出力オブジェクトを代入してしまっているので、空の配列が破棄されてしまっています。

    そのため、3行目では、オブジェクトに文字列を追加しようとして失敗しています。

    いずれにしても、ここでは配列は使いません。作るべきなのはオブジェクトです。

    オブジェクトの作り方はいくつもありますが、今回のケースだと、Get-EventLogコマンドの出力オブジェクトであるEventLogEntryオブジェクトを加工するのが一番楽ではないかと思います。

    Select-Objectコマンドを用いれば、「TimeWritten→日時」のように、元のプロパティ名をリネームしたり、「処理」のような元のオブジェクトに存在しないプロパティを追加したりすることができます。

    たとえば以下のようなコードが考えられます。

    $test = Get-EventLog Application -Newest 1 | Select-Object `
        @{Name="日時"; Expression="TimeWritten"},
        @{Name="処理"; Expression={"タイトル"}},
        @{Name="コード"; Expression="CategoryNumber"},
        @{Name="メッセージ"; Expression="Message"},
        @{Name="詳細"; Expression="ReplacementStrings"}
    上記のようにSelect-Objectを使いこなすには、連想配列、スクリプトブロック、オブジェクト、プロパティ等、PowerShellの基本的な概念を把握する必要が出てくるかと思います。

    それと、これはフォーラムの使い方の話になりますが、回答がつくたびに質問文を書き直してしまうと、後からスレッドを見ると流れが分からなくなる恐れがありますので、元の質問文は残して追記するか、スレッドに返信として投稿することをお勧めします。


    2017年7月11日 10:33
    モデレータ
  • ReplacementStringsプロパティは文字列配列を返しますが、CSVでは配列を返すプロパティ値を表現できないのが原因かと思います。

    (CSVはもともと、階層構造を持ったデータを保存するのには適さないフォーマットだと思います)

    したがって、どうしても配列値をCSVに埋め込みたい場合は、何らかの文字列による表現に変換する必要があります。

    どのように表現するのかは考える必要がありますが、単純に、配列要素を「,」で連結するだけならば、以下のようなコードで可能です。

     @{Name="詳細"; Expression={$_.ReplacementStrings -join ","}}

    • 回答としてマーク mie.8 2017年7月11日 12:31
    • 回答としてマークされていない mie.8 2017年7月11日 12:43
    • 回答としてマーク mie.8 2017年7月11日 12:43
    2017年7月11日 11:59
    モデレータ
  • Get-Eventlogコマンドは、Windowsのイベントログを取得するものです。そしてイベントログはPowerShellのエラーを蓄積するだけのものではなく、もっと汎用的にWindowsで動作するアプリやサービスのログを記録するものです。

    ただし、PowerShell5.0以上には「PowerShell スクリプト ブロックのログ記録を有効にする」機能があり、スクリプトの呼び出し履歴をイベントログに記録することができます。

    PowerShellスクリプトブロックログは、新形式のイベントログ(ETW)なので、Get-Eventlogコマンドではなく、Get-WinEventコマンドを利用する必要があります。

    エラー情報を抽出するには例えば以下のようにします。

    Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell";Id="4100"}

    なお、PowerShellのエラーをイベントログに書き出すというのは、このスレッドのテーマと外れてくると思いますので、続けるのであれば別スレッドを立て直すようにお願いします。
    • 回答としてマーク mie.8 2017年7月12日 0:19
    2017年7月11日 20:22
    モデレータ

すべての返信

  • 文字コードを、"default"に指定すると、1行目の【#TYPE System.String】が消えました。
    > 出来れば、1行目の文字も消したいです。(表示させない)

    Export-Csvコマンドの-NoTypeInformationパラメータを用いれば可能です。

    コンソール上は、正しく表示されているのにCSV形式で出力すると入力されているのが文字数です。

    Export-Csvコマンドは、オブジェクト(の配列)のプロパティ値を、CSV形式のテキストファイルに出力するコマンドです。

    よって、ご提示のスクリプトだと文字列をそのまま渡しているので、stringオブジェクトに含まれる唯一のプロパティ値である、Lengthプロパティ(文字数)値のみがCSVに出力されることになります。

    そこで、実際には、何らかの文字列値プロパティを含むオブジェクトを生成してから、Export-Csvコマンドに渡す必要があります。具体的にはどのようなフォーマットのCSVを想定されていますでしょうか?

    "DateTime","Message"
    "
    2017-07-11 18:01:26.026","内容1…"
    "2017-07-11 18:03:10.138","内容2…"

    こんな感じでしょうか?

    2017年7月11日 9:05
    モデレータ
  • ご回答ありがとうございます。

    「 -NoTypeInformationパラメータ 」 を調べ、実装してみたいと思います。

    フォーマットですが、申し訳ございませんが、投稿内容を更新させて頂きました。

    *出力したいフォーマットも記載しました。

    お手数お掛け致しますが、よろしくお願い致します。

    2017年7月11日 9:43
  • の様に、変数を配列化し、3回に分けて取得をしようとしてますが、3行目でエラーになってしまいます。

    ご提示のコードの1行目で$test変数に空の配列が格納されていますが、2行目で、$testにGet-EventLogの出力オブジェクトを代入してしまっているので、空の配列が破棄されてしまっています。

    そのため、3行目では、オブジェクトに文字列を追加しようとして失敗しています。

    いずれにしても、ここでは配列は使いません。作るべきなのはオブジェクトです。

    オブジェクトの作り方はいくつもありますが、今回のケースだと、Get-EventLogコマンドの出力オブジェクトであるEventLogEntryオブジェクトを加工するのが一番楽ではないかと思います。

    Select-Objectコマンドを用いれば、「TimeWritten→日時」のように、元のプロパティ名をリネームしたり、「処理」のような元のオブジェクトに存在しないプロパティを追加したりすることができます。

    たとえば以下のようなコードが考えられます。

    $test = Get-EventLog Application -Newest 1 | Select-Object `
        @{Name="日時"; Expression="TimeWritten"},
        @{Name="処理"; Expression={"タイトル"}},
        @{Name="コード"; Expression="CategoryNumber"},
        @{Name="メッセージ"; Expression="Message"},
        @{Name="詳細"; Expression="ReplacementStrings"}
    上記のようにSelect-Objectを使いこなすには、連想配列、スクリプトブロック、オブジェクト、プロパティ等、PowerShellの基本的な概念を把握する必要が出てくるかと思います。

    それと、これはフォーラムの使い方の話になりますが、回答がつくたびに質問文を書き直してしまうと、後からスレッドを見ると流れが分からなくなる恐れがありますので、元の質問文は残して追記するか、スレッドに返信として投稿することをお勧めします。


    2017年7月11日 10:33
    モデレータ
  • ご回答ありがとうございます。

    サンプルのコード、大変勉強になりました。ありがとうございます。

    $title = "タイトル.csv"
    $test = Get-EventLog Application -Newest 1 | Select-Object @{Name="日時"; Expression="TimeWritten"},@{Name="処理"; Expression={"$($title)"}},@{Name="コード"; Expression="CategoryNumber"},@{Name="メッセージ"; Expression="Message"},@{Name="詳細"; Expression="ReplacementStrings"}
    $test | Export-Csv $filename -Encoding $Encode -append -NoTypeInformation

    上記コードで取得出来ました。

    しかし、コンソール上は「ReplacementStrings」に値があるのに、CSVに出力すると

    『System.String[]』となってしまいます。

    値が取得出来ていないと言う事でしょうか?

    何度も、申し訳ございません。

    フォーラムの使い方も、以後気を付けます。


    • 編集済み mie.8 2017年7月11日 11:42 修正
    2017年7月11日 11:41
  • ReplacementStringsプロパティは文字列配列を返しますが、CSVでは配列を返すプロパティ値を表現できないのが原因かと思います。

    (CSVはもともと、階層構造を持ったデータを保存するのには適さないフォーマットだと思います)

    したがって、どうしても配列値をCSVに埋め込みたい場合は、何らかの文字列による表現に変換する必要があります。

    どのように表現するのかは考える必要がありますが、単純に、配列要素を「,」で連結するだけならば、以下のようなコードで可能です。

     @{Name="詳細"; Expression={$_.ReplacementStrings -join ","}}

    • 回答としてマーク mie.8 2017年7月11日 12:31
    • 回答としてマークされていない mie.8 2017年7月11日 12:43
    • 回答としてマーク mie.8 2017年7月11日 12:43
    2017年7月11日 11:59
    モデレータ
  • 度々、ご回答ありがとうございます。

    そうなんですね。

    もっと深い所まで考慮しないといけないんですね。

    「エラーメッセージ・エラーログ出力取得」の為に、エラーをわざと発生させ、

    ログ出力処理の前に、「10 / 0」を追加し、取得したメッセージと合っているか

    write-host "info: $($_.Exception.Message)" -foregroundcolor red

    で、取得しました。

    上記コードだと、表示されるメッセージが【info: 0 で除算しようとしました。】

    $test = Get-EventLog Application -Newest 1 | Select-Object @{Name="日時"; Expression="TimeWritten"}, @{Name="処理"; Expression={"$($fileName)"}}, @{Name="コード"; Expression="CategoryNumber"}, @{Name="メッセージ"; Expression="Message"}, @{Name="詳細"; Expression={$_.ReplacementStrings -join ","}}
    $test | Export-Csv $fpath -Encoding $Encode -append -NoTypeInformation

    で取得したメッセージが【The Software Protection service has stopped.】

    *翻訳:ソフトウェア保護サービスが停止しました。

    ・・・でした。

    メッセージが一致しませんが、「Get-EventLog」では、エラーログ取得の為の

    メッセージ等は、そもそも取得出来ないのでしょうか?

    2017年7月11日 12:42
  • Get-Eventlogコマンドは、Windowsのイベントログを取得するものです。そしてイベントログはPowerShellのエラーを蓄積するだけのものではなく、もっと汎用的にWindowsで動作するアプリやサービスのログを記録するものです。

    ただし、PowerShell5.0以上には「PowerShell スクリプト ブロックのログ記録を有効にする」機能があり、スクリプトの呼び出し履歴をイベントログに記録することができます。

    PowerShellスクリプトブロックログは、新形式のイベントログ(ETW)なので、Get-Eventlogコマンドではなく、Get-WinEventコマンドを利用する必要があります。

    エラー情報を抽出するには例えば以下のようにします。

    Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell";Id="4100"}

    なお、PowerShellのエラーをイベントログに書き出すというのは、このスレッドのテーマと外れてくると思いますので、続けるのであれば別スレッドを立て直すようにお願いします。
    • 回答としてマーク mie.8 2017年7月12日 0:19
    2017年7月11日 20:22
    モデレータ
  • 細かくご説明して頂き、ありがとうございます。

    教えて頂いた事を参考に、更にいろいろと調べ、試してみたいと思います。

    ありがとうございました。

    2017年7月12日 0:20