简体   繁体   中英

PowerShell: Set-Content having issues with “file already in use”

I'm working on a PowerShell script that finds all the files with PATTERN within a given DIRECTORY, prints out the relevant lines of the document with the PATTERN highlighted, and then replaces the PATTERN with a provided REPLACE word, then saves the file back. So it actually edits the file.

Except I can't get it to alter the file, because Windows complains about the file already being open. I tried several methods to solve this, but keep running into the issue. Perhaps someone can help:

param(
    [string] $pattern = ""
    ,[string] $replace = ""
    ,[string] $directory ="."
    ,[switch] $recurse = $false
    ,[switch] $caseSensitive = $false)

if($pattern -eq $null -or $pattern -eq "")
{
    Write-Error "Please provide a search pattern." ; return
}

if($directory -eq $null -or $directory -eq "")
{
    Write-Error "Please provide a directory." ; return
}

if($replace -eq $null -or $replace -eq "")
{
    Write-Error "Please provide a string to replace." ; return
}

$regexPattern = $pattern
if($caseSensitive -eq $false) { $regexPattern = "(?i)$regexPattern" }
$regex = New-Object System.Text.RegularExpressions.Regex $regexPattern

function Write-HostAndHighlightPattern([string] $inputText)
{
    $index = 0
    $length = $inputText.Length
    while($index -lt $length)
    {
        $match = $regex.Match($inputText, $index)
        if($match.Success -and $match.Length -gt 0)
        {
            Write-Host $inputText.SubString($index, $match.Index) -nonewline
            Write-Host $match.Value.ToString() -ForegroundColor Red -nonewline
            $index = $match.Index + $match.Length
        }
        else
        {
            Write-Host $inputText.SubString($index) -nonewline
            $index = $inputText.Length
        }
    }
}

Get-ChildItem $directory -recurse:$recurse |
    Select-String -caseSensitive:$caseSensitive -pattern:$pattern |    
    foreach {
        $file = ($directory + $_.FileName)
        Write-Host "$($_.FileName)($($_.LineNumber)): " -nonewline
        Write-HostAndHighlightPattern $_.Line
        %{ Set-Content $file ((Get-Content $file) -replace ([Regex]::Escape("[$pattern]")),"[$replace]")}
        Write-Host "`n"
        Write-Host "Processed: $($file)"
    }

The issue is located within the final block of code, right at the Get-ChildItem call. Of course, some of the code in that block is now a bit mangled due to me trying to fix the problem then stopping, but keep in mind the intent of that part of the script. I want to get the content, replace the words, then save the altered text back to the file I got it from.

Any help at all would be greatly appreciated.

Removed my previous answer, replacing it with this:

Get-ChildItem $directory -recurse:$recurse
foreach {        
    $file = ($directory + $_.FileName)

    (Get-Content $file) | Foreach-object {
        $_ -replace ([Regex]::Escape("[$pattern]")),"[$replace]")
    } | Set-Content $file
}

Note:

  • The parentheses around Get-Content to ensure the file is slurped in one go (and therefore closed).
  • The piping to subsequent commands rather than inlining.
  • Some of your commands have been removed to ensure it's a simple test.

Just a suggestion but you might try looking at the documentation for the parameters code block. There is a more efficient way to ensure that a parameter is entered if you require it and to throw an error message if the user doesn't.

About_throw: http://technet.microsoft.com/en-us/library/dd819510.aspx About_functions_advanced_parameters: http://technet.microsoft.com/en-us/library/dd347600.aspx

And then about using Write-Host all the time: http://powershell.com/cs/blogs/donjones/archive/2012/04/06/2012-scripting-games-commentary-stop-using-write-host.aspx

Alright, I finally sat down and just typed everything sequentially in PowerShell, then used that to make my script.

It was actually really simple;

$items = Get-ChildItem $directory -recurse:$recurse
$items |
foreach {
    $file = $_.FullName
    $content = get-content $file
    $newContent = $content -replace $pattern, $replace
    Set-Content $file $newcontent
}

Thanks for all your help guys.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM