Powershell StreamReader - 如何等待新文件可读

Powershell StreamReader - how to wait for a new file to be readable

我的脚本通常假设存在一个 *.txt 文件,其中包含有助于其更好运行的设置。但是,如果脚本不存在,它会创建一个本地文件来保存这些设置。我意识到没有逻辑 需要 然后读取此文件,但我想了解为什么我不能。

[void][System.IO.File]::Create($PSFileName)
$ReadPS = New-Object System.IO.StreamReader($PSFileName)

脚本可能(很少)创建文件后立即尝试读取它,这会生成以下错误:New-Object : Exception calling ".ctor" with "1" argument(s): "The process cannot access the file 'C:\Temp\MyFile.txt' because it is being used by another process."

所以我必须等待文件可用,对吗?然而,简单的 start-sleep 5s 是行不通的。但是如果我用 try-catch 将它包装在一个循环中,它每次都会在几分之一秒内工作:

[void][System.IO.File]::Create($PSFileName)
$RCount = 0                                                             # if new file created, sometimes it takes a while for the lock to be released. 
Do{
    try{
        $ReadPS = New-Object System.IO.StreamReader($PSFileName)
        $RCount+=100
    }catch{                                                             # if error encountered whilst setting up StreamReader, try again up to 100 times.
        $RCount++
        Start-Sleep -Milliseconds 1                                     # Wait long enough for the process to complete. 50ms seems to be the sweet spot for fewest loops at fastest performance
    }
}Until($RCount -ge 100)
$ReadPS.Close()
$ReadPS.Dispose()

这太复杂了。为什么文件会在任意时间段内保持锁定状态,而我等待它的时间似乎会增加?我可以在文件创建和 StreamReader 之间调整或添加什么以确保文件可用吗?

您只需关闭文件句柄即可。尝试:

$fh = [System.IO.File]::Create($PSFileName)
[void]$fh.Close()
[void]$fh.Dispose()
$ReadPS = New-Object System.IO.StreamReader($PSFileName)

正如评论中已经提到的那样,您使用的方法确实会在文件上创建一个锁,该锁会一直保留到您调用 close / dispose 方法或 powershell 会话结束.

这就是为什么您等待它的时间越长,您的会话保持打开状态的时间就越长,文件锁定的时间也就越长。

我建议您只使用 New-Item,这是 Powershell 的原生方式。

虽然您正在创建 StreamReader 对象,但不要忘记在结束后关闭/处置该对象。

New-Item -Path $PSFileName -ItemType File
$ReadPS = New-Object System.IO.StreamReader($PSFileName)

#Stuff

$ReadPS.Close()
$ReadPS.Dispose()

最后,如果出于某种原因您仍想使用 [System.IO.File]::Create($PSFileName),您还需要调用 close 方法来释放锁。

Create 方法returns 一个FileStream 对象。由于 StreamReader 派生自 Stream,我的解决方案是重铸为 astream reader。几乎是一条线...:[=​​12=]

$PSFileName = 'c:\temp\testfile.txt'
$Stream = [System.IO.StreamReader][System.IO.File]::Create($PSFileName)

或者,Jeroen Mostert 的建议:

$PSFileName = 'c:\temp\testfile.txt'
$Stream = [System.IO.StreamReader]::New( [System.IO.File]::Create($PSFileName) )

您不必担心使用此方法进行垃圾收集,因为生成的对象引用了变量...

老实说,我对此不太确定,我相信可以直接利用 FileStream 对象进行读写,但我对 StreamReader 和 Writer 对象的熟悉程度不如我,所以如果是我,我我会重铸,这样我就可以继续前进,但稍后会进一步研究。

此外,如果您使用其他方法,我会使用 .CLose() 而不是 .Dispose()。我基于.Net 文档的理解更接近更透彻,无论如何在内部调用Dispose...