web-dev-qa-db-ja.com

Get-Content -waitがドキュメントの説明どおりに機能しない

Get-Content path/to/logfile -Wait、ドキュメントで説明されているとおり、出力は実際には毎秒更新されません。 Windowsエクスプローラでログファイルがあるフォルダに移動し、フォルダを更新すると、Get-Contentは、最新の変更をログファイルに出力します。

tail -f cygwinで同じログファイルに(同時にではなくget-content)、それは期待どおりにテールし、私が何もしなくてもリアルタイムで更新されます。

なぜこれが起こるのか誰かが知っていますか?

26
julio.g

編集:BernhardKönigは、これがPowershell 5で最終的に修正されたとコメントで報告しています

たしかにそれは正しいね。 -WaitGet-Contentオプションは、ファイルが閉じられるまで待機してから、さらにコンテンツを読み取ります。これをPowershellで説明することは可能ですが、次のようなループとして正しく実行するのは難しい場合があります。

while (1){
get-date | add-content c:\tesetfiles\test1.txt 
Start-Sleep -Milliseconds 500
}

ループを回るたびに出力ファイルを開いて閉じます。

この問題を説明するには、2つのPowershellウィンドウ(またはISEの2つのタブ)を開きます。次のコマンドを入力します。

PS C:\> 1..30 | % { "${_}: Write $(Get-Date -Format "hh:mm:ss")"; start-sleep 1 } >C:\temp\t.txt

これは30秒間実行され、毎秒1行がファイルに書き込まれますが、毎回ファイルを閉じたり開いたりすることはありません。

別のウィンドウでGet-Contentを使用してファイルを読み取ります。

get-content c:\temp\t.txt -tail 1 -wait | % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }

-Waitオプションを使用すると、 Ctrl+C コマンドを停止して、コマンドを3回実行して、最初の2つのそれぞれの数秒後に数秒待機し、3番目のコマンドの後にさらに待機すると、次の出力が得られます。

PS C:\> get-content c:\temp\t.txt -tail 1 -wait | % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }
8: Write 12:15:09 read at 12:15:09

PS C:\> get-content c:\temp\t.txt -tail 1 -wait | % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }
13: Write 12:15:14 read at 12:15:15

PS C:\> get-content c:\temp\t.txt -tail 1 -wait | % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }
19: Write 12:15:20 read at 12:15:20
20: Write 12:15:21 read at 12:15:32
21: Write 12:15:22 read at 12:15:32
22: Write 12:15:23 read at 12:15:32
23: Write 12:15:24 read at 12:15:32
24: Write 12:15:25 read at 12:15:32
25: Write 12:15:26 read at 12:15:32
26: Write 12:15:27 read at 12:15:32
27: Write 12:15:28 read at 12:15:32
28: Write 12:15:29 read at 12:15:32
29: Write 12:15:30 read at 12:15:32
30: Write 12:15:31 read at 12:15:32

これから私ははっきりと見ることができます:

  1. コマンドが実行されるたびに、最新の行がファイルに書き込まれます。つまり、キャッシュに問題はなく、フラッシュする必要のあるバッファもありません。
  2. 1行だけが読み取られ、他のウィンドウで実行されているコマンドが完了するまで、それ以上の出力は表示されません。
  3. 完了すると、保留中の行がすべて一緒に表示されます。これは、ファイルを閉じるソースプログラムによってトリガーされたに違いありません。

また、他の2つのウィンドウでGet-Contentコマンドを実行して演習を繰り返したとき、1つのウィンドウが3行目を読み取り、もう1つのウィンドウが6行目を待ったため、その行は確実にファイルに書き込まれています。

-Waitオプションが、アドバタイズされた1秒を待たずに、ファイルクローズイベントを待っていることはかなり決定的なようです。ドキュメントが間違っています。

編集: Adi Inbarは私が間違っていると主張しているように思われるので、ここで示した例ではPowershellを使用しているため、Powershellの議論に最も適していると思われるので、追加する必要があります。また、Python=を使用して動作が説明したとおりであることを確認しました。

アプリケーションがバッファをフラッシュした場合、ファイルに書き込まれたコンテンツは、新しいGet-Content -Waitコマンドですぐに読み取ることができます。

Get-Content -Waitを使用するPowershellインスタンスは、後で開始された別のPowershellインスタンスが新しいデータを参照しても、書き込まれているファイルに新しいコンテンツを表示しません。これは、データがPowershellにアクセス可能であり、Get-Content -Waitが1秒間隔でポーリングしていないが、次にデータを検索する前にトリガーイベントを待機していることを最終的に証明します。

