danml3
danml3

Reputation: 3

Powershell: exclusive lock for a file during multiple set-content and get-content operations

I am trying to write a script which runs on multiple client machines and writes to a single text file on a network share.

I want to ensure that only one machine can maniputale the file at any one time, whilst the other machines run a loop to check if the file is available.

the script runs this first:

Set-Content -Path $PathToHost -Value (get-content -Path $PathToHost | Select-String -Pattern "$HostName " -NotMatch) -ErrorAction Stop

Which removes some lines if they are matching the criteria. Then I want to append a new line with this:

Add-Content $PathToHost "$heartbeat$_" -ErrorAction Stop

The problem is that between the execution of those two commands another client has access to the file and begins to write to the file as well.

I have explored the solution here: Locking the file while writing in PowerShell

$PathToHost = "C:\file.txt"
$mode = "Open"
$access = "ReadWrite"
$share = "None"

$file = [System.IO.File]::Open($path, $mode, $access, $share)
$file.close()

Which can definitely lock the file, but I am not sure how to proceed to then read and write to the file.

Any help is much appreciated.

EDIT: Solution as below thanks to twinlakes' answer

$path = "C:\Users\daniel_mladenov\hostsTEST.txt"
$mode = "Open"
$access = "ReadWrite"
$share = "none"

$file = [System.IO.File]::Open($path, $mode, $access, $share)
$fileread = [System.IO.StreamReader]::new($file, [Text.Encoding]::UTF8)

# Counts number of lines in file
$imax=0
while ($fileread.ReadLine() -ne $null){

    $imax++
}
echo $imax

#resets read position to beginning 
$fileread.basestream.position = 0

#reads content of whole file and discards mathching lines
$content=@()
for ($i=0; $i -lt $imax; $i++){
    $ContentLine = $fileread.ReadLine()

        If($ContentLine -notmatch "$HostIP\s" -and $ContentLine -notmatch "$HostName\s"){
        $content += $ContentLine
        }
}
echo $content

#Writes remaining lines back to file
$filewrite = [System.IO.StreamWriter]::new($file)
$filewrite.basestream.position = 0
for ($i=0; $i -lt $content.length; $i++){
    $filewrite.WriteLine($content[$i])
}
$filewrite.WriteLine($heartbeat)
$filewrite.Flush()

$file.SetLength($file.Position) #trims file to the content which has been written, discarding any content past that point
$file.close() 

Upvotes: 0

Views: 1472

Answers (1)

twinlakes
twinlakes

Reputation: 10238

$file is a System.IO.FileStream object. You will need to call the write method on that object, which requires a byte array.

$string = # the string to write to the file
$bytes = [Text.Encoding]::UTF8.GetBytes($string)
$file.Write($bytes, 0, $bytes.Length)

Upvotes: 1

Related Questions