Change line in JS file in a post-build event (Visual Studio) - visual-studio

I have a JS file that has a path field that needs to be updated on deployment. I don't see that post build events allow the replacement of text or a full line by themselves, so I created a batch file to do so. However, I'm getting some odd errors.
Trying to replace the strings using a batch with powershell throws errors with the single quotes being escaped:
powershell -Command "(gc Utility.js) -replace 'var applicationRoot = \'\/WebApplication;\'', 'var applicationRoot = \'\';' | Out-File -encoding ASCII myFile.txt"
Taking out the single quotes to see if it will at least run gives an unidentified error:
powershell -Command "(gc Utility.js) -replace 'var applicationRoot = \/WebApplication;', 'var applicationRoot = ;' | Out-File -encoding ASCII myFile.txt"
I'm calling it as a post build event:
call "$(SolutionDir)Scripts\ChangeJSFile.bat"
Any ideas?

Personally, I'd do this a bit differently.
Assume you have a Powershell script named _PostBuild.ps1 in the root of the project where Utility.js lives. That .ps1 script has the following contents:
param(
[string] $pathToJsFile,
[string] $toReplace = "var applicationRoot = '/WebApplication';",
[string] $replaceWith = 'var applicationRoot = ;'
)
$fileContent = Get-Content -Path $pathToJsFile
$newContent = $fileContent.Replace($toReplace, $replaceWith)
Set-Content -Path $pathToJsFile -Value $newContent
Then, assume that the aforementioned project has a post-build actions such as the below:
Powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command "$(ProjectDir)\_PostBuild.ps1" -pathToJsFile "$(ProjectDir)\Utility.js"
That will allow you to perform a string replace on Utility.js on successful build.
Obviously, you may want to make some edits to the post-build action based on your project structure.

It was a slight nightmare but I think I have a solution for you. Basically the only issue was where you were escaping.
powershell -Command "(gc Utility.js) -replace \"var applicationRoot = '/WebApplication;'\", \"var applicationRoot = ';'\" | Out-File -Encoding ascii myFile.txt"

The problematic part is passing the innermost single quotes literally to PowerShell. Literal quotes inside of quotes of the same type will need some kind of escape. You have that case here since your -replace components are surrounded by single quotes but are intending to match literal single quotes. In those cases, you can simply use two single quotes, which escapes one single quote.
powershell -Command "(gc Utility.js) -replace 'var applicationRoot = ''/WebApplication;''', 'var applicationRoot = '''';' | Out-File -encoding ASCII myFile.txt"

Related

Escape double quotes in powershell -Command

