PowerShell Add-Content Error The Process Cannot Access The File

Have you ever seen the PowerShell Add-Content error The Process Cannot Access The File?

I stumbled upon it a while ago when I wrote a simple PowerShell logging function. Originally the script used the Add-Content cmdlet to write and append output to a text file for logging purposes. This was fine for sporadic logging of infrequent events.

But when I used the same logging function on a script with heavy processing, which wrote thousands of log entries to a text file in quick succession, it couldn’t write fast enough and I eventually received the error:

The Process Cannot Access the File [Text File] because it is being used by another process.

The reason is because the Add-Content cmdlet requires a read-lock on the text file before it can write to it. The simple solution is to replace any instances of Add-Content with the Out-File cmdlet, which doesn’t require a read-lock. For example, change this:

Add-Content -Value "Some text" -Path "C:\Alkane\alkane.txt"

To this:

"Some text" | Out-File "C:\Alkane\alkane.txt" -Append

And your heavy processing script will write log entries much more efficiently.

 

Use PowerShell to Output Text to a File

There a various ways we can use PowerShell to output text to a file. And we’ll discuss a few options throughout this blog.

Use Set-Content and Add-Content to Output Text to a File

The first approach is to use the set-content cmdlet, which will add/replace text in a file even if the file doesn’t exist:

Set-Content -Path "C:\Alkane\alkane.txt" -Value "Alkane test"

But set-content can’t be used for appending text to a file, and hence it will keep overwriting all the contents. If we wanted to append text to a file we would need to use add-content instead:

Add-Content -Path "C:\Alkane\alkane.txt" -Value "Alkane test"

The disadvantage of both cmdlets is that they keep a handle open to the file whilst writing, meaning that other applications cannot read the file. A better alternative might be to use the out-file cmdlet instead.

Use Out-File to Output Text to a File

When using out-file to output text to a file, it will also create the text file if it doesn’t exist:

"Alkane test" | Out-File -FilePath "C:\Alkane\alkane.txt"

By default, out-file will overwrite all content in a file. But the beauty of out-file is that we can also append text to a file which can be useful for logging operations:

"Alkane test" | Out-File -FilePath "C:\Alkane\alkane.txt" -Append

And whilst out-file is running, other applications can also read the text file.

You will also notice that out-file can be used as part of the pipeline, meaning that we can output the results of another operation. For example, here we get all file names in a directory and write the file names to a text file using out-file :

Get-ChildItem "C:\AlkaneFiles" -File | Select -ExpandProperty Name | Out-File -FilePath "C:\Alkane\alkane.txt"

Set the Encoding when we Output Text to a File

Sometimes the encoding of a file is important, depending upon which application may utilise the text file.

I handily borrowed this very useful PowerShell function to check the encoding each time I created a text file like so:

function Get-Encoding
{
param
(
[Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[Alias('FullName')]
[string]
$Path
)
process
{
$bom = New-Object -TypeName System.Byte[](4)
$file = New-Object System.IO.FileStream($Path, 'Open', 'Read')
$null = $file.Read($bom,0,4)
$file.Close()
$file.Dispose()
$enc = [Text.Encoding]::ASCII
if ($bom[0] -eq 0x2b -and $bom[1] -eq 0x2f -and $bom[2] -eq 0x76) 
{ $enc =  [Text.Encoding]::UTF7 }
if ($bom[0] -eq 0xff -and $bom[1] -eq 0xfe) 
{ $enc =  [Text.Encoding]::Unicode }
if ($bom[0] -eq 0xfe -and $bom[1] -eq 0xff) 
{ $enc =  [Text.Encoding]::BigEndianUnicode }
if ($bom[0] -eq 0x00 -and $bom[1] -eq 0x00 -and $bom[2] -eq 0xfe -and $bom[3] -eq 0xff) 
{ $enc =  [Text.Encoding]::UTF32}
if ($bom[0] -eq 0xef -and $bom[1] -eq 0xbb -and $bom[2] -eq 0xbf) 
{ $enc =  [Text.Encoding]::UTF8}
[PSCustomObject]@{
Encoding = $enc
Path = $Path
}
}
}
Remove-Item -Path "C:\Alkane\alkane.txt" -Force
Add-Content -Path "C:\Alkane\alkane.txt" -Value "Alkane test"
Get-Encoding "C:\Alkane\alkane.txt"
Remove-Item -Path "C:\Alkane\alkane.txt" -Force
"Alkane test" | Out-File -FilePath "C:\Alkane\alkane.txt"
Get-Encoding "C:\Alkane\alkane.txt"

And the result was that add-content uses ASCII encoding by default, and out-file uses Unicode encoding by default. We can of course change the encoding type of out-file to match add-content by using the -encoding parameter like so:

"Alkane test" | Out-File -FilePath "C:\Alkane\alkane.txt" -Encoding ascii

We can use out-file to write a simple PowerShell logging function that prepends the date and time to each log message.