Read File Without Updating LastAccess TimeStamp using PowerShell

In my last article I showed you how to write to a file covertly without updating the LastWrite and LastAccess time and even avoid being tripped by the FileSystemWatcher event.  This is a follow-up to that article that uses many of the same techniques, but instead allows you to read a file without updating the LastAccess timestamp.

https://learn-powershell.net/2013/02/14/write-to-an-existing-file-without-updating-lastwritetime-or-lastaccesstimestamps-using-powershell/

Simply retrieving the properties of an item or opening up a file will cause the LastAccessTime attribute to report the current time that the action took place.

image

It would be nice to be able to read a file without this being updated, now wouldn’t it? Of course it would! Instead of using a StreamWriter like in my previous article, I will instead use the StreamReader class to make the initial connection to a file, call the SetFileTime() method defined using a Win32 API against the handle that was created and then grab all of the contents of the file to read. Depending on what I want to do, I can figure out what type of encoding to read the file in or I can even output the content in bytes. Perfect if I want to get a binary file and output it to another file or append to an existing file.

The following example will show two attempts at reading a text file first without updating the LastAccess timestamp and then another attempt at reading the file without using the SetFileTime() method and watching the timestamp get updated.

$file = ".\NewFile.txt"
$signature = @"
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetFileTime(IntPtr hFile, ref long lpCreationTime, ref long lpLastAccessTime, ref long lpLastWriteTime);
"@
#endregion p/invoke signature

#region Create Win32 API object
[int64]$FILETIMEUNCHANGED = 0xFFFFFFFF
If (-Not $setFileTime) {
    $Global:setFileTime = Add-Type -Name FileTime -MemberDefinition $signature -PassThru
}

#region Check file name and convert to full path
If (-Not ([uri]$file).IsAbsoluteUri) {
    Write-Verbose ("{0} is not a full path, using current directory: {1}" -f $file,$pwd)
    $file = (Join-Path $pwd ($file -replace "\.\\",""))
}

Get-Item $file | Select Name,LastAccess*
#Open the file to read
$fileStream = New-Object System.IO.StreamReader -ArgumentList ([IO.File]::Open($file,"Open"))
#Tell file system operations to not updated LastAccess timestamp
$setFileTime::SetFileTime($fileStream.basestream.handle,[ref]$null,[ref]$FILETIMEUNCHANGED,[ref]$null)

#Read all contents of the file
$fileStream.ReadToEnd()
#Close the file stream
$fileStream.BaseStream.Close()
Get-Item $file | Select Name,LastAccess*
Start-Sleep -Seconds 5
Get-Content $file
Get-Item $file | Select Name,LastAccess*

image

It’s a lot of stuff to go through, but you can see that I use a Stream object with [IO.File]::Open() to create the initial stream to the text file that is then supplied as an argument to the System.IO.StreamReader constructor. This is done because if I attempt to connect to the file just using the StreamReader constructor, it will actually cause the SetFileTime() method to not work properly, thus causing the LastAccess timestamp to update (which is not what I want to happen). The rest of this is pretty straightforward. I call the ReadToEnd() method to read all of the lines in the text file and then close the StreamReader via the BaseStream.Close() method. I wait 5 seconds and then read the file by using Get-Content and watch as the timestamp is updated this time.

As with my last article on writing to a file without updating the timestamps, this example wouldn’t be complete without a little function that makes the job much easier as well as allowing you to read a file in bytes, if needed.

Get-FileContent

Using the techniques shown earlier, I put this into a nice function called Get-FileContent that you can use to read the content of a file in a specified encoding which includes bytes. The following examples will show the function in action as it reads a file without updating the LastAccess timestamp.

Reading a file

Get-FileContent -File .\NewFile.txt

image

Reading a file in bytes

Get-FileContent -File .\NewFile.txt -Encoding Byte

image

Copying a file in bytes to another existing file using Write-File

Get-FileContent -File .\HonoraryScriptingGuy.png -Encoding Byte | 
Write-File -File .\TEST.png -Encoding byte

image

Like nothing ever happened!

The Get-FileContent function is available to download from the link below in the Script Repository. As always, if you find a use for this, I would love to hear about it!

Download Get-FileContent

http://gallery.technet.microsoft.com/scriptcenter/Read-file-without-updating-15eb9cb8

This entry was posted in powershell, scripts and tagged , , , . Bookmark the permalink.

Leave a comment