Due to some limitations, I have to execute the Power Shell command from Windows Command Prompt
powershell -Command "(gc C:\my_configuration.conf) -replace 'INSERT_URL', \`"https://mytestserver/WW48.2'22/testing.bin\`" | Out-File C:\my_configuration.conf"
However, I am constantly getting the ParserError like below
The string is missing the terminator: '.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
How should I properly wrap the URL string with double quotes? Thanks for answering.
Remove the ` before ", and your command should work; that is, when calling powershell.exe from cmd.exe / outside PowerShell, use \" , not \`" (or `") in order to escape " chars.:
powershell -Command "(gc C:\my_configuration.conf) -replace 'INSERT_URL', \"https://mytestserver/WW48.2'22/testing.bin\" | Out-File C:\my_configuration.conf"
While you do need to escape the " characters embedded in your overall "..." command string, escaping them as \" is sufficient - no need to also use `, the backtick, PowerShell's usual escape character.
The PowerShell CLI (powershell.exe) expects \-escaping of ", so as to better align with most CLIs, even though inside a PowerShell session you need to use `" or (inside "..." only) "".[1]
You'd only need both \ and ` - in the form `\", note that ` comes first - if your embedded "..." itself contained " chars; a contrived example:
:: OK: Prints '3" of snow.'
powershell.exe -c " Write-Output \"3`\" of snow.\" "
As iRon notes, an alternative solution is to use embedded '...' quoting (single-quoting) instead.
Since your URL itself contains a ' char., that character must then be escaped as '':
:: Note the use of '...' around https://... and the inner ' escaped as ''
powershell -Command "(gc C:\my_configuration.conf) -replace 'INSERT_URL', 'https://mytestserver/WW48.2''22/testing.bin' | Out-File C:\my_configuration.conf"
[1] In PowerShell (Core) 7+, whose CLI is pwsh.exe, you may alternatively use "" inside overall "..." on the command line too, which is actually the more robust choice when calling from cmd.exe. When calling powershell.exefromcmd.exe, the robust choice is "^""(sic) - see [this answer](https://stackoverflow.com/a/49060341/45375). However, the PowerShell CLI recognizes"in _both_ editions, and"also works for"chars. _not_ inside overall"..."`.
Try using this syntax, always works
"%windir%\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "& { <# PUT ANYTHING HERE #> }"
You won't need to worry about escaping anything.
Your code:
"%windir%\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "& { (gc C:\my_configuration.conf) -replace 'INSERT_URL', "https://mytestserver/WW48.2%2722/testing.bin" | Out-File 'C:\my_configuration.conf' }"
EDIT1: Check here for URL special characters. the single quote (') can be handled by its replacement (%27) in your hard-coded string. (I changed it above in the 2nd code sample)

Converting unix script to windows script - emulating a Sed command in PowerShell

I have a unix script (korn to be exact) that is working well and I need to convert it windows batch script. So far I have tried inserting a powershell command line on my code, but it doesn't work. Please help, I am just new to both unix scripting and windows scripting so any help will do.
This is the line of code that I need to convert:
#create new file to parse ; exclude past instances of timestamp
parsefile=/tmp/$$.parse
sed -e "1,/$TIMESTAMP/d" -e "/$TIMESTAMP/d" $DSTLOGFILE > $parsefile
So far I have tried a powershell command line to be called on my script but it didn't work:
:set_parse_file
#powershell -Command "Get-Content $SCHLOGFILE | Foreach-Object {$_ -replace('1,/"$TIMESTAMP"/d' '/"$TIMESTAMP"/d'} | Set-Content $PARSEFILE"
Any suggestions please?
PowerShell has no sed-like constructs for processing ranges of lines (e.g., sed interprets 1,/foo/ as referring to the range of consecutive lines from line 1 through a subsequent line that matches regex foo)
Emulating this feature with line-by-line processing would be much more verbose, but a comparatively more concise version is possible if the input file is processed as a whole - which is only an option with files small enough to fit into memory as a whole, however (PSv5+ syntax).
Here's the pure PowerShell code:
$escapedTimeStamp = [regex]::Escape($TIMESTAMP)
(Get-Content -Raw $SCHLOGFILE) -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') `
-replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') |
Set-Content -NoNewline $PARSEFILE
Note that [regex]::Escape() is used to make sure that the value of $TIMESTAMP is treated as a literal, even if it happens to contain regex metacharacters (chars. with special meaning to the regex engine).
Your ksh code doesn't do that (and it's nontrivial to do in ksh), so if - conversely - $TIMESTAMP should be interpreted as a regex, simply omit that step and use $TIMESTAMP directly.
The -replace operator is regex-based and uses the .NET regular-expression engine.
It is the use of Get-Content's -Raw switch that requires PSv3+ and the use of Set-Content's -NoNewline switch that requires PSv5+. You can make this command work in earlier versions, but it requires more effort.
Calling the above from cmd.exe (a batch file) gets quite unwieldy - and you always have to be wary of quoting issues - but it should work:
#powershell.exe -noprofile -command "$escapedTimeStamp = [regex]::Escape('%TIMESTAMP%'); (Get-Content -Raw '%SCHLOGFILE%') -replace ('(?ms)\A.*?\r?\n.*?' + $escapedTimeStamp + '.*?\r?\n') -replace ('(?m)^.*?' + $escapedTimeStamp + '.*\r?\n') | Set-Content -NoNewline '%PARSEFILE%'"
Note how the -command argument is passed as a single "..." string, which is ultimately the safest and conceptually cleanest way to pass code to PowerShell.
Also note the need to embed batch variables as %varname% in the command, and since they are enclosed in embedded '...' above, the assumption is that their values contain no ' chars.
Therefore, consider implementing your entire script in Powershell - you'll have a much more powerful scripting language at your disposal, and you'll avoid the quoting headaches that come from bridging two disparate worlds.

Replacing strings in a PowerShell file and then calling it via batch + command line arguments

