PowerShell 脚本来检查锁定文件的应用程序?

问题描述 投票:0回答:12

在 PowerShell 中使用,如何检查应用程序是否锁定文件?

我喜欢检查哪个进程/应用程序正在使用该文件,以便我可以关闭它。

powershell scripting filelock
12个回答
50
投票

您可以使用SysInternals工具handle.exe来完成此操作。尝试这样的事情:

PS> $handleOut = handle
PS> foreach ($line in $handleOut) { 
        if ($line -match '\S+\spid:') {
            $exe = $line
        } 
        elseif ($line -match 'C:\\Windows\\Fonts\\segoeui\.ttf')  { 
            "$exe - $line"
        }
     }
MSASCui.exe pid: 5608 ACME\hillr -   568: File  (---)   C:\Windows\Fonts\segoeui.ttf
...

23
投票

这可以帮助您:使用 PowerShell 找出哪个进程锁定了文件。它解析每个进程的 System.Diagnostics.ProcessModuleCollection Modules 属性,并查找锁定文件的文件路径:

$lockedFile="C:\Windows\System32\wshtcpip.dll"
Get-Process | foreach{$processVar = $_;$_.Modules | foreach{if($_.FileName -eq $lockedFile){$processVar.Name + " PID:" + $processVar.id}}}

20
投票

您应该能够从常规命令行或 PowerShell 使用 openfiles 命令

openfiles 内置工具可用于文件共享或本地文件。对于本地文件,您必须打开该工具并重新启动机器(同样,仅限第一次使用)。我相信打开此功能的命令是:

openfiles /local on

例如(适用于 Windows Vista x64):

openfiles /query | find "chrome.exe"

成功返回与 Chrome 关联的文件句柄。您还可以传入文件名来查看当前访问该文件的进程。


13
投票

我也在寻找解决方案,但遇到了一些问题。

  1. 不想使用外部应用程序
  2. 打开文件需要本地 ON 属性,这意味着必须将系统配置为在执行之前使用它。

经过广泛搜索,我发现了。

https://github.com/pldmgg/misc-powershell/blob/master/MyFunctions/PowerShellCore_Compatible/Get-FileLockProcess.ps1

感谢保罗·迪马吉奥

这似乎是纯粹的powershell和.net / C#


12
投票

您可以使用 SysinternalHandle 实用程序找到解决方案。

我必须(稍微)修改代码才能使用 PowerShell 2.0:

#/* http://jdhitsolutions.com/blog/powershell/3744/friday-fun-find-file-locking-process-with-powershell/ */
Function Get-LockingProcess {

    [cmdletbinding()]
    Param(
        [Parameter(Position=0, Mandatory=$True,
        HelpMessage="What is the path or filename? You can enter a partial name without wildcards")]
        [Alias("name")]
        [ValidateNotNullorEmpty()]
        [string]$Path
    )

    # Define the path to Handle.exe
    # //$Handle = "G:\Sysinternals\handle.exe"
    $Handle = "C:\tmp\handle.exe"

    # //[regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\b(\d+)\b)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)"
    # //[regex]$matchPattern = "(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\d+)\s+type:\s+(?<Type>\w+)\s+\w+:\s+(?<Path>.*)"
    # (?m) for multiline matching.
    # It must be . (not \.) for user group.
    [regex]$matchPattern = "(?m)^(?<Name>\w+\.\w+)\s+pid:\s+(?<PID>\d+)\s+type:\s+(?<Type>\w+)\s+(?<User>.+)\s+\w+:\s+(?<Path>.*)$"

    # skip processing banner
    $data = &$handle -u $path -nobanner
    # join output for multi-line matching
    $data = $data -join "`n"
    $MyMatches = $matchPattern.Matches( $data )

    # //if ($MyMatches.value) {
    if ($MyMatches.count) {

        $MyMatches | foreach {
            [pscustomobject]@{
                FullName = $_.groups["Name"].value
                Name = $_.groups["Name"].value.split(".")[0]
                ID = $_.groups["PID"].value
                Type = $_.groups["Type"].value
                User = $_.groups["User"].value.trim()
                Path = $_.groups["Path"].value
                toString = "pid: $($_.groups["PID"].value), user: $($_.groups["User"].value), image: $($_.groups["Name"].value)"
            } #hashtable
        } #foreach
    } #if data
    else {
        Write-Warning "No matching handles found"
    }
} #end function

示例:

PS C:\tmp> . .\Get-LockingProcess.ps1
PS C:\tmp> Get-LockingProcess C:\tmp\foo.txt

Name                           Value
----                           -----
ID                             2140
FullName                       WINWORD.EXE
toString                       pid: 2140, user: J17\Administrator, image: WINWORD.EXE
Path                           C:\tmp\foo.txt
Type                           File
User                           J17\Administrator
Name                           WINWORD

PS C:\tmp>

3
投票

您可以在handle.exe上找到您的路径。

我使用了 PowerShell,但您可以使用其他命令行工具。

具有管理权限:

handle.exe -a | Select-String "<INSERT_PATH_PART>" -context 0,100

向下搜索“Thread: ...”,您应该会看到使用您的路径的进程名称。


2
投票

