PowerShell - Get-Content - CreateFile 将 dwShareMode 设置为 FILE_SHARE_READ | FILE_SHARE_WRITE

PowerShell - Get-Content - CreateFile sets dwShareMode to be FILE_SHARE_READ | FILE_SHARE_WRITE

是否有任何原因在使用 Get-Content -path file.txt 时,CreateFileW API 参数是:

dwDesiredAccess         GENERIC_READ
dwShareMode             FILE_SHARE_READ | FILE_SHARE_WRITE  
lpSecurityAttributes    NULL
dwCreationDisposition   OPEN_EXISTING
dwFlagsAndAttributes    FILE_FLAG_OPEN_NO_RECALL

如果我使用: [System.IO.File]::ReadAllLines("file.txt")

dwDesiredAccess         GENERIC_READ
dwShareMode             FILE_SHARE_READ
lpSecurityAttributes    NULL
dwCreationDisposition   OPEN_EXISTING
dwFlagsAndAttributes    FILE_FLAG_OPEN_NO_RECALL | FILE_FLAG_SEQUENTIAL_SCAN

为什么 FILE_SHARE_WRITE 设置为读取?有任何想法吗?谢谢

共享模式参数用于指示调用者可以接受哪些类型的共享并发访问 - Get-Content 正在使用 FILE_SHARE_READ | FILE_SHARE_WRITE 告诉操作系统“我不关心是否有人else 想在我持有此文件句柄时读取或写入文件。

这在连续跟踪日志文件时很重要,例如:

# imagine this is a log file
$tmpFile = New-TemporaryFile

$backgroundJob = Start-Job {
  param([string]$LiteralPath)

  1..10 |ForEach-Object {
    "Log entry $_" |Add-Content @PSBoundParameters

    Start-Sleep -Milliseconds 500
  }
} -ArgumentList $tmpFile.FullName |Out-Null

# Tail the file and read the 10 log message as they roll in
$tmpFile |Get-Content -Wait |Select -First 10

$tmpFile |Remove-Item

如果你 运行 上面的代码,你会发现 Get-Content -Wait 能够连续读取文件,即使我们打开它并从后台作业写入它 10次.

如果未使用 FILE_SHARE_WRITE.

打开基础文件句柄,这 不可能

为了观察这一点,让我们采用相同的示例并从使用 only FILE_SHARE_READ:

打开的文件句柄中读取
$tmpFile = New-TemporaryFile

$backgroundJob = Start-Job {
  param([string]$LiteralPath)

  1..10 |ForEach-Object {
    "Log entry $_" |Add-Content @PSBoundParameters

    Start-Sleep -Milliseconds 500
  }
} -ArgumentList $tmpFile.FullName

try {
  # OpenRead() will open the file with SHARE_READ
  $rfs = [System.IO.StreamReader]::new($tmpFile.OpenRead())

  # Attempt to read the log lines, time out after 10 x 500ms
  $c = 10
  while(--$c){
    $line = $rfs.ReadLine()

    if($line -is [string]){
      Write-Host $line
    }
    else{
      # wait a little longer
      Start-Sleep -Milliseconds 500
    }
  }
}
finally {
  # clean up file reader
  $rfs |ForEach-Object Dispose
  $tmpFile |Remove-Item
}

# Fetch background job output, observe file access errors
$backgroundJob |Receive-Job -AutoRemoveJob -Wait

您应该会看到后台作业未能将消息记录到文件中,而不是在屏幕上显示日志消息:

The process cannot access the file 'C:\Users\irm\AppData\Local\Temp\tmpECB9.tmp' because it is being used by another
process.
    + CategoryInfo          : WriteError: (C:\Users\irm\Ap...emp\tmpECB9.tmp:String) [Add-Content], IOException
    + FullyQualifiedErrorId : GetContentWriterIOError,Microsoft.PowerShell.Commands.AddContentCommand
    + PSComputerName        : localhost