Почему invoke operator (&) и Invoke-Expression дают разные результаты для одного и того же ввода? - PullRequest
0 голосов
/ 25 апреля 2018

Насколько я понимаю, оператор invoke (&) и командлет Invoke-Expression должны вести себя одинаково.Однако, как можно видеть ниже, это не тот случай:

PS C:\Users\admin> powershell -Command "& {""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsb
G93b3JsZCc=')))""}"
echo 'helloworld'

PS C:\Users\admin> powershell -Command "IEX ""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVs
bG93b3JsZCc=')))"""
helloworld

Здесь 'ZWNobyAnaGVsbG93b3JsZCc=' - это строка в кодировке Base64 "echo helloworld".

Может кто-нибудь уточнить?

1 Ответ

0 голосов
/ 25 апреля 2018

Invoke-Expression (со встроенным псевдонимом iex) и &, оператор вызова , служат различным целям:

  • Invoke-Expression оценивает данную строку как исходный код PowerShell , как если бы вы выполнили содержимое строки непосредственно как команду.

    • Как таковой, он аналогичен eval в bash и, следовательно, только для использования с входом, который полностью находится под контролем вызывающего абонента или входом, которому вызывающий абонент доверяет .

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

  • & используется для вызова команды (& <nameOrPath> [...]) или блока сценария (& { ... } [...]) :

    • Ни один из случаев не предполагает оценку строки в качестве исходного кода.

В данном случае:

ядром вашей команды является следующее выражение, котороеich возвращает string
"echo 'helloworld'" (его содержимое не включает в себя " - это просто представление результирующей строки в виде строкового литерала PowerShell):

[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsbG93b3JsZCc='))

Также обратите внимание, что из-за синтаксического анализа командной строки ""..."", окружающий основное выражение в ваших исходных командах, эффективно игнорируется , что объясняет, почему выражение выполняется вместо того, чтобы рассматриваться как содержимое строки. [1]

Следовательно, ваши две команды составляют:

  • & { "echo 'helloworld'" }

    • & выполняет оператор внутри блока скрипта, который является строкой и самой строкой - если он не назначенпеременная или перенаправленная в другое место - это просто вывод как есть.
      В этом случае команда фактически такая же, как и само выполнение "echo 'helloworld'" (, включая включающий", о котором вы можете думать как
      echo "echo 'helloworld'"), поэтому echo 'helloworld' выводит на консоль.

    • Обратите внимание, что echo является встроенным псевдонимом для командлета Write-Output, для которого явное используетсяредко требуется: возвращаемые значения из команд или выражений выводятся неявно , если они не захвачены в какой-либо форме, как в этом случае, когда выполнение строки как оператора просто выводит строку.(Вы можете попробовать это, отправив, например, 'hi' в приглашении).

  • iex "echo 'helloworld'"

    • Этозаставляет iex (Invoke-Expression) оценивать содержимое строки как исходный код, который поэтому выполняет echo 'helloworld', который выводит helloworld на консоль.

[1] Необязательное чтение: проблемы цитирования PowerShell при вызове внешних программ

Примечание:

  • Обработка цитирования по отношению к внешним программам или при вызове из внешних программНасколько я могу судить, не является частью официальной документации (на момент написания статьи ни about_Parsing , ни about_Quoting_Rules , ни about_Special_Characters не упоминали об этом - я открыл эта проблема на GitHub для решения этой проблемы).

  • Существуют недостатки в существующей обработке, но они не могут быть исправлены без нарушения обратной совместимости.

  • При звонке из PowerShellПервый подход заключается в использовании блока сценариев , который обходит проблемы цитирования - см. ниже.

Даже если вы правильно встраивали ", экранируя их как "" внутри общей строки "..." из PowerShell- внутренняя перспектива, требуется дополнительное экранирование " с \ для передачи их во внешнюю программу , даже если эта внешняя программа является другим экземпляром PowerShell, вызываемым черезpowershell.exe.

Возьмите этот упрощенный пример:

powershell.exe -command " ""hi"" "  # !! BROKEN
powershell.exe -command ' "hi" '    # !! BROKEN
  • Внутренняя оболочка PowerShell, " ""hi"" " и ' "hi" ' преобразуются в строку с буквальным содержимым "hi" , которая при выполнении печатает hi.

  • К сожалению, PowerShell передает эту строку в powershell.exe как " "hi" " - обратите внимание, как "" превратился в обычный ", и включающие одинарные кавычки были заменены двойными кавычками -что фактически приводит к hi после анализа новым экземпляром (потому что " "hi" " анализируется как конкатенация подстрок " ", hi и " "), поэтому PowerShell завершает попыткувыполнить (предположительно несуществующую) команду с именем hi.

В отличие от этого, если вам удастся передать внедренное значение как " как \" (sic) - после удовлетворения собственных потребностей PowerShell в экранировании - команда работает так, как задумано.Поэтому, как указано, вам необходимо объединить внутреннее экранирование PowerShell с экранированием для CLI, чтобы пропустить встроенный ", чтобы:

  • внутри в целом "...", каждый встроенный " должен быть экранирован как \"" (sic) или \`" (sic)
  • внутри в целом '...', \" можно использоватькак есть.
powershell.exe -command " \""hi\"" " # OK
powershell.exe -command " \`"hi\`" " # OK
powershell.exe -command ' \"hi\" '   # OK

В качестве альтернативы используйте блок сценариев вместо команды string , которая обходит кавычкиголовные боли:

powershell.exe -command { "hi" } # OK, but only works when calling from PS

Обратите внимание, что техника блока сценариев работает только при вызове из PowerShell , а не из cmd.exe.


cmd.exe имеет свои собственные требования цитирования: В частности, cmd.exe поддерживает только "" для встраивания двойных кавычек (не также `");таким образом, среди приведенных выше решений только
powershell.exe -command " \""hi\"" " работает из cmd.exe (пакетный файл) без дополнительного экранирования.

Недостатком \"", однако, является то, что пробеги внутреннего пробела между \""...\"" сворачиваются в одно пространство каждый.Чтобы избежать этого, используйте \"...\", но cmd.exe затем видит подстроки между экземплярами \" как без кавычек , что приведет к разрыву команды, если эта подстрока содержит метасимволы, такие как | или &;например, powershell.exe -command " \"a|b\" ";чтобы исправить это, вы должны индивидуально ^ -создать следующие символы: & | < > ^

powershell.exe -command ' "hi" ' аналогично хрупким, поскольку cmd.exe не распознает ' как разделитель строк, поэтому любые метасимволывнешние вложенные "..." снова интерпретируются самим cmd.exe;например, powershell.exe -command ' "hi" | Measure-Object '

Наконец, используя "" из cmd.exe для встраивания " иногда работает, но не надежно ;например, powershell.exe -command " 'Nat ""King"" Cole' " печатает Nat "King Cole (закрывающий " отсутствует).
Кажется, это было исправлено в PowerShell Core .

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