4

I am trying to replace a chunk of text in a file using PowerShell. For instance, I have a .sql file and I know the exact chunk of an SQL script that needs to be replaced in that particular file. After reading through some of the PowerShell replace examples it looks like PowerShell returns the content of the file in an array (each line representing one entry in the array).

For example:

GO
:on error exit
GO
IF (DB_ID(N'$(DatabaseName)') IS NOT NULL)
BEGIN
    ALTER DATABASE [$(DatabaseName)]
    SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
    DROP DATABASE [$(DatabaseName)];
END

GO
PRINT N'Creating $(DatabaseName)...'
GO
CREATE DATABASE [$(DatabaseName)] COLLATE SQL_Latin1_General_CP1_CI_AS
GO

USE [$(DatabaseName)]
.......
.........
..... MORE SQL SCRIPT

I want to replace the text till USE [$(DatabaseName)] in the above file.

5 Answers 5

10

Here's how I would go about this. First, when you need to get the entire contents of a file for purposes like replacing text over multiple lines, don't use Get-Content. Instead, use [IO.file]::ReadAllText().

Then use the -replace operator, for example:

[IO.File]::ReadAllText("$pwd\foo.sql") -replace `
   '(?s).*?(USE \[\$\(DatabaseName\)\].*)$',"foo`n`$1" > foo.sql

Here I'm replacing the beginning text with "foo". Also note that to get the regex used by -replace to match across newlines I prefix the regex with (?s) - single-line mode.

Mjolinor brings up a good point, in the case where the replacement text has characters that could be interpreted as regex special variables e.g. $1, $2, etc. While you can use [regex]::escape() to escape the regex there's still PowerShell code that will interpret $<something> as a variable or start of a sub-expression. In this case, it's pretty simple to work around by just capturing the part you want to keep with the -replace operator and then prepend the new text in a second step e.g.:

$keep = [IO.File]::ReadAllText("$pwd\foo.sql") -replace `
            '(?s).*?(USE \[\$\(DatabaseName\)\].*)$','$1'
$newText + $keep > foo.sql

Note that in the replace in this case, I use single quotes around $1 which prevents PowerShell from interpreting any special PowerShell characters. It's kind of like a verbatim string in C#.

Sign up to request clarification or add additional context in comments.

4 Comments

Depending on what that chunk of text is getting replaced with, that could get pretty ugly.(IMHO)
In that case, don't bother to try to insert the new text in the replace op - just grab the trailing part to keep. Then separately prepend the text to the trailing part.
Or put the new text in a here-string, and use that as your replace argument.
Saw your reply after posting mine. Pretty much the same. So you get the green tick :)
1

I ended up doing the below. I find it easier to understand.

$fileContent = [System.Io.File]::ReadAllText($filePath)
$toReplace = [System.Io.File]::ReadAllText($anotherPath)
$afterReplace = $fileContent.Replace($toReplace,$newContent)
[System.Io.Directory]::WriteAllText($filePath,$afterReplace)

Comments

1

In Get-Content, you can set the ReadCount to 0. This will create an array of one item. You can access it via $contents[0]:

$contents = Get-Content file.sql -ReadCount 0
$contents[0].Replace($oldstring, $newString)

You can also pipe the whole thing to Out-String, but I think the first option would be a bit faster.

2 Comments

Actually specifying a ReadCount of 0 sends the entire content as one array (one line per index) down the pipeline.
Good to know - i thought -readcount 0 translated down to IO.FIle.ReadAllText(), Thanks Keith, good to know.
0

Assuming your replacement text is in newscript.txt:

 $new = Get-Content newscript.txt

 Get-Content file.sql | % {
     if ($_ -eq 'USE [$(DatabaseName)]') {
         $test = $true
     }
     if ($test) {
         $new += $_
     }
 }

 $new | Out-File newfile.sql

Comments

0

After a script solution, a command line solution. Assuming your replacement text is in newscript.txt:

# Read the file $file into an array of lines
PS > $file = Get-Content 'C:\temp\sql.txt'

# Retrieve the line beginning the replacement
PS > $begin  =  $file | Select-String -Pattern 'USE \[\$\(DatabaseName\)\]' 
PS > $begin.linenumber
17

# Selecting (in a new array) the last computed lines
PS > $secondPart = $file | Select-Object -Last ($file.count - $begin.linenumber +1) 
PS > $secondPart
USE [$(DatabaseName)]
.......
.........
..... MORE SQL SCRIPT

# Creating the new file
PS > $new = Get-Content newscript.txt
PS > ($new + $secondPart) | Out-File newfile.sql

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.