Command line output for PowerShell

I have a Windows application and events, it calls the following command:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'" 

The name parameter sometimes has a ' . Is it possible to somehow escape?

+7
source share
5 answers

Actually it is much more difficult than you think. Highlighting nested quotes in strings passed from cmd to PowerShell is a major headache. What makes this especially difficult is that you need to make a replacement for the variable extended by cmd in the quoted argument passed to powershell.exe in the single-track argument passed to the PowerShell script parameter. AFAIK cmd does not have any built-in functions for even basic string replacements, so you need PowerShell to replace for you.

If the argument to the -data strong> parameter (the one contained in the cmd x variable) does not have to be single, the simplest thing for do is the double quote, so the single quote in the x value does not need to be run at all. I say "simple", but even that is a little complicated. As Vasily Sirakis pointed out, ^ usually an escape character in cmd, but in order to avoid double quotes in a string (double-), you should use \ . So you can write your command command as follows:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name \"%x%\" -data '%y%'"

This passes the following PowerShell command:

 G:\test.ps1 -name "value of x, which may contain 's" -data 'value of y' 

If, however, x can also contain characters that are special characters in PowerShell interpolated strings ( " , $ ` ), then it gets LOT harder. The problem is that % x is a cmd variable that extends to cmd before how PowerShell has the ability to touch it.If you specify one quote in the command that you pass powershell.exe and it contains one quote, then you give the PowerShell session a string that ends earlier, so PowerShell is not able to perform any operations on Itโ€™s obvious that this doesnโ€™t work This is because, before replacing anything, the -replace operator must provide the correct line:

 'foo'bar' -replace "'", "''" 

On the other hand, if you specify it twice, then PowerShell interpolates the string before it performs any replacements on it, so if it contains any special characters, they are interpreted before they can be escaped by the replacement. I searched high and low for other ways to quote inline string strings (something equivalent to perl q // in which nothing needs to be escaped, but a separator of your choice), but doesn't seem to be anything.

So, the only remaining solution is to use a string here, which requires a multi-line argument. This is difficult in a batch file, but it can be done:

 setlocal EnableDelayedExpansion set LF=^ set pscommand=G:\test.ps1 -name @'!LF!!x!!LF!'@ -data '!y!' C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "!pscommand!" 
  • It is assumed that x and y were installed earlier in the batch file. If your application can only send the single-line command to the cmd command, you need to add this to the batch file by adding the following two lines at the beginning:

     set x=%~1 set y=%~2 

    Then call the batch file as follows:

     path\test.bat "%x%" "%y%" 

    ~ removes quotation marks associated with command line arguments. You need quotes to include spaces in variables, but quotes are also added to the value of the variable. The package is so dumb.

  • Two blank lines are required following set LF=^ .

This applies to single quotes, which also interpret all other characters in the x value literally, with one exception: double quotes. Unfortunately, if double quotes can be part of the value, as indicated in the comment, I donโ€™t think the problem can be overcome without using a third-party utility. The reason is that, as mentioned above, the package does not have its own way of doing line replacements, and the value of x extends to cmd before PowerShell ever sees it.

WHO WHO ... GOOD QUESTION !!


UPDATE:

In fact, it turns out that you can perform static string replacements in cmd. Duncan added an answer that shows how to do this. This is a bit confusing, so I will go into detail about what happens in the Duncan solution.

The idea is that %var:hot=cold% expands to the value of the var variable, with all occurrences of hot being replaced by cold :

 D:\Scratch\soscratch>set var=You're such a hot shot! D:\Scratch\soscratch>echo %var% You're such a hot shot! D:\Scratch\soscratch>echo %var:hot=cold% You're such a cold scold! 

So, in the command (modified from Duncan's answer to align with the OP example, for clarity):

 powershell G:\test.ps1 -name '%x:'=''%' -data '%y:'=''%' 

all occurrences of ' in the variables x and y are replaced by '' , and the command expands to

 powershell G:\test.ps1 -name 'a''b' -data 'c''d' 

Let break the key element of this, '%x:'=''%' :

  • The two ' at the beginning and at the end are explicit external quotes passed to PowerShell to quote the argument, that is, the same single quotes that the OP had around %x
  • :'='' is a string replacement indicating that ' should be replaced with ''
  • %x:'=''% expands to the value of the variable x with the replacement ' by '' , which is equal to a''b
  • So the whole thing expands to 'a''b'

This solution avoids single quotes in the variable value is much simpler than my workaround above. However, the OP indicated in the update that the variable might also contain double quotes, and so far this solution still does not pass double quotes to x in PowerShell - those that are still removed with cmd before PowerShell receives the command.

The good news is that with the cmd string replacement method this becomes irresistible. Run the following cmd commands after the initial x value has already been set:

  • Replace ' with '' to avoid single quotes for PowerShell:

     set x=%x:'=''% 
  • Replace " with \" to avoid double quotes for cmd:

     set x=%x:"=\"% 

    The order of these two assignments does not matter.

  • Now, the PowerShell script can be called using the syntax that OP used in the first place (the path to powershell.exe was deleted so that it matches all on the same line):

     powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'" 

Again, if the application can send only a single-line command in cmd, these three commands can be placed in a batch file, and the application can call the batch file and pass the variables as shown above (the first bullet in my original answer).

It is interesting to note that if replacing " by \" is performed in string mode, and not using a separate set command, you will not escape " in string, even if they are inside a string with two quotes, for example:

 powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:"=\"' -data '%y'" 

... not :

 powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:\"=\\"' -data '%y'" 
+11
source

Itโ€™s a little incomprehensible to me in the question whether %x and %y CMD variables (in this case you should use %x% to replace it or to replace it in another application.

You need to avoid the single quote that you pass to PowerShell by doubling it at the command line CMD.EXE. You can do this by replacing any quotation marks in a variable with two single quotation marks.

For instance:

 C:\scripts>set X=a'b C:\scripts>set Y=c'd C:\scripts>powershell .\test.ps1 -name '%x:'=''%' '%y:'=''%' Name is 'a'b' Data is 'c'd' 

where test.ps1 contains:

 C:\scripts>type test.ps1 param($name,$data) write-output "Name is '$name'" write-output "Data is '$data'" 

If the command line you specify is generated in an external application, you can still do this by first assigning the line to a variable and using & to separate the commands (be careful to avoid spaces in the set command).

 set X=a'b& powershell .\test.ps1 -name '%x:'=''%' 

The CMD shell supports both a simple form of substitution and a way to extract substrings when changing variables. They only work when substituting into a variable, so if you want to make several substitutions at the same time or extract the substitution and substring, you need to do it one at a time, setting the variables for each step.

 Environment variable substitution has been enhanced as follows: %PATH:str1=str2% would expand the PATH environment variable, substituting each occurrence of "str1" in the expanded result with "str2". "str2" can be the empty string to effectively delete all occurrences of "str1" from the expanded output. "str1" can begin with an asterisk, in which case it will match everything from the beginning of the expanded output to the first occurrence of the remaining portion of str1. May also specify substrings for an expansion. %PATH:~10,5% would expand the PATH environment variable, and then use only the 5 characters that begin at the 11th (offset 10) character of the expanded result. If the length is not specified, then it defaults to the remainder of the variable value. If either number (offset or length) is negative, then the number used is the length of the environment variable value added to the offset or length specified. %PATH:~-10% would extract the last 10 characters of the PATH variable. %PATH:~0,-2% would extract all but the last 2 characters of the PATH variable. 
+3
source

I believe you can avoid this with ^ :

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name ^'%x^' -data ^'%y^'"

+1
source

Try encapsulating your random variable in single quotes inside a pair of double quotes to avoid this problem.

 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '"%x'" -data '"%y'"" 

The problem is that you used single quotes, and random extra single quotes that appear inside single quotes are fooling PowerShell. This should not happen if you use a double quote with backticks, since a single quote does not throw away anything inside the double quotes, and backticks will allow you to double / double quote.

0
source

Just for your information, I had some problems with the robocopy command in powershell, and I wanted to exclude the folder with a single quote in the name (and the back quote did not help, and ps and robocopy did not work with a double quote); So, I solved this by simply making a variable for the folder with a quote in it:

 $folder="John Doe stuff" robocopy c:\users\jd\desktop \\server\folder /mir /xd 'normal folder' $folder 
0
source

All Articles