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 fil开发者_如何学编程e. 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.
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#.
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)
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.
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
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
精彩评论