Start-Process с PowerShell.exe демонстрирует другое поведение со встроенными одинарными и двойными кавычками.

Во-первых, если кто-то задается вопросом, почему мы вызываем PowerShell таким образом, я столкнулся с этим поведением с более сложной командой, которую мы создавали, но это поведение можно продемонстрировать на более простом примере, как показано ниже. На практике мы запускаем команду в 32-разрядной оболочке PowerShell от имени администратора с дополнительными переменными, отображаемыми в строке (поэтому я просто не использую одинарные кавычки для внешней части), но это, похоже, не влияет на поведение ниже.


Когда я вызываю PowerShell через Start-Process, я получаю странное поведение, если использую одинарные кавычки, окружающие параметр -Command исполняемого файла PowerShell. Например:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command 'ping google.com'"

просто отображает ping google.com в качестве вывода и завершает работу. Однако, если я использую вложенные двойные кавычки вместо одинарных, как показано ниже:

Start-Process -FilePath Powershell.exe -ArgumentList "-Command `"ping google.com`""

ping запускается и производит ожидаемый результат:

Проверка связи с google.com [173.194.78.113] с 32 байтами данных:

Ответ от 173.194.78.113: байты = 32 время = 34 мс TTL = 45

Ответ от 173.194.78.113: байты = 32 время = 33 мс TTL = 45

Ответ от 173.194.78.113: байты = 32 время = 35 мс TTL = 45

Ответ от 173.194.78.113: байты = 32 время = 32 мс TTL = 45

Статистика пинга для 173.194.78.113:

Пакетов: отправлено = 4, принято = 4, потеряно = 0 (потеря 0%),

Приблизительное время в оба конца в миллисекундах:

Минимум = 32 мс, максимум = 35 мс, средний = 33 мс

Почему командная строка просто отображается как есть, а не выполняется, если я использую одинарные кавычки для параметра -Command вместо двойных кавычек?


person Bender the Greatest    schedule 08.04.2020    source источник


Ответы (3)


Полезный ответ js2010 верен в том смысле, что использование Start-Process является случайным для вашего вопроса и что поведение специфично для CLI PowerShell (_ 2_ для Windows PowerShell и _ 3_ для PowerShell [Core] 6+):

В Windows [1] необходимо учитывать два уровня оценки:

  • (a) Первоначальный разбор командной строки на аргументы.

  • (b) Оценка (объединенных пробелами) результирующих аргументов как код PowerShell из-за использования параметра CLI -Command (-c).

Re (a):

Как и большинство консольных программ в Windows, PowerShell распознает только " символов. (двойные кавычки) - также не ' (одинарные кавычки) - как имеющие синтаксическую функцию. [2]

  • То есть, если только " chars. экранированы, они являются разделителями строк, которые сами удаляются во время синтаксического анализа.

  • Как следует из вышеизложенного, ' символов. не удаляются.

Какие бы аргументы ни были результатом - массив, возможно, "-разделенных токенов - конкатенирован с одним пробелом между ними, который становится входом для (b).


Чтобы объяснить это в контексте вашего примера, с Start-Process, убранным с картинки:

Примечание. Следующее применимо к вызову из cmd.exe или любого контекста, в котором нет оболочки (включая Start-Process и Windows Run (WinKey -R) диалог). Напротив, PowerShell повторно заключает командную строку за кулисами, чтобы при необходимости всегда использовать ".
Иными словами: к командным строкам < em> в формате PowerShell.

Одиночная команда в кавычках:

# Note: This *would* work for calling ping if run from 
#       (a) PowerShell itself or (b) from a POSIX-like shell such as Bash.
#       However, via cmd.exe or any context where *no* shell is involved,
#       notably Start-Process and the Windows Run dialog, it does not.
powershell -Command 'ping google.com'
  • (а) приводит к тому, что PowerShell находит следующие два дословных аргумента: 'ping и google.com'

  • (b) объединяет эти дословные аргументы в форму 'ping google.com' [2] и выполняет этот как код PowerShell, поэтому выводит содержимое этого строкового литерала, ping google.com

Двойная команда в кавычках:

powershell -Command "ping google.com"
  • (a) приводит к тому, что PowerShell удаляет синтаксические " символы, обнаруживая следующий единственный дословный аргумент: ping google.com

  • (b) затем приводит к тому, что этот дословный аргумент - ping google.com - выполняется как код PowerShell, что, следовательно, приводит к вызову команды, а именно к ping исполняемому файлу с аргументом google.com.


[1] На Unix-подобных платформах первый уровень не применяется, потому что вызываемые программы всегда видят только массив дословных аргументов, а не команду строку, которую они сами должны разобрать на аргументы. Не то чтобы при вызове интерфейса командной строки PowerShell из POSIX-подобной оболочки, такой как bash на Unix-подобных платформах, именно эта оболочка распознает одинарные кавычки как разделители строк и удаляет их перед PowerShell их видит.

[2] Удивительно, но в Windows интерпретировать командную строку в конечном итоге должна каждая отдельная программа, а некоторые действительно предпочитают также распознавать ' как разделители строк ( например, Ruby). Однако многие программы в Windows, включая саму PowerShell, основаны на среде выполнения C, которая распознает только ".

[3] В стороне, обратите внимание, что это означает, что имеет место нормализация пробелов: то есть
powershell -Command 'ping google.com' в равной степени приведет к 'ping google.com'.

person mklement0    schedule 09.04.2020
comment
Спасибо за подробное объяснение. Я не осознавал, что командная строка с одинарными кавычками переносится в PowerShell, или, скорее, именно так вызов команды обрабатывает строки с одиночными кавычками в самой Windows и влияет на большинство точек входа - я всегда предполагал, что это всего лишь раздражающая причуда cmd. - person Bender the Greatest; 09.04.2020
comment
На самом деле мои примеры запускаются внутри самой PowerShell. Двойные кавычки внутри одинарных кавычек, похоже, обладают волшебным свойством, которое запускает команду, в то время как одиночные кавычки внутри двойных кавычек обрабатывают ее как строку для эха. - person js2010; 09.04.2020
comment
Рад слышать, что это было полезно, @BendertheGreatest. На самом деле это не Windows - каждая программа должна анализировать необработанную командную строку, но многие программы построены на среде выполнения C, которая распознает только " - см. Сноску [2], которую я только что добавил. - person mklement0; 09.04.2020
comment
@ js2010, пожалуйста, прочтите мое пояснение относительно вызова из cmd.exe или из контекста, когда используется no оболочка. PowerShell запутывает картину, выполняя повторное цитирование с " за кулисами. На платформах, отличных от Windows, при вызове из POSIX-подобной оболочки, такой как bash, именно эта оболочка распознает одинарные кавычки. Лучше не использовать дополнительную оболочку (кроме cmd.exe) во избежание путаницы. - person mklement0; 09.04.2020

Мы можем отбросить start-process отсюда. Кажется правдой, что встроенные двойные и одинарные кавычки по-разному обрабатываются исполняемым файлом powershell. Start-process не имеет отношения к этому поведению. Так PowerShell ведет себя в командной строке. Я не вижу способа это изменить. Вы также можете выполнить "start-job -runas32", но он не будет повышен. Другой вариант - это параметр PowerShell «-file» вместо «-command». Эти примеры запускаются из ядра Osx (unix) powershell:

pwsh -c "'ping -c 1 google.com'"

ping -c 1 google.com


pwsh -c "`"ping -c 1 google.com`""