在 PsGallery 中发布了一个 PowerShell 模块,用于发现并终止打开文件或文件夹句柄的进程。 它公开了以下功能:1) 查找锁定进程,2) 终止锁定进程。 该模块在第一次使用时会自动下载handle.exe。

Find-LockingProcess()
检索具有打开指定路径的文件句柄的进程信息。
示例:Find-LockingProcess -Path $Env:LOCALAPPDATA
示例:Find-LockingProcess -Path $Env:LOCALAPPDATA |获取进程

停止LockingProcess()
杀死所有打开指定路径的文件句柄的进程。
示例:Stop-LockingProcess -Path $Home\Documents

PsGallery 链接:https://www.powershellgallery.com/packages/LockingProcessKiller 要安装运行:
安装模块-名称LockingProcessKiller


0
投票

我遇到了这个问题并编写了一个完全独立的脚本,因为我不想依赖 SysInternals。在进行完整的递归复制之前,脚本将识别并终止锁定文件的任何进程。

https://github.com/Tikinsin/ForceCopy.ps1/blob/main/ForceCopy.ps1

这利用了 Zachery Fischer 和 Paul DiMaggio 的 Github 解决方案的答案。


0
投票

这是一个完全有效的解决方案,无需任何外部工具/依赖项。 它使用参数 FileInformationClass=47 的函数“NtQueryInformationFile”来获取 FILE_PROCESS_IDS_USING_FILE_INFORMATION 数据。

# script to get all PIDs of processes accessing/blocking a given file

cls
remove-variable * -ea 0
$errorActionPreference = 'stop'

Add-Type -TypeDefinition @"
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

public static class ProcessUtils {

    [StructLayout(LayoutKind.Sequential)]
    private struct IO_STATUS_BLOCK {
        public IntPtr Information;
        public IntPtr Status;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct FILE_PROCESS_IDS_USING_FILE_INFORMATION {
        public ulong NumberOfProcessIdsInList;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public ulong[] ProcessIdList;
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationFile(SafeFileHandle FileHandle, ref IO_STATUS_BLOCK IoStatusBlock,
        IntPtr FileInformation, uint Length, int FileInformationClass);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess,
        FileShare dwShareMode, IntPtr lpSecurityAttributes, FileMode dwCreationDisposition,
        FileAttributes dwFlagsAndAttributes, IntPtr hTemplateFile);

    public static ulong[] GetProcessesUsingFile(string filePath) {
        var processIds = new ulong[0];
        var ioStatusBlock = new IO_STATUS_BLOCK();
        var fileInfo = new FILE_PROCESS_IDS_USING_FILE_INFORMATION();

        using (var fileHandle = CreateFile(filePath, FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero)) {
            if (!fileHandle.IsInvalid) {
                var fileInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fileInfo));
                if (NtQueryInformationFile(fileHandle, ref ioStatusBlock, fileInfoPtr, (uint)Marshal.SizeOf(fileInfo), 47) == 0) {
                    fileInfo = Marshal.PtrToStructure<FILE_PROCESS_IDS_USING_FILE_INFORMATION>(fileInfoPtr);
                    if (fileInfo.NumberOfProcessIdsInList > 0) {
                        processIds = new ulong[fileInfo.NumberOfProcessIdsInList];
                        Array.Copy(fileInfo.ProcessIdList, processIds, (int)fileInfo.NumberOfProcessIdsInList);
                    }
                }
                Marshal.FreeHGlobal(fileInfoPtr);
            }
        }
        return processIds;
    }
}
"@

# Get the PIDs of all processes using a file:
[ProcessUtils]::GetProcessesUsingFile("C:\temp\test.txt")

-1
投票

我喜欢命令提示符 (CMD) 的功能,它也可以在 PowerShell 中使用:

tasklist /m <dllName>

请注意,您不能输入DLL文件的完整路径。光是名字就够了


-1
投票

我在锁定文件检测看到了一个很好的解决方案,它仅使用 PowerShell 和 .NET 框架类:

function TestFileLock {
    ## Attempts to open a file and trap the resulting error if the file is already open/locked
    param ([string]$filePath )
    $filelocked = $false
    $fileInfo = New-Object System.IO.FileInfo $filePath
    trap {
        Set-Variable -name filelocked -value $true -scope 1
        continue
    }
    $fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate,[System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
    if ($fileStream) {
        $fileStream.Close()
    }
    $obj = New-Object Object
    $obj | Add-Member Noteproperty FilePath -value $filePath
    $obj | Add-Member Noteproperty IsLocked -value $filelocked
    $obj
}

-6
投票

如果你将上面的函数稍微修改一下,它将返回 True 或 False (您需要以完全管理员权限执行) 例如用途:

PS> TestFileLock“c:\pagefile.sys”

function TestFileLock {
    ## Attempts to open a file and trap the resulting error if the file is already open/locked
    param ([string]$filePath )
    $filelocked = $false
    $fileInfo = New-Object System.IO.FileInfo $filePath
    trap {
        Set-Variable -name Filelocked -value $true -scope 1
        continue
    }
    $fileStream = $fileInfo.Open( [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None )
    if ($fileStream) {
        $fileStream.Close()
    }
    $filelocked
}
© www.soinside.com 2019 - 2024. All rights reserved.