Powershell command from cmd doesnt replace line breaks - shell

I am trying to replace a string including a line break in a file. I am using the command line for this.
I am trying to use the same command in a CMD shell and in PowerShell, however I can only seem to get it to work in the latter.
Here is the command:
powershell -Command "(Get-Content client.properties -Raw).Replace('#test`r`n','test`r`n') | Set-Content client2.properties"
Why is this not working in a CMD shell, and how do I make it work?

The `r`n escape sequence won't work inside single-quotes.
Use the -replace operator instead and use regex escapes:
powershell -Command "(Get-Content client.properties -Raw)-replace('#test\r?\n','test'+$([Environment]::NewLine)) | Set-Content client2.properties"

Related

How to escape poison character in this mixed cmd / powershell code?

I asked for a first explanation "here" and "here" but going to try a more complex situation I was unable (after two hours of trying) to understand how to solve. I read how the regular expression works but nothing, I went into the ball.
The modified code is this:
(Fsutil Dirty Query %SystemDrive%>Nul)||(powershell.exe -c "[Environment]::CommandLine; Start -Verb RunAs cmd /k, ("^""%~f0"^"" -replace '[;,()= &^]', '^$&')" & echo exit)
and the folder with the poison characters is this:
C:\Users\fposc\Desktop\Pie & tea % # ' $^
I have tried to escape the ^ in the regular expression with \^ but don't work. I have escaped also ( and ) with \( and \). But nothing work:
(Fsutil Dirty Query %SystemDrive%>Nul)||(powershell.exe -c "[Environment]::CommandLine; Start -Verb RunAs cmd /c, ("^""%~f0"^"" -replace '[;,\(\)= &\^]', '^$&')" & exit)
I added the round brackets because I wanted to put all possible characters to make the code as generic as possible.
I don't know if I was right to open another question. Maybe I should change the original question? Since other combinations are possible and not having understood the mechanism I could open many other similar questions. What do you advise me to do?
The problem is the presence of $ in your folder name, which causes the PowerShell command to interpret it as the start of a variable reference.
The workaround is to use an aux. environment variable to store the batch file's full path and let PowerShell perform its escaping based on this variable's value:
:: Unless already elevated, re-invoke this batch file with elevation,
:: via PowerShell.
set "__THISFILE=%~f0"
Fsutil Dirty Query %SystemDrive% >Nul || (powershell.exe -c "Start-Process -Verb RunAs cmd /k, ($env:__THISFILE -replace '[ &%%^]', '^$&')" & exit)
I have updated the answer to your original question to incorporate this approach, which now shows a - hopefully - robust approach to on-demand re-invocation of a batch file with elevation, including support for arguments.

Open a command file with Windows PowerShell running it directly

I want to make a file having Windows Powershell commands. Then I want to open it with windows powershell directly and without pressing any key I want windows powershell start running those commands directly same as command prompy I can make .cmd or .bat file.
For example:
These are two commands or Powershell, I want to save this file. Then I want directly execute this file by powershell. I have tried to save it as ps1 and ps2 extension as well but not working. Many methods online are not working. Any solution?
PowerShell script files, across all versions, use the .ps1 filename extension.
From within PowerShell, you can invoke them directly, e.g., .\script.ps1
Note that, unlike in cmd.exe, you must use .\ (or a full path) in order to execute a file located in the current directory - just script.ps1 won't work - see this answer for background information.
From cmd.exe, you must use PowerShell's CLI (powershell.exe in Windows PowerShell / pwsh in PowerShell [Core] v6+) in order to execute a script file:
powershell.exe -File script.ps1
pwsh -File script.ps1 (-File may be omitted)
Note that with -File the .\-prefix is not required.
However, if you use -Command (-c) instead (which is the default with powershell.exe, whereas pwsh now defaults to -File), you do need the .\, because the -Command argument(s) are interpreted as a piece of PowerShell code, i.e. as if you had submitted it inside a PowerShell session.
You've discovered this in your own answer, where you pass a PowerShell command directly to the (implied) -Command parameter.
Note, however, that it's better to double-quote such commands, so as to prevent cmd.exe from interpreting certain characters itself, which breaks the call.
For instance, the following call would break, if you didn't enclose the -Command (-c) argument in "...":
# From cmd.exe; "..." required.
C:\>powershell.exe -c "Write-Output 'a & b'"
a & b
Another important consideration is that you need to escape embedded " chars. as \" for the CLI (even though PowerShell-internally you would use `" or ""):
# From cmd.exe; note the inner " escaped as \"
C:\>powershell.exe -c "Write-Output \"hi there\""
hi there
I have found the solution. I use command powershell.exe and can directly execute powershell commands within cmd.
powershell.exe $MyVariable=Get-Content .\Path.txt
is working fine for me

How to escape spaces the parameters to a PS script running in VS post-build event