dirによって報告されるファイルのサイズは、行が追加されている間に更新されるため、Powershellがディレクトリエントリのサイズが更新されるのを待機している場合ではありません。

ファイルを書き込むプロセスがファイルを閉じると、Get-Content -Waitは新しいコンテンツをほぼ瞬時に表示します。データがディスクにフラッシュされるまで待機していた場合、Windowsがディスクキャッシュをフラッシュするまで最大で遅延が発生します。

@AdiInbar、申し訳ありませんが、ファイルを保存したときにExcelで何が行われるのか理解できません。よく見てください。 test.xlsxを編集している場合、同じフォルダに隠しファイル~test.xlsxもあります。 dir ~test.xlsx -hidden | select CreationTimeを使用して、いつ作成されたかを確認します。ファイルを保存すると、test.xlsx~test.xlsxから作成されます。つまり、Excelに保存すると~ファイルに保存され、元のファイルが削除され、~ファイルの名前が元の名前に変更され、新しい~ファイルが作成されます。そこにはたくさんの開閉があります。

保存する前は、見ているファイルが開いており、そのファイルが開いた後は別のファイルになります。 Excelはシナリオが複雑すぎて、何がGet-Contentをトリガーして新しいコンテンツを表示するのかを正確に説明することはできないと思いますが、誤って解釈したと思います。

21
Duncan

PowershellがファイルのLast Modifiedプロパティを監視しているようです。問題は、「パフォーマンス上の理由から」このプロパティを含むNTFSメタデータが 自動的に更新されない であることです。

1つの状況は、ファイルハンドルが閉じられたときです(つまり @ Duncanの観測 )。もう1つは、ファイルの情報が直接照会されるため、質問で言及されているエクスプローラーの更新動作です。

PowershellでGet-Content -Waitを使用してログを監視し、エクスプローラーをLast Modified列が表示されている詳細ビューのフォルダーで開くことで、相関関係を確認できます。ファイルが変更されても、Last Modifiedは自動的に更新されないことに注意してください。

別のウィンドウでファイルのプロパティを取得します。例えば。コマンドプロンプトで、typeファイル。または、同じフォルダで別のエクスプローラウィンドウを開き、ファイルを右クリックしてそのプロパティを取得します(私にとっては、右クリックで十分です)。これを行うとすぐに、最初のエクスプローラーウィンドウが自動的にLast Modified列を更新し、Powershellが更新に気づき、ログに追いつきます。 Powershellでは、LastWriteTimeプロパティに触れるだけで十分です。

(Get-Item file.log).LastWriteTime = (Get-Item file.log).LastWriteTime

または

(Get-Item file.log).LastWriteTime = Get-Date

これは今私のために働いています:

Start-Job {
  $f=Get-Item full\path\to\log
  while (1) {
    $f.LastWriteTime = Get-Date
    Start-Sleep -Seconds 10
  }
}
Get-Content path\to\log -Wait
11
CupawnTae

それを再現する方法を教えていただけますか?

このスクリプトを1つのPSセッションで開始できます。

get-content c:\testfiles\test1.txt -wait

そしてこれは別のセッションで:

while (1){
get-date | add-content c:\tesetfiles\test1.txt 
Start-Sleep -Milliseconds 500
}

そして、最初のセッションで新しいエントリが書き込まれているのがわかります。

2
mjolinor

WindowsUpdate.logをリアルタイムで見ようとしたときに、同じ問題が発生しました。理想的ではありませんが、以下のコードで進行状況を監視できました。 -上記と同じファイル書き込み制限のため、待機は機能しませんでした。

最後の10行を表示し、10秒間スリープし、画面をクリアしてから、最後の10行を再び表示します。 CTRL + Cでストリームを停止します。

 while(1){
Get-Content C:\Windows\WindowsUpdate.log -tail 10 
    Start-Sleep -Seconds 10
    Clear 
    }
1
Justin Kittle

Get-contentは、Windows APIを通過する場合にのみ機能し、ファイルへの追加のバージョンが異なるようです。

program.exe > output.txt

その後

get-content output.txt -wait

更新されません。だが

program.exe | add-content output.txt

で動作します。

get-content output.txt -wait    

したがって、アプリケーションがどのように出力するかによって異なります。

1
Milhous