PowerShell извлекает двойные кавычки из аргументов командной строки - PullRequest
40 голосов
/ 16 июля 2011

Недавно у меня возникли некоторые проблемы с использованием GnuWin32 из PowerShell, когда используются двойные кавычки.

При дальнейшем исследовании кажется, что PowerShell извлекает двойные кавычки из аргументов командной строки, даже при правильном экранировании.

PS C:\Documents and Settings\Nick> echo '"hello"'
"hello"
PS C:\Documents and Settings\Nick> echo.exe '"hello"'
hello
PS C:\Documents and Settings\Nick> echo.exe '\"hello\"'
"hello"

Обратите внимание, что двойные кавычки присутствуют при передаче в echo командлет PowerShell, но когда они передаются в качестве аргумента echo.exe , двойные кавычки удаляются, если их не экранировать обратной косой чертой (даже несмотря на то, что экранирующий символ PowerShell является обратным слэком, а не обратным слешем).

Мне это кажется ошибкой. Если я передаю правильные экранированные строки в PowerShell, то PowerShell должен позаботиться о том, что экранирование может понадобиться, однако при этом он вызывает команду.

Что здесь происходит?

На данный момент исправление состоит в том, чтобы экранировать аргументы командной строки в соответствии с этими правилами (которые, кажется, используются вызовом API CreateProcess, который PowerShell использует для вызова файлов .exe):

  • Чтобы передать двойную кавычку, используйте обратную косую черту: \" -> "
  • Чтобы пройти одну или несколько обратных косых черт с последующей двойной кавычкой, избегайте каждой обратной косой черты с другой обратной косой чертой и избегайте кавычки: \\\\\" -> \\"
  • Если за ними не следует двойная кавычка, для обратной косой черты не требуется экранирование: \\ -> \\

Обратите внимание, что дальнейшее экранирование двойных кавычек может потребоваться для экранирования двойных кавычек в экранированной строке Windows API в PowerShell.

Вот несколько примеров с echo.exe от GnuWin32:

PS C:\Documents and Settings\Nick> echo.exe "\`""
"
PS C:\Documents and Settings\Nick> echo.exe "\\\\\`""
\\"
PS C:\Documents and Settings\Nick> echo.exe "\\"
\\

Я полагаю, что это может быстро превратиться в ад, если вам нужно передать сложный параметр командной строки. Конечно, ничего из этого не описано в документации CreateProcess() или PowerShell.

Также обратите внимание, что нет необходимости передавать аргументы с двойными кавычками в функции .NET или командлеты PowerShell. Для этого вам нужно только избежать двойных кавычек в PowerShell.

Ответы [ 4 ]

31 голосов
/ 16 июля 2011

Это известная вещь :

СЛИШКОМ СЛОЖНО передать параметры в приложения, которым требуются строки в кавычках.Я задал этот вопрос в IRC «просторным» экспертам PowerShell, и кому-то понадобился час, чтобы найти способ (я изначально начал публиковать здесь, что это просто невозможно).Это полностью нарушает способность PowerShell служить в качестве оболочки общего назначения, потому что мы не можем делать простые вещи, такие как выполнение sqlcmd.Задача номер один в командной оболочке должна запускать приложения командной строки ... Например, при попытке использовать SqlCmd из SQL Server 2008 существует параметр -v, который принимает ряд параметров name: value.Если в значении есть пробелы, вы должны заключить его в кавычки ...

... нет единого способа написать командную строку для правильного вызова этого приложения, поэтому даже после того, как вы освоите все 4 или 5 различныхспособы цитирования и экранирования, вы все еще угадываете, что сработает, когда ... или вы можете просто раскошелиться на cmd и покончить с этим.

13 голосов
/ 15 октября 2012

Лично я избегаю использования «\» для экранирования вещей в PowerShell, потому что технически это не экранирующий символ оболочки. Я получил непредсказуемые результаты с ним. В строках с двойными кавычками вы можете использовать "", чтобы получить встроенную двойную кавычку, или экранировать ее обратной галочкой:

