Use double quote then curly brace in powershell -Command - powershell

There are a couple questions related to this on here but they specifically address Write-Host. I want to run something like
powershell.exe -Command "'example.exe' /f`"`{GUID`}`""
Only it fails with the error
Missing closing '}' in statement block.
At line:1 char:396
+ $mypid=(get-process EXEName*).id;wait-process -id $mypid;
& `C:\Users\User\AppData\Local\Temp\{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}\Target.exe`
/s /ig``{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}` /instance=1 /f3`C:\Recordings`
/f4`uninstall-log.txt` /f1`C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}\setup.iss`
/f2`C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}\setup.log` /removeonly <<<<
+ CategoryInfo : ParserError: (CloseBraceToken:TokenId) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
I have other strings starting with " and containing {} (like /f"C:\Folder\{GUID}\program.exe" and these don't cause any trouble. It's only my argument where the curly braces are adjacent to the double quotes:
/ig`"`{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`}`"
In the error message, it may or may not be noteworthy that all the double quotes are gone so my /ig argument is left with two backticks. I believe my version is 2.0. Here is my actual (modified) command:
powershell -Command "$mypid=(get-process EXEName*).id;wait-process -id
$mypid;& 'C:\Users\User\AppData\Local\Temp\{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX}\Target.exe' /s /ig`"`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}`" /instance=1 /f3`"C:\Recordings`" /f4`"uninstall-log.txt`"
/f1`"C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}\setup.iss`"
/f2`"C:\Users\User\AppData\Local\Temp\`{XXXXXXXX-XXXX-XXXX-XXXX-
XXXXXXXXXXXX`}\setup.log`" /removeonly"
Can anyone shed some light on this? I don't know why it would be invalid. Thank you!

The problem is using the -Command parameter for anything other than a simple script you will run into issues where characters such as curly braces and quotes will be misinterpritted by the command prompt before the are they are passed to Powershell. You could could tie yourself in knots by adding several layers of escaping or there is a simplier way - use the -EncodedCommand parametter instead.
For the EncodedCommand you just need to Base64 encode your command which you can do with the following Powershell script:
$command = #'
# Enter your commands containg curly braces and quotes here
# As long as you like
'#
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes) | clip.exe
This will copy the encoded command to the clipboard, then all you need to do to use your command is type:
powershell.exe -EncodedCommand
.. and then paste in your command so that you end up with something like the following:
powershell.exe -EncodedCommand IAAgACMAIABFAG4AdABlAHIAIAB5AG8AdQByACAAcwBjAHIAaQBwAHQAIABjAG8AbgB0AGEAaQBuAGcAIABjAHUAcgBsAHkAIABiAHIAYQBjAGUAcwAgAGEAbgBkACAAcQB1AG8AdABlAHMACgAgACAAIwAgAEEAcwAgAGwAbwBuAGcAIABhAHMAIAB5AG8AdQAgAGwAaQBrAGUAIAAgAA==
You now have something that is command prompt safe.

Related

PowerShell Find and Replace in file issues

I am having trouble finding how to find and replace in a file using poweshell. The file has several lines that are similar. I need to change the "ACT" to "RDY" in the line that starts with <PC_4 . All Lines start with < and end with >.
The Code:
powershell -Command(Get-Content C:\testfodler\test.XML) -replace '<PC_4 SET_THING="ACT">', '<PC_4 SET_THING="RDY">' | Out-File -encoding ASCII myFile.txt
The error I get is:
The '<' operator is reserved for future use.
Not all parse errors were reported. Correct the reported errors and try again.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : RedirectionNotSupported
Running this on a windows 10 machine.
First, you should quote the value (code) passed to the -Command parameter because it expects a string. How you quote depends on how you call powershell.exe.
If you are in PowerShell or CMD and are calling powershell.exe, you may surround the code passed to -Command with double quotes and double quote escape ("") inner double quotes, which is the PowerShell layer of escaping. Then backslash escape the escaping quote, which is for the cmd layer of escaping.
powershell.exe -command "(Get-Content C:\testfolder\test.XML) -replace '<PC_4 SET_THING=\""ACT\"">', '<PC_4 SET_THING=\""RDY\"">' | Set-Content myfile.txt -encoding ASCII"

Calling Powershell script from CMD with paths as parameters

I`m having trouble make this cmd command to run powershell script by passing two parameters (paths).
C:\WINDOWS\System32>powershell -command \"C:\Apps\Scripts\Test\testing.ps1" \"\\Data1\dataholder$\office\J Smith\backup\" \"\\Data1\dataholder$\office\J Smith\backup2\"
I`m getting the error below:
At line:1 char:36
+ ... ps\Scripts\Test\testing.ps1 "\\Data1\dataholder$\office\ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unexpected token '\\Data1\dataholder$\office' in expression or statement.
At line:1 char:146
+ ... Smith\backup" "\\Data1\dataholder$\office\J Smith\backup2"
+ ~
The string is missing the terminator: ".
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Any idea of why this is failing?
I had to modify the path leading to script. Now the command look like that:
C:\WINDOWS\System32>powershell -command \"C:\Apps\Scripts\Power test\testing.ps1" \"\\Data1\dataholder$\office\J Smith\backup\" \"\\Data1\dataholder$\office\J Smith\backup2\"
This is failing due to the space in the script path, any idea how to handle this?
You need to backslash escape quotes that you want to be sent from cmd shell to PowerShell:
powershell -command "& \"C:\Apps\Scripts\Power test\testing.ps1\" \"\\Data1\dataholder$\office\J Smith\backup\" \"\\Data1\dataholder$\office\J Smith\backup2\""
If your program or script file has spaces, you need to call it with a call operator (&) and include that in your string that is sent to -Command. Notice the double quotes around the entire value/string that maps to -Command.
Note: Inner double quotes work here because $ is succeeded by \. If you had dataholder$folder as an example, you would need single quotes or to escape the $ because PowerShell would try to interpret $folder as a variable.
Here's a backquoting the spaces approach.
type "my script.ps1"
echo $args[0]
powershell .\my` script.ps1 hi` there
hi there
Or strategic placement of single quotes. Something can be executed if the first character isn't a quote. The quote didn't work before the backslash.
powershell .\'my script.ps1' 'hi there'
hi there

Set window title when running "start powershell" in powershell not working?

The following command works in CMD (How to start powershell with a window title?).
start powershell -NoExit -command "$Host.UI.RawUI.WindowTitle = 'bits'"
But it doesn't work in Powershell.
PS C:\> start powershell -noexit -command "$Host.UI.RawUI.WindowTitle = 'test'; read-host"
Start-Process : A parameter cannot be found that matches parameter name 'noexit'.
At line:1 char:18
+ start powershell -noexit -command "$Host.UI.RawUI.WindowTitle = 'test ...
+ ~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Start-Process], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand
The following command can open a new powershell window.
start powershell "$Host.UI.RawUI.WindowTitle = 'test'; read-host"
However, the new window shows the following error message and the title is not set.
System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.WindowTitle : The term
'System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.WindowTitle' is not recognized as the name of a
cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify
that the path is correct and try again.
At line:1 char:1
+ System.Management.Automation.Internal.Host.InternalHost.UI.RawUI.Wind ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.Manageme...wUI.WindowTitle:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Bacon Bits' helpful answer explains that start in cmd.exe means something different than in PowerShell.
Use Start-Process as follows to get the desired result; note that powershell implicitly binds to parameter -FilePath, whereas the ,-separated arguments starting with -NoExit bind implicitly to the -ArgumentList (-Args) parameter, which accepts an array of strings:
# In PowerShell, `start` is an alias for `Start-Process`
start powershell '-NoExit', '-command', "`$Host.UI.RawUI.WindowTitle = 'bits'"
In paticular, --prefixed pass-through arguments must be quoted so that they're not mistaken for Start-Process's own parameters.
Also note the ` preceding the $ in $Host, which prevents up-front interpolation of $Host by the calling PowerShell instance.
You could also use '$Host.UI.RawUI.WindowTitle = ''bits''', a single-quoted literal string with embedded single quotes escaped as ''.
Important:
While passing arguments as an array to -ArgumentList is conceptually the best approach, it is unfortunately ill-advised due to a long-standing bug in Start-Process, still present as of this writing (v7.1) - see GitHub issue #5576.
For now, using a single string comprising all arguments, enclosed in embedded "..." quoting as necessary, is the only generally robust approach. As discussed in the linked GitHub issue, an -ArgumentArray parameter that supports robust array-based argument passing may be introduced in the future.
In the case at hand this means the following, as suggested by PetSerAl in a comment on the question:
Start-Process powershell '-NoExit -command "$Host.UI.RawUI.WindowTitle = ''bits''"'
Note the single-quoting_ ('...') of the overall argument-list string, which then necessitates escaping the embedded single quotes - those that PowerShell should see as part of the command - as ''.
In Command Prompt, start is the start internal command. In Windows Powershell, start is an alias for Start-Process, which does something similar but isn't identical.
Try running this:
powershell -NoExit -command "`$Host.UI.RawUI.WindowTitle = 'bits'"
Here's another way, while avoiding the dollar sign. The double quotes have to be on the outside, so that bits stays quoted.
start powershell "-noexit (get-variable host).value.ui.rawui.windowtitle = 'bits'"
You can always avoid these quoting issues putting the command in a file.
start powershell '-noexit .\window.ps1'
start powershell with the command option for setting the window title did not work for me. Maybe because I want to open another powershell with a ps1 file. so in the other ps1 file I added the first line as below,
(Get-Host).ui.RawUI.WindowTitle='TEST TEST'
and it worked like a charm....
If you add this to your powershell profile.ps1 you can get the window title to show the current running script and if you are just opening a window with no script then 'pwsh' will be displayed.
Will be systematic with no need to add a line on top of each script. The other answers
combined with $MyInvocation.MyCommand seem to give the name of the profile.ps1 instead when running a script from the context menu.
This can also be tweaked to change the result.
[console]::title = Split-Path -Leaf ([Environment]::GetCommandLineArgs()[-1]).Replace('pwsh.dll','pwsh')
Works on both PS 5 and 7 . For ver. 5 replace pwsh.dll by powershell.exe

