2

In powershell, the command "Get-Content (filename)" doesn't have any problems reading a file locked for writing in read-only mode.

So Why can't I do the same using the .net function ReadAllLines from PowerShell? I don't understand why ReadAllLines() would be more intrusive than Get-Contents? All it does is reads the content of the file and then closes it.

Anyways, I prefer to use the .net functions over the native powershell functions because they are less messed up, with the exception of ReadAllLine not working correctly with respect to partially locked files... Is there a way to get ReadAllLines to .net to work the same as Get-Content when reading a partially locked file... one that's ok for reading but locked for writing?

[Collections.Generic.List[string]]$lines  = [System.IO.File]::ReadAllLines($file)

verse:

$arr1   = Get-Content $file
$lines  = [Collections.Generic.List[string]]$arr1
pico
  • 1,660
  • 4
  • 22
  • 52
  • 2
    What happens when you invoke `[System.IO.File]::ReadAllLines($file)`? If it throws an error, please post the error message in full – Mathias R. Jessen Jan 25 '22 at 18:01
  • Exception calling "ReadAllLines" with "1" argument(s): "The process cannot access the file 'C:\Users\wpmoore\Desktop\collins\sandbox\sandbox.vsc.tbwork\out.sim\xsim.log' because it is being used by another process." At H:\tools\scripts\filter_xsimlog.ps1:26 char:1 + [Collections.Generic.List[string]]$lines = [System.IO.File]::ReadAll ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : IOException – pico Jan 25 '22 at 18:11
  • Get-Content doesn't have a problem... however... – pico Jan 25 '22 at 18:12
  • seems like somebody should file a bug with the .net people to rewrite the readalllines code to work the same as get-content from powershell and not throw an exception with partial lock access... – pico Jan 25 '22 at 18:13
  • 1
    Very interesting. I can reproduce the error you're getting with `ReadAllLines()` by just opening a writable stream to the same file before - but in that case, `Get-Content` _also_ fails for me... – Mathias R. Jessen Jan 25 '22 at 18:19

1 Answers1

3

I managed to reproduce this behavior in Windows PowerShell 5.1 as follows:

using namespace System.IO

# open writable file stream
$writeLocker = [FileStream]::new($file, [FileMode]::Open, [FileAccess]::Write)

# Get-Content can still read file contents (this doesn't throw)
Get-Content $file |Out-Null

# But this fails because another process owns a conflicting file handle
[File]::ReadAllLines($file)

This occurs because ReadAllLines() always requests read-only access to the target file - this conflicts with the already open writable file handle, and the attempt to open the file thus fails.

The FileSystem provider does not require read-only access, and thus Get-Content has no problem reading the files without exclusive access.

If you really want to, you can emulate this behavior with the BCL classes yourself by opening the file stream in read-write share mode (signaling that the caller doesn't care if someone already has read and/or write access to the file):

using namespace System.IO

class PicoFile
{
  static [string[]] ReadAllLines([string]$path)
  {
    $file = Get-Item -LiteralPath $path

    # request read-access but indicate sharing for read-write purposes are acceptable
    $fileStream = [FileStream]::new($file.FullName, [FileMode]::Open, [FileAccess]::Read, [FileShare]::ReadWrite)
    try {
      $fileReader = [StreamReader]::new($fileStream, [Text.Encoding]::Utf8)
      try {

        # keep reading lines from the file until there are no more lines    
        [string[]]$lines = while($null -ne ($line = $fileReader.ReadLine())){ $line }

        return $lines
      }
      finally {
        $fileReader |ForEach-Object Dispose
      }
    }
    finally {
      $fileStream |ForEach-Object Dispose
    }
  }
}

... but I'd strongly recommend just sticking with Get-Content :-)

Mathias R. Jessen
  • 157,619
  • 12
  • 148
  • 206