PING google.com (172.217.10.110): 56 data bytes
64 bytes from 172.217.10.110: icmp_seq=0 ttl=52 time=20.020 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 20.020/20.020/20.020/0.000 ms


pwsh -c '"ping -c 1 google.com"'        
PING google.com (172.217.6.206): 56 data bytes
64 bytes from 172.217.6.206: icmp_seq=0 ttl=52 time=22.786 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 22.786/22.786/22.786/0.000 ms
person js2010    schedule 08.04.2020
comment
Итак, прочтите мой первый абзац. Это упрощенный пример, но на самом деле для этого нам нужно запустить 32-разрядную оболочку PowerShell с повышенными привилегиями, поэтому мы вызываем SysWOW64 вариант PowerShell, используя -Verb RunAs аргумент Start-Process. В противном случае я согласен, что использование Start-Process будет излишним. - person Bender the Greatest; 09.04.2020
comment
Что касается вашего обновления, я уже установил, что существует разница между одинарными кавычками и двойными кавычками в точке входа powershell. Но я ищу более конкретный ответ, а не просто так. Такое поведение кажется неправильным, учитывая, что, за исключением нескольких нюансов между строками в двойных кавычках и строками в одинарных кавычках в PowerShell, оба являются строками, поэтому я ожидаю, что любой вариант будет выполняться независимо от того, является ли он строковым или строковым литералом. - person Bender the Greatest; 09.04.2020

В справке по powershell.exe ничего не упоминается о поддержке одинарных кавычек ... зачем вам их использовать?

-Command
    Executes the specified commands (and any parameters) as though they were
    typed at the Windows PowerShell command prompt, and then exits, unless
    NoExit is specified. The value of Command can be "-", a string. or a
    script block.

    If the value of Command is "-", the command text is read from standard
    input.

    If the value of Command is a script block, the script block must be enclosed
    in braces ({}). You can specify a script block only when running PowerShell.exe
    in Windows PowerShell. The results of the script block are returned to the
    parent shell as deserialized XML objects, not live objects.

    If the value of Command is a string, Command must be the last parameter
    in the command , because any characters typed after the command are
    interpreted as the command arguments.

    To write a string that runs a Windows PowerShell command, use the format:
        "& {<command>}"
    where the quotation marks indicate a string and the invoke operator (&)
    causes the command to be executed.
person thepip3r    schedule 08.04.2020
comment
Он не говорит, что не поддерживает их: The value of Command can be "-", a string. or a script block. И одинарные, и двойные кавычки создают строки, и пока я не ожидаю, что переменные будут отображаться внутри одинарных кавычек во время выполнения , или символы, которые нужно экранировать, здесь не должно быть различий в поведении, как это задокументировано (очевидно, на практике есть что-то другое, о чем я спрашиваю). - person Bender the Greatest; 08.04.2020
comment
да, я не могу вам ответить. Но из-за того, что одинарные кавычки всегда интерпретируются как литералы, кажется логичным, что это был бы естественный исключительный случай. Если вам не нравится обратный тик, я уверен, что удвоение двойных кавычек тоже работает - person thepip3r; 08.04.2020
comment
Я согласен с тем, что одинарные кавычки интерпретируются как буквальные строки .... но если ожидается, что -Command выполнит строку, я бы ожидал, что строка будет отображаться как буквальная строка, а затем выполняться. Мне это не кажется естественным поведением. - person Bender the Greatest; 08.04.2020
comment
Меня всегда сбивали с толку причуды PowerShell с запуском сторонних двоичных файлов из командной строки. & {}, '',. \, похоже, существует какое-то очень непоследовательное поведение или, по крайней мере, нет четких указаний по передаче параметров другим исполняемым файлам - особенно когда они сами требуют некоторого уровня цитат ... - person thepip3r; 08.04.2020