Passing Powershell from Cmd with variable evaluation

I want to use the following command
powershell -command "get-content %CONFIG_FILE% | %{$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
but with the evaluation of the %CONFIG_FILE% and the %APP_DIR% which are defined in the batch script using
set CONFIG_FILE=C:\B0_GW.cfg
set APP_DIR=C:\dbg\kernel
when i do so, i currently get the following issue:
The string is missing the terminator: ".
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
any ideas?
aschipfl has provided the crucial pointer in a comment on the question:
From within a batch file[1], you must escape % characters that you want to pass through to the target application as %%.
Otherwise, cmd interprets % as the start or end of an environment-variable reference (which in part happens by design in your command; e.g., %CONFIG_FILE%).
(You've already correctly escaped embedded " chars. in your command as \" for PowerShell).
Specifically, the % in the %{...} part of your command needs escaping (% is PowerShell's built-in alias for the ForEach-Object cmdlet):
powershell -command "get-content %CONFIG_FILE% | %% {$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
Alternatively, simply use the cmdlet's actual name, ForEach-Object, which obviates the need for escaping:
powershell -command "get-content %CONFIG_FILE% | ForEach-Object {$_ -replace \"APP_PATH=.+\",\"APP_PATH=%APP_DIR%\"}"
[1] Sadly, the behavior at cmd.exe's command prompt (in an interactive session) differs - see this answer.

Powershell command -replace with multi-line value

I'm struggling to use powershell to replace a string with multi-line value.
The value is from Jenkins input text parameter. So this value is a multi-line string.
I use powershell to replace {{BUILD_INFO_CHANGES}} with %BUILD_INFO_CHANGES%.
The %BUILD_INFO_CHANGES% value is
-bug1
-bug 2
Here is the script:
powershell -Command "(gc %JOB_BUILD_DIR%\ThisBuildInfo.md) -replace '{{BUILD_INFO_CHANGES}}', '%BUILD_INFO_CHANGES%' | Out-File %JOB_BUILD_DIR%\ThisBuildInfo.md"
However, I got the error response from Jenkins.
'{{BUILD_INFO_FIXED_BUGS}}', <<<< '- bug1 is missing the terminator: '. ... + FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
d
And I change the script and use # to wrap the value. Here is the changed script.
powershell -Command "(gc %JOB_BUILD_DIR%\ThisBuildInfo.md) -replace '{{BUILD_INFO_CHANGES}}', #'%BUILD_INFO_CHANGES%'# | Out-File %JOB_BUILD_DIR%\ThisBuildInfo.md"
I got another error.
FullyQualifiedErrorId : UnrecognizedToken
Does anyone have a solution for this?
thanks!
The problem is using the -Command parameter for anything other than a simple script you will run into issues where characters such as curly braces and quotes will be misinterpreted by the command prompt before the are they are passed to PowerShell. You could could tie yourself in knots by adding several layers of escaping or there is a simpler way - use the -EncodedCommand parameter instead.
For the -EncodedCommand you just need to Base64 encode your command which you can do with the following PowerShell script:
$command = #'
# Enter your commands containg curly braces and quotes here
# As long as you like
'#
$bytes = [System.Text.Encoding]::Unicode.GetBytes($command)
$encodedCommand = [Convert]::ToBase64String($bytes) | clip.exe
This will copy the encoded command to the clipboard, then all you need to do to use your command is type:
powershell.exe -EncodedCommand
.. and then paste in your command so that you end up with something like the following:
powershell.exe -EncodedCommand IAAgACMAIABFAG4AdABlAHIAIAB5AG8AdQByACAAcwBjAHIAaQBwAHQAIABjAG8AbgB0AGEAaQBuAGcAIABjAHUAcgBsAHkAIABiAHIAYQBjAGUAcwAgAGEAbgBkACAAcQB1AG8AdABlAHMACgAgACAAIwAgAEEAcwAgAGwAbwBuAGcAIABhAHMAIAB5AG8AdQAgAGwAaQBrAGUAIAAgAA==
You now have something that is command prompt safe.
Thanks Dave! Your solution give me an idea.
Here is the easiest solution to work on Jenkins.
I put the following scripts on a Powershell build step in my Jenkins build job.
$thisBuildInfoPath="$env:JOB_BUILD_DIR\ThisBuildInfo.md"
$fixedBugs=#("$env:BUILD_INFO_FIXED_BUGS")
(gc $thisBuildInfoPath) -replace "{{BUILD_INFO_FIXED_BUGS}}", $fixedBugs | sc $thisBuildInfoPath