Экранирование одинарной кавычки из командной строки для PowerShell
у меня есть приложение Windows и на события, он вызывает команду, как это:
C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -ExecutionPolicy Bypass "G:test.ps1 -name '%x' -data '%y'"
параметр name иногда '
в нем. Возможно ли каким-то образом избежать этого?
4 ответов
это на самом деле намного сложнее, чем вы думаете. Экранирование вложенных кавычек в строках, передаваемых из cmd в PowerShell, является серьезной головной болью. Что делает это особенно сложным, так это то, что вам нужно сделать замену в переменной, расширенной cmd в приведенном аргументе, переданном powershell.exe в одинарном аргументе, переданном параметру сценария PowerShell. AFAIK cmd не имеет собственных функций даже для базовых замен строк, поэтому вам нужно PowerShell для выполнения замена для вас.
если аргумент -data paramater (тот, который содержится в переменной cmd x) не обязательно должен быть одинарным, самое простое, что нужно сделать, это дважды процитировать его, так что одинарные кавычки в пределах значения x вообще не нужно убегать. Я говорю "самый простой", но даже это немного сложно. Как указал Василий Сиракис,^
обычно является escape-символом в cmd, но для избежания двойного кавычки в (двойной)цитируемой строке, вам нужно использовать \
. Таким образом, вы можете написать свою командную команду следующим образом:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name \"%x%\" -data '%y%'"
который передает следующую команду PowerShell:
G:\test.ps1 -name "value of x, which may contain 's" -data 'value of y'
Если, однако, x также могут содержать символы, являющиеся специальными символами в интерполированных строках PowerShell ("
, $
или `
), то он становится много сложнее. Проблема это %x - это переменная cmd, которая расширяется cmd до того, как PowerShell сможет ее коснуться. Если вы процитируете его в команде, которую передаете powershell.exe и содержит одну кавычку, затем вы даете сеансу PowerShell строку, которая завершается рано, поэтому PowerShell не имеет возможности выполнять какие-либо операции над ней. Следующее, очевидно, не работает, потому что -заменить оператору необходимо указать допустимую строку прежде чем вы сможете что-либо заменить:
'foo'bar' -replace "'", "''"
с другой стороны, если вы дважды процитируете его, PowerShell интерполирует строку перед выполнением каких-либо замен на ней, поэтому, если она содержит какие-либо специальные символы, они интерпретируются до того, как они могут быть экранированы заменой. Я искал повсюду другие способы цитировать литеральные строки inline (что-то эквивалентное perl q//, в котором ничего не нужно избегать, кроме разделителя по вашему выбору), но кажется, ничего нет.
Итак, единственное оставшееся решение-использовать строку here, которая требует многострочного аргумента. Это сложно в пакетном файле, но это можно сделать:
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!"
-
это предполагает, что x и y были установлены ранее в пакетный файл. Если ваше приложение может отправить в cmd только однострочную команду, вам нужно будет поместить вышеизложенное в пакетный файл, добавив следующие две строки в начало:
set x=%~1 set y=%~2
затем вызовите пакетный файл следующим образом:
path\test.bat "%x%" "%y%"
на
~
удаляет кавычки, окружающие аргументы командной строки. Вам нужны кавычки, чтобы включить пробелы в переменные, но кавычки также добавляются к значению переменной. Партия глупа в этом смысле. две пустые строки после есть.
это заботится о одинарных кавычках, которые также интерпретация всех других символов в значении x буквально, за одним исключением: двойные кавычки. К сожалению, если двойные кавычки могут быть частью значения, как вы указали в комментарии, я не считаю, что проблема решается без использования сторонней утилиты. Причина в том, что, как упоминалось выше, batch не имеет собственного способа выполнения замен строк и значение x расширяется cmd, прежде чем PowerShell когда-либо видит он.
кстати... ХОРОШИЙ ВОПРОС!!
обновление:
на самом деле, оказывается, что это is возможно выполнять статические замены строк в cmd. Дункан добавил ответ, который показывает, как это сделать. Это немного запутанно, поэтому я расскажу о том, что происходит в решении Дункана.
идея в том, что %var:hot=cold%
расширяется до значения переменной var, С случаи hot
заменить 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!
Итак, в команде (измененной из ответа Дункана для согласования с примером OP, для ясности):
powershell G:\test.ps1 -name '%x:'=''%' -data '%y:'=''%'
все вхождения '
в переменных x и y заменены ''
, и команда расширяется до
powershell G:\test.ps1 -name 'a''b' -data 'c''d'
давайте разберем ключевой элемент этого,'%x:'=''%'
:
- два
'
s при начало и конец-это явные внешние кавычки, передаваемые PowerShell для цитирования аргумента, т. е. те же одинарные кавычки, которые были у OP вокруг%x
-
:'=''
является заменой строки, указывающей, что'
следует заменить на''
-
%x:'=''%
расширяется до значения переменной x С'
заменить на''
, которая составляетa''b
- таким образом, все это расширяется до
'a''b'
это решение избегает одинарных кавычек в значении переменной намного проще, чем мой обходной путь выше. Однако OP указал в обновлении, что переменная также может содержать двойные кавычки, и до сих пор это решение по-прежнему не передает двойные кавычки в x в PowerShell--они все еще удаляются cmd до того, как PowerShell получит команду.
хорошей новостью является то, что с помощью метода замены строки cmd это становится непреодолимым. Выполните следующие команды cmd после начального значения x уже установлен:
-
заменить
'
С''
, чтобы избежать одинарных кавычек для PowerShell:set x=%x:'=''%
-
заменить
"
С\"
, чтобы избежать двойных кавычек для cmd:set x=%x:"=\"%
порядок этих двух заданий не имеет значения.
-
теперь Сценарий PowerShell можно вызвать, используя синтаксис, который OP использовал в первую очередь (путь к powershell.exe удален, чтобы поместиться все это на одной строке):
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x' -data '%y'"
опять же, если приложение может отправить только однострочную команду cmd, эти три команды могут быть помещены в пакетный файл, и приложение может вызвать пакетный файл и передать переменные, как показано выше (первый пуля в моем исходном ответе).
один интересный момент, чтобы отметить, что если замена "
С \"
выполняется встроенным, а не отдельным set, вы не побег "
s в замене строки, даже если они находятся внутри строки с двойными кавычками, т. е. вот так:
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:"=\"' -data '%y'"
...не такой:
powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name '%x:\"=\"' -data '%y'"
я немного неясен в вопросе, является ли %x
и %y
являются переменными CMD (в этом случае вы должны использовать %x%
чтобы заменить его, или замена происходит в вашем другом приложении.
вам нужно избежать одинарной кавычки, которую вы передаете PowerShell, удвоив ее в CMD.Командная строка EXE. Вы можете сделать это, заменив любые кавычки в переменной двумя одинарными кавычками.
например:
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'
где test.ps1
содержит:
C:\scripts>type test.ps1
param($name,$data)
write-output "Name is '$name'"
write-output "Data is '$data'"
если командная строка, которую вы дали, генерируется во внешнем приложении, вы все равно сможете это сделать, сначала назначив строку переменной и используя &
для разделения команд (будьте осторожны, чтобы избежать конечных пробелов на set
command).
set X=a'b& powershell .\test.ps1 -name '%x:'=''%'
оболочка CMD поддерживает как простую форму подстановки, так и способ извлечения подстрок при замене переменных. Эти работают только при замене в переменная, поэтому, если вы хотите сделать несколько подстановок одновременно или заменить и извлечь подстроку, вам нужно сделать по одной переменной за раз с каждым шагом.
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.
Я верю, что вы можете избежать его с ^
:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name ^'%x^' -data ^'%y^'"
попробуйте инкапсулировать случайную переменную одинарной кавычки внутри пары двойных кавычек, чтобы избежать этой проблемы.
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass "G:\test.ps1 -name `"%x`" -data `"%y`""
проблема возникает, потому что вы использовали одинарные кавычки и случайные дополнительные одинарные кавычки, появляющиеся внутри одинарных кавычек дураков PowerShell. Это не должно происходить, если вы дважды цитируете с помощью backticks, так как одиночная цитата не будет ничего выбрасывать внутри двойных кавычек, а backticks позволит вам удвоить/удвоить цитату.