locked
How to check to see if a file is open/locked before trying to copy it RRS feed

  • Question

  • I am trying to copy a set of files and folders fomr one location ot another. It works ok, but if a file is open it generates an error. Is there a way to check to see if a file is open before trying to cpoy it? I mean, can I use it in conjunction with the copy-item cmdlet? My code below

    try
    {
    	copy-item -Path $SourceHomeDirectoryPath -Destination $TargetServerHomePathShort -Recurse -ErrorAction SilentlyContinue
    }
    catch
    {
    	$Error
    	$MyError = $Error
    	$arrayErrors += $MyError
    }

     

    Wednesday, August 10, 2011 6:44 PM

Answers

  • The function as-is doesn't preserve folder structure because Test-FileLock only passes through files, not folders. But it wouldn't work correctly even if folders passed through, because Copy-Item isn't capable of reproducing a folder structure based on the deepest part of the path -- and the deepest level is what it receives first.

    So, I created a small helper function that can take care of that problem. There might be a prettier way to do it, but give this function a spin. Example usage:

    Get-ChildItem "C:\test" -recurse | Test-FileLock -passthru | Copy-FilePreserveRelativePath -Destination "C:\test3" -OriginPath "C:\test"

     

    function Copy-FilePreserveRelativePath
    {
      param (
    		[parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
    		[Alias("FullName")]
    		[string]$Path,
    		[string]$Destination,
    		[string]$OriginPath,
    		[switch]$PassThru
    	)
    
    	begin {}
    	
    	Process
    	{
    		$oFile = Get-Item $Path
    		$TargetPath = $Destination + ($Path.SubString($OriginPath.Length, $Path.Length - $OriginPath.Length - $oFile.Name.Length))
    		if ((Test-Path $TargetPath) -eq $false)
    		{
    			New-Item -Path $TargetPath -ItemType Directory -Force
    		}
    		Copy-Item -Path $Path -Destination $TargetPath -Force
    	}
    
    	end {}
    
    }
    


    Thursday, August 11, 2011 1:59 PM

All replies

  • Here's a function I use for this purpose. It also tests for the file's existence, but you can easily remove that part if you're not concerned about that.

    function Test-FileLock {
    
      param (
    		[parameter(Mandatory=$true)]
    		[string]$Path
    	)
    
      $oFile = New-Object System.IO.FileInfo $Path
    
      if ((Test-Path -Path $Path) -eq $false)
      {
      	$false
      	return
      }
      
      try
      {
    	  $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
    	  if ($oStream)
    	  {
    	    $oStream.Close()
    	  }
    	  $false
      }
      catch
      {
      	# file is locked by a process.
      	$true
      }
    }


    Wednesday, August 10, 2011 6:50 PM
  • I saw the same function a few minutes ago. I like it, it does what I want, but if I use that let me ask another question. How can I pass all my files and folders to that function to check it before copying it? The only thing I can think of is to do a DIR and pipe the reults of that to a foreach function, and use that function on each file. That could get time consuming too if it is checking each file, since I could be dealing with hundreds and thousands of files (users home and roaming profile directories). With the addition of the "-ErrorActoin SilentlyContinue", should I still be generating errors or should it march on like nothing is wrong, but still logging the errors of course. If so, I am ok with that because I can then add that information to my email report at the end of the process.
    Wednesday, August 10, 2011 6:59 PM
  • Here's a quick and dirty edit of the function that's capable of passing through unlocked files so you can then pipe Test-FileLock to Copy-Item. Usage would be something like:

    gci "C:\Folder1" -recurse | Test-FileLock -passthru | Copy-Item -Destination "C:\Folder2"

    (EDIT: Keep in mind that this doesn't pass through the files that are locked, so you'll want some other way of tracking which files weren't copied!)

     

    function Test-FileLock {
    
      param (
    		[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    		[Alias("FullName")]
    		[string]$Path,
    		[switch]$PassThru
    	)
    
    	begin {}
    	
    	Process
    	{
    	  $oFile = New-Object System.IO.FileInfo $Path
    	
    	  if ((Test-Path -Path $Path) -eq $false)
    	  {
    		  if ($PassThru.IsPresent -eq $false)
    		  {
    		  	$false
    		  }
    	  	return
    	  }
    	  
    	  try
    	  {
    		  $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
    		  if ($oStream)
    		  {
    		    $oStream.Close()
    		  }
    		  if ($PassThru.IsPresent -eq $false)
    		  {
    		  	$false
    		  }
    		  else
    		  {
    		  	Get-Item $Path
    		  }
    	  }
    	  catch
    	  {
    	  	# file is locked by a process.
    		  if ($PassThru.IsPresent -eq $false)
    		  {
    		  	$true
    		  }
    	  }
    	}
    	end {}
    }


    Wednesday, August 10, 2011 7:18 PM
  • the recursion isn't processing subfolders and files. That should still be working, right?

    cls
    function Test-FileLock 
    {
    	param
    	(
    		[parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)]
    		[Alias("FullName")]
    		[string]$Path,
    		[switch]$PassThru
    	)
    	begin {}
    	Process
    	{
    	 $oFile = New-Object System.IO.FileInfo $Path
    
    	 if ((Test-Path -Path $Path) -eq $false)
    	 {
    		 if ($PassThru.IsPresent -eq $false)
    		 {
    		 	$false
    		 }
    	 	return
    	 }
    		 
    	 try
    	 {
    		 $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
    		 if ($oStream)
    		 {
    		  $oStream.Close()
    		 }
    		 if ($PassThru.IsPresent -eq $false)
    		 {
    		 	$false
    		 }
    		 else
    		 {
    		 	Get-Item $Path
    		 }
    	 }
    	 catch
    	 {
    	 	# file is locked by a process.
    		 if ($PassThru.IsPresent -eq $false)
    		 {
    		 	$true
    		 }
    	 }
    	}
    	end {}
    }
    Get-ChildItem "C:\test" -recurse | Test-FileLock -passthru | Copy-Item -Destination "C:\test3" -Recurse
    
    

     

     

    Thursday, August 11, 2011 12:48 PM
  • The function as-is doesn't preserve folder structure because Test-FileLock only passes through files, not folders. But it wouldn't work correctly even if folders passed through, because Copy-Item isn't capable of reproducing a folder structure based on the deepest part of the path -- and the deepest level is what it receives first.

    So, I created a small helper function that can take care of that problem. There might be a prettier way to do it, but give this function a spin. Example usage:

    Get-ChildItem "C:\test" -recurse | Test-FileLock -passthru | Copy-FilePreserveRelativePath -Destination "C:\test3" -OriginPath "C:\test"

     

    function Copy-FilePreserveRelativePath
    {
      param (
    		[parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
    		[Alias("FullName")]
    		[string]$Path,
    		[string]$Destination,
    		[string]$OriginPath,
    		[switch]$PassThru
    	)
    
    	begin {}
    	
    	Process
    	{
    		$oFile = Get-Item $Path
    		$TargetPath = $Destination + ($Path.SubString($OriginPath.Length, $Path.Length - $OriginPath.Length - $oFile.Name.Length))
    		if ((Test-Path $TargetPath) -eq $false)
    		{
    			New-Item -Path $TargetPath -ItemType Directory -Force
    		}
    		Copy-Item -Path $Path -Destination $TargetPath -Force
    	}
    
    	end {}
    
    }
    


    Thursday, August 11, 2011 1:59 PM
  • I know this post is reallly old. I am trying to filter out files that are opened or locked.

    Please let me know what needs to be changed in your function

    I am newbie to PowerShell, Thank you in advance

    Wednesday, January 16, 2013 9:43 PM
  • The easiest way to filter out those files that cannot be copied because they are locked is to try to copy them, and ignore the errors that result. There may be a way to determine a file's locked/open status in advance. But once you find a file is available for copying there is no guarantee that some process will not open it before your script attempts to copy it.

    As a scripting newbie, you might be improperly thinking of reportable error conditions as that negative kind of error called a MISTAKE. They are no such thing, as reportable error conditions can be extremely useful, both in scripting and in real life. Files are locked while open not to create errors, but to protect the consistency of the file.

    Consider this real-life scenario:

    You have an urgent need to use the bathroom while at a party in a friend's house, but find the door closed on the only bathroom. Which of the following methods are you most likely to use to find out whether or not the door is locked:

    1. try to open the door, and say "sorry" if you find it is locked, otherwise walk in, remembering to close and lock the door behind you.
    2. ask loudly "is anyone in the bathroom", and say "sorry" if someone answers that they are there, otherwise walk in, remembering to close and lock the door behind you.
    3. find out if the door is locked or if someone is in the bathroom by trying to determine if anyone is missing from the party. You might need to check the bedrooms as well if anyone is missing, but that might only compound the embarrassment of the situation. Once you have determined that the bathroom is probably in use cross your legs. If you have deduced that it is free for your use, try to open the door. But wait, haven't we considered that technique somewhere earlier in the process?

    A caveat here is that if a failed attempt to do something takes significantly longer than testing the likelihood of failure, you might want to find an effective way to do the testing first.

    Another caveat is that if you walk in on someone who forgot to lock the door, that could be even more embarrassing than having forced them to say: "yes, I'm using the bathroom". On the other hand you will probably correct that person of the habit of not locking the bathroom door behind them.

    My apologies for anyone out there offended by "toilet humour"!


    Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.

    Wednesday, January 16, 2013 10:55 PM
  • try {
       [IO.File]::OpenWrite($file).close();
    }catch {
       $locked = 1;
    };
    Wednesday, October 8, 2014 8:32 AM