This is my PowerShell file:
#Replace.ps1
Param(
[string]$filepath,
[string]$find,
[string]$replace
)
$content = Get-Content $filepath
$content = $content.replace($find,$replace)
$content | out-file $filepath
This is the batch file which I am using it to call this
#ChangeIP.bat
#echo on
powershell.exe -File E:\Replace.ps1 %1 %2 %3
Now when I try to call the batch file from cmd as:
ChangeIP.bat "E:\foreign logs.txt" firstword secondword
then it is showing some ridiculous errors.
I basically am stuck in passing the file name (which is having white spaces).
The code I need basically should be able to do the following things:
A PowerShell script that takes three command line arguments:
FilePath // With white spaces (don't know how)
String to replace
String to be replaced with
The PowerShell script should be able to fetch the contents of the "FilePath" supplied. Find the "String to replace" string and Replace it with "String to be replaced with" string
Then calling this PowerShell script via batch file and supplying the three command line arguments there.
Please keep in the mind, the file path contains spaces.
Try the -replace operator instead of the Replace() method in case you're starting PowerShell v2:
$content = $content -replace $find $replace

How do you resolve special folders in path strings in PowerShell?

In the Windows Command Prompt, special folders are resolved like so:
However, in powershell, these folders do not seem to be resolved:
Consider the string:
$myfile = "%temp%\\myfolder\\myfile.txt"
How can I use this as an argument to PowerShell functions (eg: Remove-Item), and have PowerShell correctly resolve the special folders, as opposed to taking it literally and prepending the current working directory?
Edit:
I am working with strings using standard windows path notation coming from external configuration files, for example:
config.json:
{
"some_file": "%TEMP%\\folder\\file.txt"
}
myscript.ps1:
$config = Get-Content -Raw -Path "config.json" | ConvertFrom-Json
Remove-Item -path $config.some_file -Force
Note: as any of the Windows special folders can appear in these strings, I'd rather avoid horrible find-replace hacks like this
$config.some_file = $config.some_file -replace '%TEMP%' $env:temp
You can expand it to a full path using:
[System.Environment]::ExpandEnvironmentVariables("%TEMP%\\myfolder\\myfile.txt")
c:\users\username\AppData\Local\Temp\\myfolder\\myfile.txt
Double-backslash \\ isn't a PowerShell thing either, \ is not a special character in a PowerShell string - but double backslashes in a path do seem to work.
Documentation: https://msdn.microsoft.com/en-us/library/system.environment.expandenvironmentvariables.aspx
If you don't mind some performance issues
$resolvedPathInABitHackyWay = (cmd /c echo "%TEMP%\\folder\\file.txt")
This will actually give you %TEMP% resolved by cmd itself.
You can grab all env variables from the env:\ drive and use that to construct a succinct regex pattern for your find-replace operation, then use the Regex.Replace() method with a match evaluator:
$vars = Get-ChildItem env:\ |ForEach-Object {[regex]::Escape($_.Name)}
$find = "%(?:$($envNames -join '|'))%"
[regex]::Replace($config.some_file, $find, {param([System.Text.RegularExpressions.Match]$found) return (Get-Item "env:\$($found.Groups[1])").Value},'IgnoreCase')

VS2010 Post-build event, replace string in a file. Powershell?

I need to replace a simple string in a minified .js file after a successful build in VS2010.
So I'm trying to run a simple command line call from the Post-build events window.
This example, from here: https://blogs.technet.com/b/heyscriptingguy/archive/2008/01/17/how-can-i-use-windows-powershell-to-replace-characters-in-a-text-file.aspx totally mangulates the resulting .js file. Something is wrong, I suspect it is coming across some weird chars in my minified .js file that screws it up.
(Get-Content C:\Scripts\Test.js) |
Foreach-Object {$_ -replace "// Old JS comment", "// New JS comment"} |
Set-Content C:\Scripts\Test.js
How can I achieve such a simple task like I could do in unix in a single line..?
It would be great to see the diff file. Without more info, some info:
Set-Content adds a new empty line at the end (probably not a problem for you)
You can use -replace operator like this:
(gc C:\Scripts\Test.js) -replace 'a','b' | sc C:\Scripts\Test.js
-replace works on arrays too.
You could read the content via [io.file]::ReadAllText('c:\scripts\test.js') and use-replace`, but again, I don't think there will be significant difference.
Edit:
Double quotes are used when evaluating the string. Example:
$r = 'x'
$a = 'test'
'beg',1,2,"3x",'4xfour','last' -replace "1|$r","$a"
gives
beg
test
2
3test
4testfour
anything
To save the content with no ending new line, just use [io.file]::WriteAllText
$repl = (gc C:\Scripts\Test.js) -replace 'a','b' -join "`r`n"
[io.file]::WriteAllText('c:\scripts\test.js', $repl)

Resources