我注意到当Get-Content path/to/logfile -Wait
时,输出实际上每秒都没有刷新,因为文档说明它应该。如果我将Windows资源管理器转到日志文件所在的文件夹并刷新文件夹,则Get-Content
会将最新更改输出到日志文件。
如果我在同一个日志文件上尝试使用cygwin tail -f
(与尝试get-content
时不同的话),那么就像人们期望的那样尝试尾巴,让我不必在不做的情况下刷新实时任何东西。
有谁知道为什么会这样?
答案 0 :(得分:21)
编辑 BernhardKönig在评论中报告说,这最终已在Powershell 5中得到修复。
你是对的。 -Wait
上的Get-Content
选项等待文件关闭,然后才能读取更多内容。可以在Powershell中证明这一点,但是可以很难将其作为循环来实现,例如:
while (1){
get-date | add-content c:\tesetfiles\test1.txt
Start-Sleep -Milliseconds 500
}
每次循环都会打开和关闭输出文件。
要演示此问题,请打开两个Powershell窗口(或ISE中的两个选项卡)。在一个输入此命令:
PS C:\> 1..30 | % { "${_}: Write $(Get-Date -Format "hh:mm:ss")"; start-sleep 1 } >C:\temp\t.txt
每秒会在文件中写入1行30秒,但每次都不会关闭并打开文件。
在另一个窗口中使用Get-Content
来读取文件:
get-content c:\temp\t.txt -tail 1 -wait | % { "$_ read at $(Get-Date -Format "hh:mm:ss")" }
使用-Wait
选项,您需要使用 Ctrl + C 来停止命令,以便在每次执行命令后等待几秒钟运行该命令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
从中我可以清楚地看到:
当我在另外两个窗口中运行Get-Content
命令重复练习时,一个窗口读取第3行然后等待,另一个窗口读取第6行,因此该行肯定被写入文件。 / p>
似乎非常确定-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
显示新内容的内容,但我确定您误解了它。
答案 1 :(得分:11)
看起来Powershell正在监视文件的Last Modified
属性。问题是“出于性能原因”,包含此属性的NTFS元数据为not automatically updated,除非在某些情况下。
一个情况是文件句柄关闭(因此@Duncan's observations)。另一个是直接查询文件的信息,因此问题中提到的Explorer刷新行为。
您可以通过让Powershell使用Get-Content -Wait
监控日志并在详细信息视图中的文件夹中打开资源管理器并显示Last Modified
列来观察相关性。请注意,Last Modified
在文件修改时不会自动更新。
现在在另一个窗口中获取该文件的属性。例如。在命令提示符下,type
该文件。或者在同一文件夹中打开另一个资源管理器窗口,然后右键单击该文件并获取其属性(对我来说,只需右键单击即可)。一旦这样做,第一个Explorer窗口将自动更新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
答案 2 :(得分:2)
你能告诉我们如何重现吗?
我可以在一个PS会话上启动此脚本:
get-content c:\testfiles\test1.txt -wait
这是在另一场会议中:
while (1){
get-date | add-content c:\tesetfiles\test1.txt
Start-Sleep -Milliseconds 500
}
我看到新条目写在第一个会话中。
答案 3 :(得分:1)
看来get-content仅在通过windows api时才有效,并且附加到文件的版本不同。
program.exe > output.txt
然后
get-content output.txt -wait
不会更新。但是
program.exe | add-content output.txt
可以使用。
get-content output.txt -wait
所以我猜这取决于应用程序的输出方式。
答案 4 :(得分:1)
我在尝试实时观看WindowsUpdate.log时遇到了同样的问题。虽然不理想,但下面的代码允许我监控进度。 - 由于上面讨论的相同文件写入限制,等待没有工作。
显示最后10行,休眠10秒,清除屏幕,然后再次显示最后10行。 CTRL + C停止流。
def splitLine(ax, x, y, splitNum, style1, style2):
'''Creates a two styled line given;
ax = an axis
x = an array of x coordinates for 2D Line
y = an array of y coordinates for 2D Line
splitNum = index number to split Line by x tick
style1 = dictionary for left part of Line
style2 = dictionary for right part of Line
'''
split = x[splitNum]
low_mask = x <= split
upper_mask = x >= split
lower, = ax.plot(x[low_mask], y[low_mask], **style1)
upper, = ax.plot(x[upper_mask], y[upper_mask], **style2)
return lower, upper
r = getPostData(wall)
earlyLike = {'color': 'r', 'lw': 1, 'ls': '--'}
agedLike = {'color': 'r', 'lw': 2, 'ls': '-', 'label': 'Likes'}
earlyShare = {'color': 'b', 'lw': 1, 'ls': '--'}
agedShare = {'color': 'b', 'lw': 2, 'ls': '-', 'label': 'Shares'}
fig, ax = plt.subplots()
splitLine(ax, np.array(range(LIMIT)), np.array(r['Likes']), 1, earlyLike, agedLike)
splitLine(ax, np.array(range(LIMIT)), np.array(r['Shares']), 1, earlyShare, agedShare)