Powershell StreamReader - как ждать, пока новый файл станет читаемым - PullRequest
0 голосов
/ 14 января 2020

Мой скрипт обычно предполагает наличие файла * .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."

Итак, я должен ждать, пока файл будет доступен, верно? Тем не менее, простой старт-сон на 5 секунд не работает. Но если я заверну его в al oop с помощью 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, чтобы обеспечить доступность файла?

Ответы [ 3 ]

1 голос
/ 14 января 2020

Как уже упоминалось в комментариях, используемый вами метод создает блокировку файла, которая действует до тех пор, пока вы не вызовете метод 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, чтобы снять блокировку.

1 голос
/ 14 января 2020

Метод Create возвращает объект FileStream. Поскольку StreamReader является производным от Stream, My Solution должен был быть преобразован в Astream Reader. Почти в одну строку ...:

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

ИЛИ, предложение от Йероена Мостерта:

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

При таком подходе вам не нужно беспокоиться о сборке мусора, потому что объект ссылается на переменную ...

Честно говоря, я не слишком уверен в этом, я считаю, что объект FileStream можно использовать непосредственно для чтения и записи, но я менее знаком, чем я с StreamReader & Writer объекты, поэтому, если бы это был я, я бы сделал повторное приведение, чтобы я мог двигаться дальше, но исследую дальше.

Кроме того, если бы вы использовали другой подход, я бы использовал .CLose () вместо .Dispose (). Мое понимание, основанное на. Net документации, является более тщательным, и в любом случае вызывает внутреннее удаление ...

1 голос
/ 14 января 2020

Вы просто должны закрыть дескриптор файла. Попробуйте:

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