Update 1:
After looking carefully at the output again, I sort of figured it out.
By adding a trailing space between the closing parenthesis and the quotes it works:
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" "$(ProjectDir) " "$(TargetDir) "
I am suspecting that PowerShell somehow interprets the )".
Is there a more elegant way around this issue?
Update 2:
This is weird. I have another script that does the clean up and works with out the space between ) and " like this:
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersionCleanup.ps1" "$(ProjectDir)"
However adding trailing spaces will cause it to fail, because internally it appends a file-name to the path, so that the path will be incorrect.
If any one understand this, I would be happy to accept the explanation as the correct answer!
Original:
I have the following prebuild-command in VisualStudio, which I want to use to inject the version from a Git-tag:
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" $(ProjectDir) $(TargetDir)
This works fine, if the path $(SolutionDir) does not contain spaces (which will then also present in $(ProjectDir) or $(TargetDir)).
When the path $(SolutionDir) does contain spaces, it appears the script starts as expected, but the arguments are not passed correctly and I am unable to figure out how to escape them in the arguments to the PS-script.
I have tried adding sing ", triple """ and also ', which gives the following (each PS-command tries a different method for escaping the spaces):
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" $(ProjectDir) $(TargetDir)
args[0]:
D:\VisualStudio
args[1]:
Projects\software_git_repo\ProgramEditor\
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" "$(ProjectDir)" "$(TargetDir)"
BS: args[0]:
D:\VisualStudio Projects\software_git_repo\ProgramEditor" D:\VisualStudio
BS: args[1]:
Projects\software_git_repo\ProgramEditor\bin\Debug"
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" """$(ProjectDir)""" """$(TargetDir)"""
BS: args[0]:
"D:\VisualStudio
BS: args[1]:
Projects\software_git_repo\ProgramEditor"
powershell -ExecutionPolicy ByPass -File "$(SolutionDir)\BuildScripts\InjectGitVersion.ps1" '$(ProjectDir)' '$(TargetDir)'
BS: args[0]:
'D:\VisualStudio
BS: args[1]:
Projects\software_git_repo\ProgramEditor\'
Doing:
echo ProjectDir:
echo $(ProjectDir)
echo TargetDir:
echo $(TargetDir)
I get:
ProjectDir:
D:\VisualStudio Projects\software_git_repo\ProgramEditor\
TargetDir:
D:\VisualStudio Projects\software_git_repo\ProgramEditor\bin\Debug\
Beware unexpected escaped quotes when a VS directory macro is expanded in a quoted parameter.
A script parameter containing a path should be wrapped in quotes to avoid being interpreted as multiple parameters if the path includes spaces. The following would therefore seem reasonable in Visual Studio's pre/post build event command line:-
powershell.exe -file "$(SolutionDir)script.ps1" -projectDirectory "$(ProjectDir)"
But the macro values that specify directories always include a trailing backslash; so when they're expanded the line becomes, for example:-
powershell.exe -file "C:\sln path\script.ps1" -projectDirectory "C:\sln path\project\"
...where the trailing \" is an escaped character that is interpreted as part of the parameter value, which is passed to the script as:-
C:\sln path\project"
Solution
The fix in my case was to insert an additional backslash after the directory macro to be escaped instead of the quotes, which restores the expected parameter value and prevents the mangling of any subsequent params:-
powershell.exe -file "$(SolutionDir)script.ps1" -projectDirectory "$(ProjectDir)\"
Not very intuitive though. Let me know if I'm missing something more obvious...
One thing I noticed is an extra backslash after $(SolutionDir). I'm using VS2017 and I think consistently $(SolutionDir) has always had a backslash. Not sure though.
I have a .bat in my solution folder that creates a json config file. My Post-Build event looks like this.
call "$(SolutionDir)CreateConfigurationJson.bat" "$(TargetDir)mySettings.json"
{"ProjectName":"$(ProjectName)"}
"$(TargetDir)mySettings.json" is the first parameter of the .bat file
{"ProjectName":"$(ProjectName)"} is the second parameter.
No escape characters necessary. Hope this helps.
The code for the bat file is:
echo off
set filePath=%1
break>%filePath%
set json=%2
echo %json% >>%filePath%
thank you #tranquil_tarn for your great answer!
I struggled with the BATCH + Powershell escaping hell for multiple hours.
Posting final code here to hopefully help someone out.
Solution
#ECHO OFF
cls
echo .SYNOPSIS
echo Allows prebuild to be Powershell, instead of BATCH. (You'll thank me later)
echo Specifically this example shows passing in VisualStudio (ProjectDir) variable AND correctly handles spaces in the path.
echo .DESCRIPTION
echo Call prebuild.ps1 and pass in built-in VS Variables: https://learn.microsoft.com/en-us/visualstudio/ide/reference/pre-build-event-post-build-event-command-line-dialog-box?view=vs-2022
echo Use powershell best-practices named parameters rather than positional to avoid positional-unmatched errors.
echo If Username '$(User)' is blank DO NOT include the Switch at all to avoid powershell errors
REM TASK1: Check a variable for blank/empty and only include the Param if not empty
SET "user=$(User)"
IF [%user%]==[] (
echo "userParam is blank"
) ELSE (
SET "userParam=-User:%user%"
)
echo "userParam: %userParam%"
REM TASK2: Warning: (ProjectDir) in VisualStudio ends in a slash, which when expanded, results in escaped quote \' at end of line
REM if you DO NOT include the quotes, then spaces in path will break it. if you DO include quotes then it gets escaped.
REM solution is to include an extra slash at the end, resulting in double backslash \\ which escapes down to single backslash '\'... whew!
REM 'see https://stackoverflow.com/questions/52205117/how-to-escape-spaces-the-parameters-to-a-ps-script-running-in-vs-post-build-even/64146880#64146880'
cd "$(ProjectDir)\"
SET cmd1=powershell -NoProfile -ExecutionPolicy Unrestricted -Command ^
".\prebuild.ps1 -ProjectDir:'$(ProjectDir)\' -Conf:'$(Configuration)' -ProjectName:'$(ProjectName)' %userParam% "
REM For debugging, view the command (including escaped strings) before calling it.
echo %cmd1%
call %cmd1%

Using PowerShell command and backtick newline inside Windows batch

I am trying to write a Windows batch file which will replace occurrences of angled brackets (><) with a newline in between.
I am new to PowerShell, but in searching though possible solutions, I have found the following works from PowerShell:
(get-content input.txt) -replace "><", ">`n<" | set-content output.txt
To use this within a windows batch, I need to wrap it inside
powershell -command "arguments"
So the final command is something like:
powershell -command "(gc input.txt) -replace '><', '>`n<' | sc output.txt"
However, this of course does not work because the single quotes around the replace text causes the grave quote escape character to be treated literally.
I have searched far and wide on the correct combination of escape characters to use to allow the PowerShell escape character to be recognised and have found a similar answer in here, but when I try this suggestion, I get a "< was unexpected at this time" error. I think what I need is more complicated because my search string also contains the angled brackets.
Look at the powershell.exe command line options. You can use a script block:
powershell -command {(gc input.txt) -replace "><", ">`n<" | sc output.txt}
Avoid using the escape character and double quotes?
powershell -command "(gc d:\t\input.txt) -replace '><', ('>'+[char]10+'<') | sc d:\t\output.txt"
I have solved the problem.
I also used delayed expansion, so the final command is:
powershell -Command "(gc !inputfile!) -replace (\"^>^<\", \"^>`n^<\") | sc !outputfile!"
So it actually uses three different types of escape characters! Combination of \ and ^ and `.
I wish I could say I worked it out logically, but in the end it was just a random attempt using different escapes on the ><.
But this is now a good reference on how to use PowerShell inside Windows batch without using single quotes which turn escape characters into literals.

Launch PowerShell from Command Prompt with custom prompt

I'm trying to open PowerShell with a customised prompt (for instance the UNIX shell prompt). I have tried:
powershell -noexit -command "& {function prompt {"$(pwd)$ "}}"
But it just starts powershell without the prompt I want. It does actually work in powershell itself. Could I get this to work or do I have to make a seperate file and do it through "-file"?
Not sure what the UNIX prompt defaults too but this should do what I think you want it to do.
powershell -noexit -command "function prompt {'{0}$ ' -f $pwd}"
If you use single quotes in the prompt function the $ doesn't get interpolated, and you don't have to worry about to many quotes.
SAVING THE PROMPT FUNCTION
Like any function, the Prompt function exists only in the current
session. To save the Prompt function for future sessions, add it to your
Windows PowerShell profiles. For more information about profiles,
see about_Profiles.
Here's how to create a new profile:
if (!(test-path $profile))
{new-item -type file -path $profile -force}
notepad $profile
Quoting on the command-line is tricky. Also, & runs a scriptblock in its own scope, so functions defined there don't "leak" out to the calling scope. The dot operator (also called dot-sourcing) is what you're looking for. This is what I got to work using backslashes to quote the strings.
powershell -noexit -command ". {function prompt {\"$(pwd)$ \"}}"
Add your custom prompt to your profile and it will load/run every time you start PowerShell.
Powershell customisation is always a bit tricky. Try adding a script with a method called prompt() like this:
function prompt() {
$myPrompt = "Ready>";
write-host -NoNewLine -ForegroundColor green $myPrompt
' '
}
Then call this in a profile, such as the one for all users:
%windir%\system32\Windows­PowerShell\v1.0\profile.ps1
Good luck!

Resources