PS C:\Users\Droj> "string ""with`" quotes"
string "with" quotes

То же самое касается одинарных кавычек:

PS C:\Users\Droj> 'string ''with'' quotes'
string 'with' quotes

Странная вещь при отправке параметров во внешние программы заключается в том, что существует дополнительный уровень оценки котировок. Я не знаю, является ли это ошибкой, но я предполагаю, что она не изменится, потому что поведение при запуске Start-Process и передаче аргументов одинаково. Start-Process принимает массив для аргументов, что делает вещи более понятными с точки зрения того, сколько аргументов фактически отправляется, но эти аргументы, кажется, оцениваются в дополнительное время.

Итак, если у меня есть массив, я могу установить в значениях arg встроенные кавычки:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> echo $aa
arg="foo"
arg=""""bar""""

Аргумента 'bar' достаточно, чтобы покрыть дополнительную скрытую оценку. Это как если бы я отправил это значение командлету в двойных кавычках, а затем снова отправил этот результат в двойных кавычках:

PS C:\cygwin\home\Droj> echo "arg=""""bar""""" # level one
arg=""bar""
PS C:\cygwin\home\Droj> echo "arg=""bar""" # hidden level 
arg="bar"

Можно ожидать, что эти аргументы будут передаваться во внешние команды как есть, как и в командлеты типа 'echo' / 'write-output', но это не так, из-за этого скрытого уровня:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> start c:\cygwin\bin\echo $aa -nonew -wait
arg=foo arg="bar"

Я не знаю точную причину этого, но поведение таково, как будто есть еще один, недокументированный шаг, предпринимаемый под крышками, который повторно анализирует строки. Например, я получаю тот же результат, если отправляю массив в командлет, но добавляю уровень синтаксического анализа, выполняя его через invoke-expression:

PS C:\cygwin\home\Droj> $aa = 'arg="foo"', 'arg=""""bar""""'
PS C:\cygwin\home\Droj> iex "echo $aa"
arg=foo
arg="bar"

... именно это я и получаю, отправляя эти аргументы моему внешнему cygwin 'echo.exe':

PS C:\cygwin\home\Droj> c:\cygwin\bin\echo 'arg="foo"' 'arg=""""bar""""'
arg=foo arg="bar"
2 голосов
/ 20 декабря 2017

Полагаться на CMD, чтобы скрыть проблему, как указано в принятом ответе, у меня не получилось, поскольку двойные кавычки по-прежнему отбрасывались при вызове исполняемого файла CMD.

Хорошим решением для меня было структурировать мою командную строку как массив строк вместо одной полной строки, содержащей все аргументы.Затем просто передайте этот массив в качестве аргументов для двоичного вызова:

$args = New-Object System.Collections.ArrayList
$args.Add("-U") | Out-Null
$args.Add($cred.UserName) | Out-Null
$args.Add("-P") | Out-Null
$args.Add("""$($cred.Password)""")
$args.Add("-i") | Out-Null
$args.Add("""$SqlScriptPath""") | Out-Null
& SQLCMD $args

В этом случае двойные кавычки, окружающие аргументы, правильно передаются вызываемой команде.

Если вам нужно, вы можетепротестируйте и отладьте его с помощью EchoArgs из Расширений сообщества PowerShell

2 голосов
/ 31 октября 2017

Это, похоже, исправлено в последних версиях PowerShell на момент написания этой статьи, поэтому больше не о чем беспокоиться.

Если вы все еще думаете, что видите эту проблему, помните, что она может бытьк чему-то другому, например к программе, которая вызывает PowerShell, поэтому, если вы не можете воспроизвести ее при вызове PowerShell напрямую из командной строки или ISE , вам следует выполнить отладку в другом месте.Я обнаружил этот вопрос при исследовании проблемы исчезновения кавычек при запуске сценария PowerShell из кода C # с использованием Process.Start.На самом деле проблема была в C #. Для запуска процесса нужны аргументы с двойными кавычками - они исчезают .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...