I am writing a batch script in which I am trying to replace a value in a prop file. I am using PowerShell for the replacement code as I couldn't find any comparable way to do in batch script.
powershell -Command "(gc %PROPFILEPATH%) -replace '%FTPoldfilepath%', '%FTPnewfile%' | Set-Content %PROPFILEPATH%"
The variables %PROPFILEPATH%, %FTPoldfilepath% and %FTPnewfile% contain double backslashes (Eg: C:\\testing\\feed)
I realize that backslashes need to be escaped, can anyone guide me how to implement the escape function here.
Use double backslashes. Does not hurt if they come through doubled, or even tripled.
You will need to use $ENV:PROFILEPATH, $ENV:FTPoldfilepath, and $ENV:FTPnewpath in place of %PROPFILEPATH%, '%FTPoldfilepath%', and '%FTPnewfile%'
If your goal is to load the current path, replace the old path with the new one and save the new path, consider doing so with a full script instead of a single command:
$oldftppath = 'c:\some\path'
$newftppath = 'c:\new\path'
$newpath = $ENV:PROFILEPATH.replace($oldftppath,$newftppath)
But then it gets tricky. If you need a persisent environment variable, you need to use .NET framework to set it. https://technet.microsoft.com/en-us/library/ff730964.aspx
[Environment]::SetEnvironmentVariable("TestVariable", "Test value.", "User")
So, using this syntax:
[Environment]::SetEnvironmentVariable("PROFILEPATH", "$newpath", "User")
Or it could be "machine" for the context.
For one thing, as #Xalorous mentioned, you'll have to use PowerShell syntax for accessing environment variables:
powershell -Command "(gc $env:PROPFILEPATH) -replace $env:FTPoldfilepath, $env:FTPnewfile | Set-Content $env:PROPFILEPATH"
Also, only the search string needs to be escaped, not the replacement string. You can use the Escape() method of the regex class for that:
powershell -Command "(gc $env:PROPFILEPATH) -replace [regex]::Escape($env:FTPoldfilepath), $env:FTPnewfile | Set-Content $env:PROPFILEPATH"
Escaping is required here, because the -replace operator treats the search string as a regular expression.
However, since you apparently want just a simple string replacement, not a regular expression match, you could also use the Replace() method of the source string:
powershell -Command "(gc $env:PROPFILEPATH) | % { $_.Replace($env:FTPoldfilepath, $env:FTPnewfile) } | Set-Content $env:PROPFILEPATH"
As a side note, since you're using PowerShell anyway, you should seriously consider writing the whole script in PowerShell. It usually makes things a lot easier.
Related
I am trying to extract the Get-Help comment headers from a PowerShell script...using PowerShell. The file I'm reading looks something like this:
<#
.SYNOPSIS
Synopsis goes here.
It could span multiple lines.
Like this.
.DESCRIPTION
A description.
It could also span multiple lines.
.PARAMETER MyParam
Purpose of MyParam
.PARAMETER MySecondParam
Purpose of MySecondParam.
Notice that this section also starts with '.PARAMETER'.
This one should not be captured.
...and many many more lines like this...
#>
# Rest of the script...
I would like to get all the text below .DESCRIPTION, up to the first instance of .PARAMETER. So the desired output would be:
A description.
It could also span multiple lines.
Here's what I've tried:
$script = Get-Content -Path "C:\path\to\the\script.ps1" -Raw
$pattern = '\.DESCRIPTION(.*?)\.PARAMETER'
$description = $script | Select-String -Pattern $pattern
Write-Host $description
When I run that, $description is empty. If I change $pattern to .*, I get the entire contents of the file, as expected; So there must be something wrong with my RegEx pattern, but I can't seem to figure it out.
Any ideas?
(get-help get-date).description
The `Get-Date` cmdlet gets a DateTime object that represents the current date
or a date that you specify. It can format the date and time in several Windows
and UNIX formats. You can use `Get-Date` to generate a date or time character
string, and then send the string to other cmdlets or programs.
(get-help .\script.ps1).description
the Select-String cmdlet works on entire strings and you have given it ONE string. [grin]
so, instead of fighting with that, i went with the -match operator. the following presumes you have loaded the entire file into $InStuff as one multiline string with -Raw.
the (?ms) stuff is two regex flags - multiline & singleline.
$InStuff -match '(?ms)(DESCRIPTION.*?)\.PARAMETER'
$Matches.1
output ...
DESCRIPTION
A description.
It could also span multiple lines.
note that there is a blank line at the end. you likely will want to trim that away.
In the words of #Mathias R. Jessen:
Don't use regex to parse PowerShell code in PowerShell
Use the PowerShell parser instead!
So, let's use PowerShell to parse PowerShell:
$ScriptFile = "C:\path\to\the\script.ps1"
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseFile($ScriptFile, [ref]$null, [ref]$null)
$ScriptAST.GetHelpContent().Description
We use the [System.Management.Automation.Language.Parser]::ParseFile() to parse our file and ouput an Abstract Syntax Tree (AST).
Once we have the Abstract Syntax Tree, we can then use the GetHelpContent() method (exactly what Get-Help uses) to get our parsed help content.
Since we are only interested in the Description portion, we can simply access it directly with .GetHelpContent().Description
I have a text file that contains:
#define VERSION "0.1.2"
I need to replace that version number from a running batch file.
set NEW_VERSION="0.2.0"
powershell -Command "(gc BBB.iss) -replace '#define VERSION ', '#define VERSION %NEW_VERSION% ' | Out-File BBB.iss"
I know that my match pattern is not correct. I need to select the entire line including the "0.2.0", but I can't figure out how to escape all that because it's all enclosed in double quotes so it runs in a batch file.
I'm guessing that [0-9].[0-9].[0-9] will match the actual old version number, but what about the quotes?
but what about the quotes?
When calling PowerShell's CLI from cmd.exe (a batch file) with powershell -command "....", use \" to pass embedded ".
(This may be surprising, given that PowerShell-internally you typically use `" or "" inside "...", but it is the safe choice from the outside.[1].)
Note:
While \" works robustly on the PowerShell side, it can situationally break cmd.exe's parsing. In that case, use "^"" (sic) with powershell.exe (Windows PowerShell), and "" with pwsh.exe (PowerShell (Core) 7+), inside overall "..." quoting. See this answer for details.
Here's an approach that matches and replaces everything between "..." after #define VERSION :
:: Define the new version *without* double quotes
set NEW_VERSION=0.2.0
powershell -Command "(gc BBB.iss) -replace '(?<=#define VERSION\s+\").+?(?=\")', '%NEW_VERSION%' | Set-Content -Encoding ascii BBB.iss"
Note that using Out-File (as used in the question) to rewrite the file creates a UTF-16LE ("Unicode") encoded file, which may be undesired; use Set-Content -Encoding ... to control the output encoding. The above command uses Set-Content -Encoding ascii as an example.
Also note that rewriting an existing file this way (read existing content into memory, write modified content back) bears the slight risk of data loss, if writing the file gets interrupted.
(?<=#define VERSION\s+\") is a look-behind assertion ((?<=...)) that matches literal #define VERSION followed by at least one space or tab (\s+) and a literal "
Note how the " is escaped as \", which - surprisingly - is how you need to escape literal " chars. when you pass a command to PowerShell from cmd.exe (a batch file).[1]
.+? then non-greedily (?) matches one or more (+) characters (.)...
...until the closing " (escaped as \") is found via (?=\"), a look-ahead assertion
((?<=...))
The net effect is that only the characters between "..." are matched - i.e., the mere version number - which then allows replacing it with just '%NEW_VERSION%', the new version number.
A simpler alternative, if all that is needed is to replace the 1st line, without needing to preserve specific information from it:
powershell -nop -Command "#('#define VERSION \"%NEW_VERSION%\"') + (gc BBB.iss | Select -Skip 1) | Set-Content -Encoding ascii BBB.iss"
The command simply creates an array (#(...)) of output lines from the new 1st line and (+) all but the 1st line from the existing file (gc ... | Select-Object -Skip 1) and writes that back to the file.
[1] When calling from cmd.exe, escaping an embedded " as "" sometimes , but not always works (try
powershell -Command "'Nat ""King"" Cole'").
Instead, \"-escaping is the safe choice.
`", which is the typical PowerShell-internal way to escape " inside "...", never works when calling from cmd.exe.
You can try this,
powershell -Command "(gc BBB.iss) -replace '(?m)^\s*#define VERSION .*$', '#define VERSION %NEW_VERSION% ' | Out-File BBB.iss"
If you want double quotes left,
powershell -Command "(gc BBB.iss) -replace '(?m)^\s*#define VERSION .*$', '#define VERSION "%NEW_VERSION%"' | Out-File BBB.iss"
I have some config files structured like:
PATH_KEY=C:\\dir\\project
foo=bar
I want to write a small script that replaces a certain key with current folder.
So basically I'm trying to replace "PATH_KEY=..." with "PATH_KEY=$PSScriptRoot"
My code so far:
$cfgs = Get-Childitem $PSScriptRoot -Filter *name*.cfg
foreach ($cfg in $cfgs)
{
( Get-Content $cfg) -replace 'PATH_KEY=.*?\n','PATH_KEY=$PSScriptRoot' | Set-Content $cfg
}
But the regular expression to take everything till end of line is not working.
Any help is appreciated!
You can use
'(?m)^PATH_KEY=.*'
or even
'PATH_KEY=.*'
Note that $ in the replacement should be doubled to denote a single $, but it is not a problem unless there is a digit after it.
See the demo:
For one of my daily manually find and replace task I want to create a powershell script to find and replace some network location and replace with new one.
Like :-
To find : \\Test\test
Replace with \\Test1\Test1
I am able to replace text without any special characters but for above I am getting regular expression error.
And also after replacing above string I want to save result in a text file which logs records of files which have been updated.
Seeing your example in the comment, just needs an Add-Content snip to complete it. This is how I would do it.
$c = '\\filedep\iservershare\DCSVWA'
$c = $c -replace [regex]::Escape('\\filedep\iservershare\DCSVWA'),('\\EGSISFS01\VolksWagon\DCSVWA')
Add-Content C:\Output.txt $c
I've run it twice and the file resulted in.
\\EGSISFS01\VolksWagon\DCSVWA
\\EGSISFS01\VolksWagon\DCSVWA
Alrighty..
So I am editing an AutoIt script that has a lot of unused functions in it. The original author saw fit to add all the functions from his/her includes files.
At first I tried to use the tools within AutoIt/SciTe to remove unused functions however for some freakish reason this rendered the script/compiled file useless. So now I am thinking it would be best to write a function remover.
Here is what I have so far:
Search for lines with "Func _" count number of times that function appears in the file. If 1 time then Select String
$FileName=".\FILENAME.au3"
$File=Get-Content $FileName
$Funcs=$File|Select-String "Func _"
foreach ($Func in $Funcs) {
$FuncName=$Func.ToString().Split('( ')[1]
$Count=($File|Select-String $FuncName | Measure-Object).Count
if ($count -eq 1) {
$File|Select-String "Func _" $FuncName
}
}
What I would like to do is remove the function, likely with regex. So something like:
REMOVE "Func _"$func * "EndFunc"
The trouble has been that this is a search that spans multiple lines, from Func _NAMEOFFUCTION to EndFunc. Its unclear to me if regex in PowerShell can even do this. Not all regex implementations seem to be able to span a search across lines. Is regex even the answer? I don't know.
When you use Get-Content in PowerShell 1.0 or 2.0 you can only get back an array of strings - one for each line. This isn't going to work when you need a regex to span multiple lines. Use this approach to read the file as a single string:
$FileContent = [io.file]::ReadAllText($FileName)
If you are on PowerShell V3 you can use the -Raw parameter to read the file as a single string:
$FileContent = Get-Content $FileName -Raw
Then when you use Select-String you will need to modify the regex to enable singleline s (and probably multiline m) mode e.g.:
$FileContent | Select-String "(?smi)$FuncName" -AllMatches
Note the i is there to be case-insensitive. Use the -AllMatches parameter to match multiple function definitions within a file.
Here's a regex that should match an AutoIt function definition. It assumes the Func and EndFunc keywords are always placed at the beginning of a line and that they're case sensitive. The function name is captured in the group named FuncName (in C# you would access it via Groups["FuncName"]);
"(?m)^Func\s+\b(?<FuncName>_\w+\b).*\n(?:(?!EndFunc\b).*\n)*EndFunc.*\n?"
For the function names alone you can use "\b_\w+\b" or maybe "\b_[A-Za-z]+\b"; I don't know how strict you need to be). Having almost zero experience with PowerShell, I would probably use [regex]::Matches and [regex]::Replace to do the work. I don't know if PS offers a better way.
I'm assuming you've read the whole file into a string as #Keith suggested, not line by line as you were doing originally.