在 PowerShell 脚本中打开文件流时的意外行为
Unexpected behavior when opening a file stream in a PowerShell script
我正在尝试创建一个附加(或创建,然后附加)现有文件的脚本。该文件正在与另一个系统连接,因此为了避免在我追加文件时读取文件的情况,我在其上创建了一个非共享锁。脚本似乎完美地工作,但有一个我无法理解的例外。这是一个代码示例:
$ErrorActionPreference = "Stop"
try
{
[System.IO.FileStream]$file1 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
}
catch
{
Write-Host "Unable to create a lock on this file"
}
有一种情况不会触发 catch 块 - 如果文件已经通过 powershell 控制台锁定并且如果此代码是 运行 来自脚本而不是直接输入 window .如果我首先在 Excel 中打开文件,catch 块总是有效。如果我通过在控制台中输入这段代码来执行它,它也总是有效。但是,如果我首先通过在控制台中键入此文件来锁定文件,然后 运行 尝试执行相同操作的脚本,则不会抛出异常并且不会触发 catch 块。
我在这里错过了什么?
更新:我做了额外的故障排除,这是我的发现。首先,如何重现:
- 创建一个名为 test.txt
的文件
- 创建一个名为
test1.ps1
的脚本,将上面的代码放入其中
运行 控制台中的这个命令:
[System.IO.FileStream]$file2 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
现在执行test1.ps1
脚本
并且脚本的 catch 块不会触发...在我的工作环境中!我在家里测试过,它按预期工作。所以我回去工作并再次测试它,这次使用 PC 本地驱动器上的文件并且它有效。使它不起作用的一个区别是我正在处理的文件位于我公司的 DFS 名称空间中。我在映射到我的 PC 上的本地网络驱动器上进行了尝试,它运行良好。
因此,无论出于何种原因,PowerShell 都不会像预期的那样对 DFS 命名空间上由 PowerShell 锁定的文件做出反应。我尝试过的所有其他应用程序都检测到锁定:记事本和 Excel 拒绝读取文件,当我在 PDF 文档上测试它时,Acrobat 也不会打开它。但是 PowerShell 并没有抱怨,甚至在没有错误的情况下处理了所有后续方法——这没有任何效果; CopyTo()
和 Flush()
方法没有失败,但文件没有改变。
当您运行将上述代码作为脚本时,file/handle会在脚本(或者更准确地说,PowerShell 进程)终止时自动关闭。您需要保持脚本 运行 保持句柄打开,例如通过在 Open()
调用之后添加一个无限循环:
$ErrorActionPreference = "Stop"
try {
$writeStream = [IO.File]::Open($FilePath,[IO.FileMode]::Append,[IO.FileAccess]::Write, [IO.FileShare]::None)
do {
Start-Sleep -Milliseconds 100
} while ($true)
} catch {
Write-Host "Unable to create a lock on this file"
}
我正在尝试创建一个附加(或创建,然后附加)现有文件的脚本。该文件正在与另一个系统连接,因此为了避免在我追加文件时读取文件的情况,我在其上创建了一个非共享锁。脚本似乎完美地工作,但有一个我无法理解的例外。这是一个代码示例:
$ErrorActionPreference = "Stop"
try
{
[System.IO.FileStream]$file1 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
}
catch
{
Write-Host "Unable to create a lock on this file"
}
有一种情况不会触发 catch 块 - 如果文件已经通过 powershell 控制台锁定并且如果此代码是 运行 来自脚本而不是直接输入 window .如果我首先在 Excel 中打开文件,catch 块总是有效。如果我通过在控制台中输入这段代码来执行它,它也总是有效。但是,如果我首先通过在控制台中键入此文件来锁定文件,然后 运行 尝试执行相同操作的脚本,则不会抛出异常并且不会触发 catch 块。
我在这里错过了什么?
更新:我做了额外的故障排除,这是我的发现。首先,如何重现:
- 创建一个名为 test.txt 的文件
- 创建一个名为
test1.ps1
的脚本,将上面的代码放入其中 运行 控制台中的这个命令:
[System.IO.FileStream]$file2 = [System.IO.File]::Open("test.txt", [System.IO.FileMode]::Append,[System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
现在执行
test1.ps1
脚本
并且脚本的 catch 块不会触发...在我的工作环境中!我在家里测试过,它按预期工作。所以我回去工作并再次测试它,这次使用 PC 本地驱动器上的文件并且它有效。使它不起作用的一个区别是我正在处理的文件位于我公司的 DFS 名称空间中。我在映射到我的 PC 上的本地网络驱动器上进行了尝试,它运行良好。
因此,无论出于何种原因,PowerShell 都不会像预期的那样对 DFS 命名空间上由 PowerShell 锁定的文件做出反应。我尝试过的所有其他应用程序都检测到锁定:记事本和 Excel 拒绝读取文件,当我在 PDF 文档上测试它时,Acrobat 也不会打开它。但是 PowerShell 并没有抱怨,甚至在没有错误的情况下处理了所有后续方法——这没有任何效果; CopyTo()
和 Flush()
方法没有失败,但文件没有改变。
当您运行将上述代码作为脚本时,file/handle会在脚本(或者更准确地说,PowerShell 进程)终止时自动关闭。您需要保持脚本 运行 保持句柄打开,例如通过在 Open()
调用之后添加一个无限循环:
$ErrorActionPreference = "Stop"
try {
$writeStream = [IO.File]::Open($FilePath,[IO.FileMode]::Append,[IO.FileAccess]::Write, [IO.FileShare]::None)
do {
Start-Sleep -Milliseconds 100
} while ($true)
} catch {
Write-Host "Unable to create a lock on this file"
}