Powershell: Позвонить родному оператору

Созданный на 30 июн. 2020  ·  189Комментарии  ·  Источник: PowerShell/PowerShell

Постановка задачи

В настоящее время бывают случаи, когда вырезание и вставка собственной командной строки не выполняется должным образом в PowerShell. Это может быть связано с неправильным синтаксическим анализом кавычек, предназначенных для передачи встроенной команде, или использованием синтаксиса PowerShell, который не предназначен для интерпретации как PowerShell. PowerShell имеет специальный аргумент --% При использовании с собственными командами обрабатывает остальные аргументы как литералы, передаваемые собственной команде, но имеет несколько проблем:

  1. Его невозможно обнаружить, пользователи должны знать об этом специальном параметре заранее.
  2. | , && и || имеют приоритет, поэтому: wsl --% ls | less выполнит wsl ls и передаст результаты в less работает в PowerShell, а не less работает в wsl
  3. Если вы вырежете и вставите командную строку, вам нужно будет отредактировать строку, чтобы вставить --% в начало
  4. В системах Unix аргументы после -% передаются дословно без подстановки, тогда как собственные команды в Unix ожидают, что оболочка выполнит подстановку

Предлагаемые детали технической реализации

Предлагается ввести новый оператор --% (Call Native).

Любое текстовое содержимое после этого оператора будет вызывать для выполнения «оболочку по умолчанию» ОС. В Windows это будет cmd.exe, а в системах на базе Unix - / bin / sh. Это решает проблему глобализации в системах на базе Unix, а также позволяет расширение %variable% в Windows. В отличие от переключателя --% , это также означает, что | , && и || обрабатываются как часть собственной командной строки.

Это означает, что эти два функционально одинаковые:

wsl --% ls $foo `&`& echo $PWD
--% wsl ls $foo && echo $PWD

где $foo и $PWD оцениваются оболочкой в ​​WSL. Обратите внимание, что в первом примере вы должны знать, что нужно экранировать && чтобы он выполнялся в WSL, а не в PowerShell.

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

$out = --% ls *.txt
$out | select-string hello

Обратите внимание, что в отличие от текущего оператора вызова & , вы не можете использовать какой-либо синтаксис PowerShell, поэтому:

--% $commandline

не разрешает $commandline как переменную сначала PowerShell, а вместо этого передает $commandline в оболочку по умолчанию для обработки неразрешенных.

Проблема вырезания и вставки решается простой вставкой после ввода --% .

Приведенный выше пример для wsl будет выглядеть так:

--% wsl ls | less

где цель состоит в том, чтобы вся эта строка выполнялась в экземпляре WSL Linux.

Обнаруживаемость

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

Альтернативные соображения

&! и &n были предложены в качестве сигилы, но были отклонены, потому что &! является допустимым оператором на некоторых языках, что затрудняет поиск документации в Интернете. Также было опасение, что визуальный элемент, подобный оператору вызова & , будет сбивать с толку пользователей.

Возникает вопрос о поддержке продолжения строки при использовании этого нового оператора. Я бы посоветовал нам изначально не поддерживать его.

Было предложено решение cmdlet вместо операторного решения. Мы считаем, что это не решает проблему «вырезать и вставить», поскольку теперь вам нужно знать, как поместить конвейер в одинарные кавычки и / или экранировать специальные символы. Мы действительно считаем, что такой командлет, как в Invoke-NativeCommand (существительное будет определено), был бы полезен в качестве дополнительной опции вместо замены необходимости в операторе.

Связанные вопросы

Это также должно решить следующие проблемы:

https://github.com/PowerShell/PowerShell/issues/12491
https://github.com/PowerShell/PowerSHell/issues/1761

Committee-Reviewed Issue-Discussion Issue-Enhancement

Самый полезный комментарий

Я только что опубликовал модуль Native , ( Install-Module Native -Scope CurrentUser ), который я приглашаю всех вас попробовать и чьи команды я буду использовать ниже ; упоминаемые числовые варианты использования взяты из комментария @rkeithhill выше .

Если мы когда-нибудь захотим рассеять концептуальный туман, я думаю, нам нужно иначе сформулировать варианты использования.
_Ни один из них не требует изменений в парсинге_.

Пример использования [вызов собственной оболочки]:

Вы хотите выполнить _индивидуальную команду_ (вариант использования 1) или всю _многокомандную командную строку_ (вариант использования 2), написанную для собственной оболочки платформы (эта проблема):

  • Поскольку вы используете другой синтаксис оболочки, вы передаете команду (строку) _ как одну строку_ целевой оболочке, чтобы _it_ выполнял синтаксический анализ; Интерполяция строк PowerShell предлагает возможность встраивать значения PowerShell в переданную строку (вариант использования 2a).
  • ins ( Invoke-NativeShell ) охватывает эти варианты использования.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

Синтаксис здесь-строки не самый удобный (отсюда и предложение реализовать встроенный вариант - см. # 13204), но вы не _должны_ его использовать; для дословных команд вы можете использовать '...' , что требует только _doubling_ embedded ' , если он присутствует.

Кроме того, вот разумная замена оператору StackOverflow :

Если вы поместите следующий обработчик клавиш PSReadLine в свой файл $PROFILE , вы сможете использовать Alt-v для формирования вызова ins с дословной строкой в который вставляется текущий текст из буфера обмена.Enter отправляет звонок.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Пример использования [прямой вызов исполняемого файла]:

Вы хотите вызвать _один внешний исполняемый файл_, используя синтаксис _PowerShell_, с _индивидуальными аргументами_ (варианты использования 1a и 3).

  • Это основная задача любой оболочки, и жизненно важно, чтобы она работала надежно.

    • Вы должны быть уверены в том, что PowerShell сможет передавать любые аргументы, возникающие в результате _its_ синтаксического анализа _as-is_ в целевой исполняемый файл. В настоящее время это не так - см. № 1995 .
  • Это требует от вас понимания синтаксиса _PowerShell_ и метасимволов _its_ режима аргументов.

    • Вы должны знать, что ` используется как escape-символ и для продолжения строки.
    • Вы должны знать, что PowerShell имеет дополнительные метасимволы, которые требуют цитирования / экранирования для дословного использования; это (обратите внимание, что @ проблематично только как первый символ аргумента.):

      • для POSIX-подобных оболочек (например, Bash): @ { } `$ , если вы хотите предотвратить предварительное расширение с помощью PowerShell)
      • для cmd.exe : ( ) @ { } # `
      • Индивидуально ` экранирование таких символов. достаточно (например, printf %s `@list.txt ).

Пока мы ждем исправления # 1995, функция ie (for i nvoke (external) e xecutable) заполняет пробел, просто добавляя к прямому вызову; например, вместо следующего вызова:

# This command is currently broken, because the '{ "name": "foo" }' argument isn't properly passed.
curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

вы бы использовали следующее:

# OK, thanks to `ie`
ie curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

В модуле Native есть что-то вроде echoArgs.exe , команды dbea ( Debug-ExecutableArguments ); пример вывода в Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Используя переключатель -UseIe ( -ie ), вы можете указать dbea передать аргументы через ie , что демонстрирует, что он устраняет проблемы:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Примечание. После исправления # 1995 ie больше не требуется; в интересах прямой совместимости ie предназначен для обнаружения и автоматического откладывания исправления; то есть, как только исправление будет внесено, ie перестанет применять свои обходные пути и будет эффективно действовать как прямой вызов / & .

Пример использования [прямой вызов вредоносного исполняемого файла Windows]:

Есть две основные проблемы, которые возникают только в _Windows_:

  • Вы вызываете «мошеннический» исполняемый файл, требования к цитированию которого отличаются от наиболее широко используемых соглашений ; например, msiexec.exe и msdeploy.exe требуют, чтобы prop="<value with spaces>" цитировалось именно таким образом - цитирование только вокруг _value_ части - даже если "prop=<value with spaces>" - цитирование всего аргумент - _должен_ быть эквивалентным (последнее - то, что PowerShell вполне оправданно делает за кулисами).

  • Вы вызываете _ пакетный файл_ с _ аргументом, который не содержит пробелов, но содержит метасимволы cmd.exe например & , ^ или | ; например:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - оправданно - передает http://example.org/a&b _unquoted_ (поскольку нет встроенных пробелов), но это прерывает вызов пакетного файла, потому что cmd.exe - необоснованно - передает аргументы, переданные пакетному файлу, в такое же правило синтаксического анализа, которое будет применяться внутри cmd.exe вместо того, чтобы принимать их как литералы.

    • Примечание. Хотя использование пакетных файлов для _directly_ реализации функций, вероятно, сокращается, использование их в качестве _CLI точек входа_ по-прежнему очень распространено, например, Azure az CLI, который реализован как пакетный файл az.cmd .

Примечание. ie автоматически обрабатывает эти сценарии для командных файлов и для msiexec.exe и msdeploy.exe , поэтому для вызовов _most_ не требуется дополнительных усилий ; однако невозможно предвидеть _все_ "мошеннические" исполняемые файлы.

Есть два способа решить эту проблему:

  • Учитывая, что cmd.exe , после применения своей собственной интерпретации к аргументам в командной строке _должно_ сохранить указанное цитирование, вы можете делегировать вызов ins в Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Если вы используете строку _expandable_, это снова позволяет вам встраивать значения PowerShell в командную строку.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

  • В качестве альтернативы используйте текущую реализацию --% , но помните о ее ограничениях:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Учитывая, как реализован --% , единственный способ встраивать значения PowerShell - это - неудобно - определить _aux. переменная среды_ и укажите на нее синтаксис %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Обработка ошибок

Ожидающий рассмотрения https://github.com/PowerShell/PowerShell-RFC/pull/88 обеспечит лучшую интеграцию обработки ошибок с внешними (собственными) исполняемыми файлами.

Между тем, модуль Native поставляется с двумя функциями для специального согласия на обработку ненулевого кода выхода как ошибки завершения сценария :

  • ins поддерживает переключатель -e / -ErrorOnFailure , который выдает ошибку, если $LASTEXITCODE равно нулю после вызова собственной оболочки.

  • iee - это функция-оболочка для ie которая аналогично выдает ошибку, если $LASTEXITCODE равно нулю после вызова внешнего исполняемого файла.

Команды, поставляемые с модулем, имеют множество тонкостей.
Мы надеемся, что довольно обширная помощь на основе комментариев достаточно их охватывает.

Все 189 Комментарий

Мне нравится эта идея, и я считаю ее полезной, но думаю, что было бы гораздо полезнее (я думаю конкретно в случае DSL), если бы я мог получить расширение одной строки. Итак, если я построил в PS строку, совместимую с cmd.exe, у меня есть способ дословно преобразовать cmd.exe. Возможно

&n -command $string

Это потенциально также позволило бы мне указать моего интерпретатора:

&n -shell /bin/sh -command $string
или же
&n -shell cmd.exe -command $string

с точки зрения имени / оператора это "&!" в использовании? Это похоже на shbang ("#!").

$! - хорошее предложение!

Оболочку можно указать, просто указав ее как команду:

&! /bin/sh -c blah

Расширение $var создает проблему в том, что $ может быть буквальным для передачи в собственную команду / оболочку. Одна из проблем, которых это пытается избежать, - это когда людям нужно выяснить, как правильно все избежать. Если вам требуется расширение переменной, вы всегда можете сделать:

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

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

$mycommand = "/bin/sh ls"
Invoke-Expression "&! $mycommand"

Не могу поверить, что вы только что предложили IEX. :-)

@essentialexch бывает несколько раз, когда это уместно :)

Обратите внимание, что в этом примере ожидается, что пользователь правильно проверил содержимое $mycommand перед выполнением!

Определенно необходимы хорошая рецензия и решение проблем, подобных той, которую вы описали.
Я сам не знал о --% и, к сожалению, не помню, чтобы когда-либо видел его. Поэтому предлагаемый оператор &n может иметь аналогичные проблемы с обнаружением, и мне не кажется естественным комбинировать символ с символом (и это напоминает мне% f в операторах printf языка Си). Так как это в любом случае критическое изменение. Есть ли причина, по которой этого не может быть, например, && ?
Может быть, мне было бы более интуитивно понятнее использовать что-то вроде скобок (или двойных скобок?), Чтобы отметить область, которую вы хотите выполнить. Пример: & [ wsl ls ] | less

Я согласен с тем, что &! - это более интуитивно понятный способ для тех, которые поступают из других оболочек, однако в super-duper-linter я обычно запускаю собственные команды, создавая массив параметров, а затем передавая его команде.

$command = 'linter'
$myargs = @(
    '-config'
    'linterconfig.path'
)
if ($Test) {$myargs += 'test'}
& $command <strong i="7">@myargs</strong>

Так что это лучший способ в случае условного и еще чего-то для меня

@bergmeister Изначально я думал, что && визуально похож на & который многие знают сегодня. Однако && являющийся оператором цепочки конвейера, может вызвать путаницу в том, что он должен делать. Мне нравится предложение &! настоящее время.

@JustinGrote , нет причин не использовать это дальше. синтаксис &! действительно предназначен для случаев, когда вы просто хотите вырезать и вставить или имеют некоторые аргументы, которые конфликтуют с синтаксисом PowerShell и не хотите или не знаете, как правильно избежать

О, чувак, я помню, как десять лет назад обсуждал это с Брюсом и Джейсоном. Мне кажется, что изобретать другого оператора здесь не нужно. Я знаю, что некоторые люди в этой ветке не слышали о --% но держу пари, что их больше, чем вы думаете. Что случилось с:

# run ls in wsl, return results to powershell, then pipe to wsl again, to grep.
& wsl ls | wsl grep -i "foo"  

#  the entire pipeline right of wsl will run in wsl. 
& --% wsl ls | grep -i "foo"

Почему никто этого не предложил? Это логически согласуется с использованием --% другом месте, чтобы сказать, что "все, что после этого должно быть передано без специальной обработки PowerShell".

Например, @oising хорошо, эта команда не работает, потому что вы должны предварять ее командой, которую хотите запустить, вы предлагаете добавить функциональность?
image

Это вроде как делает то, что ожидается:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

Invoke-LiteralCommand ping -W 200 www.google.com | grep icmp

@JustinGrote Да, я знаю, что сейчас это не работает :) Я предлагаю _ вместо &n или &! или чего-то еще, о чем идет речь. Для меня это просто имеет больше смысла: & - это вызов, а --% - подавление синтаксического анализа PowerShell; вместе они последовательны и более заметны, чем добавление чего-то нового. Мне не нравится идея иметь "вызов" и "родной" оператор.

Я расширил свой пример, чтобы показать различия.

@oising Я бы не

@oising Я бы не

Не за что. Это для пользователя PowerShell, который не понимает сложных правил цитирования между PowerShell / cmd.exe / bash. Как сказано в заголовке проблемы, "вызовите родной" для вызова собственных исполняемых файлов.

@oising Я бы не

Не за что. Это для пользователя PowerShell, который не понимает сложных правил цитирования между PowerShell / cmd.exe / bash. Как сказано в заголовке проблемы, "вызовите родной" для вызова собственных исполняемых файлов.

Верно, или для тех из нас, кто предпочел бы вообще не думать о них.

Да я с @oising. Эта концепция существует, ее просто ужасно недостаточно. Если мы собираемся реализовать что-то совершенно новое, нам лучше отказаться от старого синтаксиса или удалить его.

Мне кажется, что эти идеи часто озвучиваются, но их фактическая целевая аудитория не придается должного значения. Если пользователи _ уже_ изо всех сил пытаются найти существующие методы и обнаруживают, что существующие методы отсутствуют, когда они обнаруживаются, это призыв: а) улучшить документацию и б) улучшить фактическую функциональность существующих операторов.

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

Я согласен с тем, что для ознакомления с этими концепциями следует использовать сообщения об ошибках и / или систему предложений. Я не согласен, что нам нужен третий? четвертый? пятый? (Я буквально сбился со счета, кто-нибудь поможет мне здесь) способ вызова команд. Существующих методов недостаточно - это означает, что мы должны _ улучшать_ их, а не оставлять их позади, как паутину, чтобы еще больше запутать пользователей, когда они начнут копаться в них.

Первый вопрос, который я обычно задаю, когда люди понимают, что есть 5 способов сделать что-то, это «почему есть 2–3 способа, которые буквально делают одно и то же, но не работают так же хорошо», и я отвечаю, как всегда - - команда PS _way_ слишком сосредоточена на том, чтобы все, что было за последнее десятилетие ++, по-прежнему работает, при попытке добавить / улучшить функциональность. Здесь мы снова видим это, у нас есть существующие, но недостаточные реализации, которые нуждаются в доработке и улучшении, и предлагаемое решение состоит в том, чтобы еще больше замутить пул, добавив еще одну потенциально незавершенную реализацию.

Мы должны завершить то, что начали, а не начинать новую реализацию заново, ИМО.

Я считаю, что если мы собираемся использовать & для более чем нескольких уникальных вариантов использования, нам следует подумать о том, чтобы сделать это глаголом, или взглянуть на стандартизацию во всем языке.

Меньше всего мне хотелось бы запутаться в том, что должен делать &. В целом.

Я бы рассмотрел 3 сценария:

  1. Полное расширение - текущее поведение PowerShell.
  2. Полный литерал - это предлагается в ОП.
  3. Частичное расширение - wsl ls $path все еще работает

Интересно, где комментарии @ mklement0 ? :-)

В начале чтения этой ветки я подумал; отличная идея! Но после прочтения комментариев я задумался. Я всегда находил команду & «не достойными стандартов PS» и что-то напоминающее мне о «днях PERL» (тьфу). Введение другой нестандартной команды PS (не существительное-глагол) не поможет. Конечно, не тем, кто менее сообразителен в PS.

Про параметр -% я тоже не знал. Я использую тот же принцип, что и @JustinGrote в качестве решения.

Вырезание / вставка команд в оболочку PS никогда не было моим главным делом.
Не лучше ли нам заменить эти собственные команды на PowerShell?
Сделать путь для полной замены cmd.exe ...

Я голосую за улучшение существующих команд. И действительно - улучшение некоторой документации по использованию -% и &

@iSazonov вариант использования, предлагаемый @ SteveL-MSFT, - это сценарий «вырезать и вставить». Я думаю, что частичное расширение значительно усложнит работу (со стороны AST)

Сразу же после «#!» а потом "!#". Нравится «&!».

Вероятно, мы можем посмотреть на добавление ускорителей токенов к вызовам функций в токенизаторе. Это позволило бы использовать следующее.

Iex -l
Или Invoke-Expression -literal, который останавливает синтаксический анализ перед передачей.

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

Get-Help &, например, ни с чем не отображается. И искать надо в about_Operators.

Однако есть уникальные случаи для call_operator, которые не документируются.
& название модуля {команда}
позволяет вам войти в модуль. И я уверен, что есть еще кое-что. Но его обнаруживаемость настолько мала, что его трудно найти в нативной документации. И сообщество знает об этом только через JSnover и его доклад, показывающий нам крутые вещи.

Я считаю, что какое бы решение ни было принято, необходимо полностью учесть, насколько доступна эта «функция» с точки зрения нового пользователя, и иметь в виду, что новые пользователи попытаются использовать ее в Powershell 5, не зная, что это новое для ядра pwsh, если обнаруживаемость слишком низкая.

Я бы хотел, чтобы мы изменили существующее поведение, но изменение такого фундаментального API в PowerShell просто сломало бы так много вещей. Возможно, мы могли бы рассмотреть возможность перехода на новую конфигурацию.

Более прямой выход из строя когда-то был возможен до того, как PS 6 перешла в GA, но, честно говоря, даже тогда это было бы серьезной угрозой обратной совместимости (в отличие от функции Python print() ).

Что касается передачи аргументов в подпроцессы, я думаю, что и синхронный вызов, и вызов Start-Process уже имеют исторические проблемы при обсуждении капитального ремонта, и я чувствую, что оба требуют вложений одновременно для согласованности. В частности, Start-Process необходимо обновить поддержку передачи массива аргументов.

Для синхронного вызова с передачей аргументов cmdline я вижу четыре возможных типа передачи аргументов:

  • Текущее поведение, которое станет устаревшим, подчиняется CommandLineToArgvW в Windows и дает неожиданные результаты
  • Поведение по умолчанию, которое должно передавать аргументы в соответствии с их значением выражения, но применять обычные правила токенов для простых слов, так что такие вещи, как > и | разделяют команды. В этом режиме значение, которое подпроцесс принимает из выражения, должно быть тем, что отображается Write-Host $val
  • Дословное поведение, при котором все токены интерпретируются как простые строки до тех пор, пока не будет виден escape-токен. Это то, что должен делать --% сегодня, но в идеале он должен иметь только один конечный токен, который можно встроить в ту же строку, например --% ... %--
  • Поведение строки чтения в Bash, где применяется альтернативная логика экранирования, ориентированная на sh, что упрощает совместимость.

Я думаю, что от первого поведения следует постепенно отказываться, вводя второе в качестве экспериментальной функции и затем меняя их местами.

У второго и третьего вариантов поведения могут быть свои собственные сигилы, например &! и &# или, возможно, +% ... -% или что-то в этом роде. Но для дословного режима я думаю, что важным аспектом было бы упрощение токена выхода, чтобы дословно бралось больше токенов. Похож на heredoc.

Если запрос касается только сценария копирования и вставки, например «Любое текстовое содержимое после этого оператора будет вызывать для выполнения« оболочку по умолчанию »ОС». почему бы не сказать об этом прямо с помощью shell ?
`` Powerhell
PS> оболочка wsl ls | Меньше
PS> (оболочка wsl ls * .txt) | Select-String Hello

Его легче обнаружить и прочитать, чем зашифрованные операторы в стиле Forth / APL.

Я абсолютно не понимаю, как это разрешить # 1995: см. Https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

Кроме того, проблема с WSL - это проблема поведения wsl.exe (см. Https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021), а не PowerShell.
(Хорошо, есть проблема с PowerShell, но это № 1995)

О, а разве это не дубликат https://github.com/PowerShell/PowerShell/issues/12975 ? Потому что в основном я могу повторить то, что упоминал там:

@bitcrazed
Проблема в том, что отсутствие возможности выделить часть командной строки для передачи varbatim принимающей команде / скрипту - это то, что постоянно сбивает с толку пользователей.
Что вы имеете в виду под «частью командной строки, которую нужно передать varbatim»? Вы имеете в виду: 1. передать некоторую последовательность символов дословно вызываемому исполняемому файлу так, чтобы вызываемый исполняемый файл имел эту последовательность символов в одном элементе своего массива аргументов (например, эти символы затем доступны в `argv [1]` в [main ] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)) Или вы имеете в виду 2. вставить некоторая последовательность символов дословно в [параметр lpCommandLine в CreateProcess] (https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) - - Если вы имеете в виду (1.), то по сути он уже работает:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(За исключением проблемы встроенных кавычек, обсуждаемой в https://github.com/PowerShell/PowerShell/issues/1995). Можно было бы возразить, что добавление строки в одну строку улучшит некоторые варианты использования, но я думаю, не в этом суть вопроса. Поскольку это уже работает более или менее, я полагаю, вы имели в виду (2.) --- Если вы имеете в виду (2.), то позвольте мне выразить свое мнение по этому поводу несколько драматично: Пожалуйста, пожалуйста, не добавляйте специальный синтаксис для этого. Это в основном то, что пытался сделать `-%`, что также никогда не должно было быть реализовано. Почему я так категорически против этого? 1. Это проблема только Windows, поэтому добавление синтаксиса будет означать, что powershell в Windows имеет другой синтаксис, чем в Linux. (Или синтаксис будет поддерживаться, но совершенно бессмысленен, как в настоящее время имеет место для `-%`) 2. Если основная оболочка командной строки в Windows, опубликованная Microsoft, добавляет первоклассную функцию (через специальный синтаксис, а не через командлет) для вызова исполняемых файлов, которые не соответствуют типичным [правилам синтаксического анализа командной строки] (https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs -2019 # parsing-c-command-line-arguments) (если инструмент следует типичным правилам, вам не нужно (2.), обычно вы можете лучше использовать (1.)), тогда это поощряет авторов командной строки инструмент, чтобы не следовать этим правилам, что только ухудшает ["анархию командной строки Windows"] (https://stackoverflow.com/a/4094897/2770331). Чем меньше людей следуют типичным правилам, тем сложнее программно вызывать внешние исполняемые файлы или вообще писать кроссплатформенный код, поэтому я определенно считаю, что авторов программ следует поощрять следовать этим типичным правилам. 3. Я сильно сомневаюсь, что это распространенный вариант использования> И это не просто проблема, которая влияет на WSL: она также влияет на вызовы командной строки wt Windows Terminal и многие другие инструменты. Не могли бы вы добавить несколько примеров, где возникают такие проблемы? Потому что в случае WSL я бы сказал, что WSL-синтаксический анализ командной строки просто [сломан] (https://github.com/Microsoft/WSL/issues/1746) (проблема связана с `bash.exe`, но ситуация по умолчанию не лучше с wsl.exe) в случае по умолчанию - я бы рассмотрел все инструменты, которые не соответствуют типичным [правилам синтаксического анализа командной строки] (https://docs.microsoft.com/en-us/ cpp / cpp / main-function-command-line-args? view = vs-2019 # parsing-c-command-line-arguments) не работает, но поведение WSL по умолчанию - IMHO, даже не задокументировано должным образом ... Я сказал, что "по умолчанию "поведение` wsl.exe` нарушено - при написании этого ответа я заметил, что `wsl.exe` на самом деле, похоже, ведет себя так, как ожидалось, при использовании` -e`:
PS C:\> wsl -e bash -c 'cd / && ls . | cowsay'
 _______________________________________
/ acct bin boot cache cygdrive data dev \
| etc home init lib lib64 lost+found    |
| media mnt opt proc root run sbin snap |
\ srv sys tmp usr var                   /
 ---------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
PS C:\>
Таким образом, единственное, чего не хватает для этого явного варианта использования, - это IMO параметр для `wsl.exe` для вызова оболочки по умолчанию с анализируемыми аргументами командной строки, как в любом другом обычном exe.

Единственное, что касается «части командной строки, которая будет передана varbatim принимающей команде / сценарию», которая будет улучшена с помощью этого оператора, будет означать (2.) выше, и, как уже упоминалось, я думаю, что развитие в этом направлении - это Плохо.

Если все, что вы хотите сделать, это ярлык, скопируйте и вставьте существующие командные строки, почему бы вам не добавить командлет (например, Invoke-Shell , который вызывает bash -c в Linux и cmd /c в окна?
Тогда ты мог бы просто сделать

Invoke-Shell @'
whatever existing comandline containg '"quotes"' or whatnot
'@

Если это должна быть одна строка, добавьте синтаксис «одна строка здесь строка», но, пожалуйста, не реализуйте специальный оператор, который не может быть правильно найден и просто все усложняет.

И, пожалуйста, не бросайте ради этого # 1995!

+10 баллов за использование слова «сигил».

Что хорошо в сравнении &! с shell это то, что "оболочка" - это обычное английское слово, поэтому у людей может быть уже существующая команда с именем shell. (На самом деле у меня есть и функция с именем «shell», и функция с именем «n» (поэтому мне не нравится &n ).)

@oising : & --% - это хорошо, за исключением того, что одна из явных целей предлагаемой функции - означать, что | становится чем-то, что передается в собственную оболочку; не то, что возвращает вас к синтаксическому анализу PowerShell. И если мы сделаем приоритет конвейера другим для & --% чем для foo $blah --% more args , я обнаружу, что это сбивает с толку ... что, если бы вы сделали & $foo --% more args | blah ? Таким образом, на мой взгляд, предлагаемый &! достаточно отличается, чтобы иметь право использовать другого оператора. (Другими словами: предлагаемая функциональность связана, но отличается от комбинации & и --% .)

Эта функция предназначена не только для «нового пользователя PowerShell, который знает bash» и «пользователя, который не знает всех сложных правил цитирования / экранирования». Это также для самых опытных пользователей, которые просто хотят скопировать / вставить команду из StackOverflow без необходимости вмешиваться и возиться со всем. Это случается со мной довольно часто - продукт, над которым я работаю, имеет много документации о том, как что-то делать, а lingua franca - это команды cmd.exe, поэтому мне нужно пойти и заняться исправлением% VARIABLE_REFERENCES% и т. Д. .; было бы намного лучше набрать &! а затем вставить. Фактически, если это будет реализовано, я, вероятно, буду называть его «оператором StackOverflow». : D

@ vexx32 : Это не оператор вызова типа «одно кольцо, чтобы править всеми»; просто еще один инструмент в наборе инструментов для решения распространенной проблемы. Я не думаю, что мы должны избавляться от существующих инструментов, чтобы освободить место для этого; они делают разные вещи. Я понимаю, что новые пользователи могут чувствовать себя ошеломленными, когда в ящике с инструментами есть молоток, отвертка, дрель, ударная дрель и гвоздь ... но несмотря на то, что все они предназначены для того, чтобы «проткнуть одну или несколько вещей» вещи », они бесценны для профессионала; все они используются в разных (хотя и связанных) ситуациях.

@TSlivede : Я согласен с тем, что № 1995 - отдельный. Но я не согласен с тем, что «это проблема только Windows»: копирование и вставка специфичных для платформы команд из StackOverflow в равной степени применимо к любой ОС. (На самом деле, это было бы очень полезно для многих людей вроде меня, которые лучше работают на одной платформе, чем на другой - я лучше разбираюсь в технологиях Windows, поэтому в Linux я, как правило, много копирую и вставляю из SO.)

Будь то новый оператор &! или какая-то команда Invoke-Shell , у меня нет таких сильных чувств, как у других ... Я сочувствую людям, которые жалуются на то, что операторов трудно найти, и т. Д., Но Мне очень нравится лаконичность &! .

@jazzdelightsme, это не «еще один инструмент в наборе инструментов», а «еще одна невероятно специфическая насадка для отвертки, в которой уже есть около 3-5 штук, которые в настоящее время уже _ в порядке_ и могут быть улучшены», на мой взгляд.

@jazzdelightsme Часть «Только для Windows», возможно, больше связана с проблемой, в которой я изначально разместил это утверждение - да, хорошо, это не точная копия этой проблемы.

Если # 1995 не заброшен, а запрошенная здесь функция не решается с помощью специального синтаксиса, а с помощью командлета (или, возможно, нового синтаксиса для однострочной строки, которая работает везде, а не только для этого особого случая), то я на самом деле поддерживаю добавление этой функциональности. (Или, может быть, не «за», а «это полностью приемлемо для меня»)

Другая идея: если проблема на самом деле связана с копированием и вставкой полных команд, которые были предназначены для собственных оболочек, возможно, PsReadLine мог бы добавить функцию для приблизительного перевода команды из стиля bash или cmd в powershell. Перевод может запускаться с помощью специальной комбинации клавиш или автоматически, если при попытке выполнить команду возникает ошибка синтаксического анализа.

Это даст дополнительное преимущество, так как будет обучать пользователей синтаксису powershell.

Что касается обнаруживаемости: PsReadLine всегда может отображать сообщение на короткое время о том, что можно использовать определенную комбинацию клавиш, всякий раз, когда вставляется синтаксически неправильный контент (который будет действительным после перевода).

Но я мог понять, было ли это слишком сложно осознать.

@jazzdelightsme ты разговаривал с @joeyaiello? Я думал, только он назвал это StackOverflow operator что заставило меня съежиться, хотя это очень правда

@ SteveL-MSFT Нет, я ни с кем об этом не говорил; Я пришел к «оператору StackOverflow» самостоятельно. xD

@jazzdelightsme

... за исключением того, что одна из явных целей предлагаемой функции - означать, что | становится чем-то, что передается в собственную оболочку; не то, что возвращает вас к синтаксическому анализу PowerShell.

Я не уверен, что вы поняли мое предложение; то, что вы говорите, _точно_ то, что я говорю. Позвольте мне быть более подробным с некоторыми стратегическими подсказками: D

# NON-OPTIMAL SOLUTION: run "ls" in wsl, return results to powershell, then pipe to wsl again, to "grep."
& wsl ls | wsl grep -i "foo"  

# NEW SOLUTION: the entire pipeline/string, INCLUDING THE PIPE SYMBOL, right of wsl will be executed in wsl. 
& --% wsl ls | grep -i "foo"

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

# NEW SOLUTION: allow placing --% beyond first argument
# pass everything right of --% to the command in $wsl 
$wsl = gcm wsl
& $wsl --% ls | grep -i "foo"

# or

& wsl --% ls | grep -i "foo"

# NEW SOLUTION: execute multiline pasted code without powershell parsing.
& --% @'
(pasted multiline code)
@'

Что мне здесь не хватает? Если канал появляется после --% это просто еще один тупой символ: аргумент. Нет никакого возни с приоритетом операторов. Если он содержится во вложенном конвейере или подвыражении, снова запустите синтаксический анализ для PowerShell.

что, если бы вы сделали & $ foo -% more args | мля?

Что ж, это будет пытаться выполнить аргумент в $foo и передать "more" , "args" , "|" и "blah" которые будут рассматриваться как любые в $foo хочет. Правила кажутся мне достаточно простыми, но, по общему признанию, я давно использую powershell / monad. Это не серьезное критическое изменение в & поскольку & $foo --% ... рассматривает --% как любой другой аргумент. Так что, если мы не использовали эту сигилу в другом месте, мы должны быть в безопасности.

Мысли?

Я внес несколько изменений в приведенное выше, чтобы охватить частичную оценку строки, многострочную оценку и переменное позиционирование --% . И все это без введения новых символов, операторов или странного синтаксиса. Я думаю, что это все. Единственное: сможет ли парсер с этим справиться? Как вы думаете, @lzybkr @JamesWTruher @BrucePay ?

Слушай, слушай, @TSlivede.

Простите за резкость, но мы уже давно говорили об этих проблемах:

То, что предлагает этот выпуск, сводится к магическому мышлению:
Это связано с злополучным, по своей сути проблематичным «символом остановки анализа», --% , который в его нынешней форме имеет ограниченное использование _в Windows_ (см. Https://github.com/MicrosoftDocs/PowerShell- Docs / issues / 6149) и практически бесполезны на _Unix-подобных платформах_ (см. Https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963).
--% никогда не должны были реализовываться, равно как и это предложение не должно реализовываться в том виде, в каком оно представлено.

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

  • (a) «Я хочу передать токены, разделенные пробелами, в какую-то внешнюю программу, разрешив любые голые слова, не беспокоясь о метасимволах оболочки, таких как & или | ». - см. https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • Конечно, такая особенность противоречит использованию таких команд _ как часть конвейера_ или с _ операторами цепочки конвейеров_ ( && и || ), а также размещению дополнительных команд в той же строке, после ; - и, что немаловажно, возможность использовать любые переменные и выражения _PowerShell_ в командной строке.
  • (b) «Я хочу, чтобы моя командная строка обрабатывалась так, как я привык _ из оболочки, которую я обычно использую_ (или оболочки, для которой изначально была написана командная строка)», что, конечно, _is_ зависит от использования символов без кавычек например, & или | интерпретируются как метасимволы и поэтому имеют _синтаксическое_ значение.

    • Показательный пример: ни cmd ни /bin/sh (или любая POSIX-подобная оболочка, такая как bash ) не будут принимать wsl ls | less способом, который проходит через ls , | и less _verbatim_ до wsl - все они интерпретируют _unquoted_ | как _свой_ символ вертикальной черты.

Эти идеи расходятся друг с другом и требуют разных решений - ни одно из них не требует особого синтаксиса как такового:

  • Решение (а) состоит в том, чтобы _ отказаться_ от идеи и вместо этого использовать собственный существующий синтаксис _PowerShell_, чтобы гарантировать, что метасимволы используются _verbatim_, что неизменно требует _quoting_ или _escaping_ ,:

    • Либо: цитируйте голые слова (токены, не заключенные в кавычки), содержащие метасимволы _ как целое_: wsl ls '|' less
    • Или: ` -экранировать метасимволы _индивидуально_ в голых словах: wsl ls `| less
    • Или: используйте _array [переменную] _ для передачи аргументов _verbatim_: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • Конечно, # 1995 демонстрирует, что, к сожалению, этот подход был и всегда был нарушен, когда дело доходит до аргументов, которые имеют _embedded_ " _ при вызове внешних программ_ - _ это необходимо решить_: см. Ниже.
  • Решение (b) состоит в том, чтобы фактически _ вызвать оболочку, которую я обычно использую_, передав командную строку _ как одну строку_.

    • Как предлагает @TSlivede , Invoke-Shell которому вы передаете командную строку _ как одну строку_ и который вызывает _ платформенную оболочку_, и у нас уже есть синтаксис для дословных строк ( '...' или его вариант здесь, @'<newline>...<newline>'@
    • Я рекомендую называть его Invoke-NativeShell , а на Unix-подобных платформах - /bin/sh не bash .

      • Кроме того, я предлагаю определить ins как краткий псевдоним.

    • Этот подход решает все концептуальные проблемы с помощью --% и имеющегося предложения:

      • Это делает очень очевидным, где заканчивается команда, переданная в собственную оболочку (в силу того, что она _ одна строка_), и позволяет использовать вызов Invoke-NativeShell / ins в обычном конвейере PowerShell / с Перенаправления PowerShell без двусмысленности.

      • Вы можете дополнительно использовать _expandable_ строку ( "...." или ее вариант здесь-строки) для встраивания значений переменных _PowerShell_ в командную строку (что-то, что --% не может сделать).


Поскольку сильные мира сего пришли к выводу, что # 1995 нельзя исправить, чтобы не нарушить обратную совместимость - в конце концов, все существующие обходные пути не работают, см. Https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 - Лучшее, что мы можем сделать, - это поддержать подписку на правильное поведение с помощью как можно более краткого синтаксиса, чтобы свести к минимуму боль от необходимости стараться изо всех сил, чтобы получить поведение, которое всегда должно было быть по умолчанию.

Следовательно: специальный синтаксис, такой как &! должен быть предоставлен только в качестве подписки, чтобы получить правильную передачу аргументов во внешние программы для адресации # 1995 , на основе поведения, предложенного в RFC @TSlivede - см. https://github.com/PowerShell/PowerShell-RFC/pull/90. Если будущей версии PowerShell когда-либо будет разрешено нарушать обратную совместимость, то это должен быть прямой вызов / вызов с & который должен демонстрировать такое поведение.

С точки зрения пользователя это означает: все, что мне нужно сделать, это удовлетворить требования к синтаксису _PowerShell_ - см. Https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192 - и я могу положиться на PowerShell, чтобы надежно передать полученные дословные токены в целевую программу - _это основная задача любой оболочки, которую PowerShell, к сожалению, никогда не удовлетворял в отношении внешних программ_.

Например, следующая команда, которая в настоящее время _не_ работает должным образом (при вызове напрямую или с & - см. Эту проблему с документацией ), должна работать:

PS> &! bash -c 'echo "one two"'
one two   # Currently (invoked with & instead of &! or with neither) prints only 'one', 
          # because PowerShell passes the entire argument (brokenly) as  "echo "one two"",
          # which the target program sees as *2* verbatim arguments `echo one` and `two`

Те, кто ищет обходной путь в текущих / более ранних версиях PowerShell, могут найти простую функцию, которая будет охватывать большинство случаев использования на https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059 (ограничения обсуждаются на https: // github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892) и более подробную версию на https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606

PS: Проблема обнаружения операторов (не только на основе символов) - это отдельная проблема, которую стоит решить отдельно: решение состоит в _enhance Get-Help чтобы разрешить поиск операторов_ - см. # 11339, который также ссылки на временное решение.

Поскольку власть имущие пришли к выводу, что # 1995 нельзя исправить, чтобы не нарушить обратную совместимость - в конце концов, все существующие обходные пути будут нарушены, см. # 1995 (комментарий) - лучшее, что мы можем сделать, - это поддержать подписку на правильное поведение с максимально сжатым синтаксисом, чтобы свести к минимуму боль от необходимости стараться изо всех сил, чтобы получить поведение, которое всегда должно было быть по умолчанию.

@ mklement0 Я думаю, что https://github.com/PowerShell/PowerShell/issues/1995, и оно было отклонено. Так что для меня это звучит как принятие желаемого за действительное. Я ошибся?

@AndrewSav , изначально мы обсуждали только

Однако @joeyaiello сказал следующее в https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987, когда закрыл проблему (курсив добавлен):

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

Новый оператор - это своего рода согласие, но, надеюсь, беспроблемное (хотя необходимость в нем неуместна, но это цена обратной совместимости).

Хотя это предложение якобы также решает # 1995, это не так, поскольку оно нацелено (в первую очередь) на приспособление синтаксиса _других_ оболочек. Учитывая, что обе проблемы стоит решать независимо, мое предложение было следующим:

  • Давайте представим новый оператор, но будем использовать его исключительно для исправления # 1995 (через https://github.com/PowerShell/PowerShell-RFC/pull/90), то есть для исправления ошибки _own_ (не эмуляции другой оболочки) PowerShell. обработка передачи аргументов во внешние программы.

    • В этом случае новый код всегда должен использовать &! (или любую другую форму, которую мы выберем) вместо & при вызове внешних программ (вы можете думать об этом как об устаревании & для внешних программ) .
    • При вызове команд _PowerShell_ &! должен действовать так же, как и & сейчас (никогда не было проблем), тем самым обеспечивая последовательное использование &! .
  • Чтобы решить проблему повторного использования командных строк из других оболочек, давайте реализуем командлет Invoke-NativeShell / ins , который вызывает соответствующую платформе собственную оболочку ( cmd /c в Windows , /bin/sh -c на Unix-подобных платформах) с командной строкой, переданной как _ single string_.

    • В простейшем случае, когда в командной строке нет ' подойдет обычная строка в одинарных кавычках; например:

      ins 'ls | кошка -n '

    • При наличии ' они могут быть либо _doubled_; например:

      ins 'echo' 'привет' '| кошка -n '

    • или, если желательно вообще не касаться командной строки, можно использовать строку здесь (как уже показано @TSlivede) - хотя этот синтаксис немного более громоздкий, он всегда должен работать:

      ins @ '
      эхо "привет" | кошка -n
      '@

    • Кроме того, используя _expandable_ (интерполирующую) строку ( "..." или @"<newline>...<newline>"@ ), вы можете включить переменные _PowerShell_ в строку, передаваемую в собственную оболочку (что-то, что --% не поддерживает):

      $ foo = 'привет'
      ins @ "
      echo '$ foo' | кошка -n
      "@

    • Заметка:

      • Командлет Invoke-NativeShell представляет собой довольно тонкую оболочку для вызовов cmd /c / /bin/sh -c , которые вы, очевидно, могли бы сделать непосредственно сами, но у него есть то преимущество, что вам не нужно знать конкретное имя исполняемого файла и синтаксис передачи командной строки.
      • В качестве потенциально полезного побочного эффекта вы сможете использовать общие параметры, такие как -OutVariable , и можете подумать, разрешить ли параметрам -Error* действовать на _stderr_ (хотя для симметрии Invoke-Command тоже должны, что было бы критическим изменением).

        • Поскольку POSIX-подобные оболочки поддерживают _multiple_ аргументы с -c - первый составляет код для выполнения, а последующие - аргументы для передачи этому коду, начиная с $0 (!) - например, bash -c 'echo $@' _ 1 2 3 - также должны поддерживаться необязательные дополнительные аргументы.

        • Поскольку вызывается собственная оболочка платформы, такие вызовы по определению _не_ переносимы из-за фундаментальных различий между cmd и sh .Вот почему так важно исправить _own_ передачу аргументов в PowerShell (# 1995), поскольку тогда он предоставляет надежный, действительно кроссплатформенный метод вызова внешних программ.


Последний кусок головоломки - это _документация_.

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152 - давнее предложение написать концептуальный раздел справки о вызове внешних программ (например, about_Native_Calls ), который должен в одном месте: охватить _все_ аспекты, относящиеся к вызову внешних программ; помимо того, что там уже описано (синтаксические ошибки из-за дополнительных метасимволов, отсутствие конвейера необработанных байтов, отсутствие интеграции с обработкой ошибок PowerShell, ...), следует также обсудить рассматриваемые вопросы:

  • Как передача аргументов со встроенными аргументами " (и _empty_) не работает в (надеюсь, скоро) более ранних версиях (в настоящее время обсуждается в отдельном предложении, https://github.com/MicrosoftDocs/PowerShell-Docs / issues / 2361) и как обойти это (ручное \ -эскапирование или использование одной из вспомогательных функций из # 1995, одна из которых достаточно лаконична, чтобы быть непосредственно включенной в тему).

  • Как только &! следует использовать в дальнейшем для вызова собственных (внешних) исполняемых файлов, чтобы обеспечить правильную передачу аргументов.

  • Как Invoke-NativeShell / ins можно использовать для выполнения командных строк, написанных для собственной оболочки платформы как есть.

Интересно, что сегодня пользователи могут:

  1. прямое обращение
  2. & оператор
  3. Старт-процесс

Совершенно не ясно, что пользователи должны использовать.

После исправления нарушенного поведения пользователи получат следующие возможности:

  1. прямое обращение
  2. & оператор
  3. Старт-процесс
  4. &! оператор
  5. Start-ProcessV2

Не слишком ли много для оболочки запускать ping 127.0.0.1 ?

Да, это моя главная забота - добавить еще одного оператора @iSazonov. Это уже сбивает с толку, и это не поможет. Документация - это только часть головоломки. Даже если у нас есть приличная документация, простой факт, что есть _ пять разных способов_ сделать это, каждый из которых ведет себя немного по-своему, всегда будет вызывать путаницу.

Если мы вообще заботимся о UX, мы должны улучшить существующие кейсы, а не добавлять новые.

Если мы вообще заботимся о UX, мы должны улучшить существующие кейсы, а не добавлять новые.

Что вы не можете, потому что вы должны быть обратно совместимы. Я чувствую, что мы ходим по кругу. https://github.com/PowerShell/PowerShell/issues/1995 был таким и был закрыт именно по этой причине.

@iSazonov :

Существующий недостаток руководства необходимо устранить, даже если мы не введем нового оператора:

Вкратце резюмируем:

  • Не используйте Start-Process для вызова _console_ (терминала) программ (если - что доступно только в Windows - вы не хотите запускать их _в новом окне_).

  • Прямой вызов и вызов через & _эквивалентны_; (оставляя блоки сценария в стороне), & всегда требуется только по _синтаксическим_ причинам, когда имя или путь команды _цитируются_ и / или содержат _переменные ссылки_.


После исправления нарушенного поведения пользователи получат следующие возможности:
прямое обращение

  • & оператор
  • Старт-процесс
  • &! оператор
  • Start-ProcessV2
  • Нет, Start-ProcessV2 , только _new parameter_, -IndividualArguments - см. Https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • Да, будет &! (или любой другой символ, который мы выберем), но он эффективно заменяет как & и прямой вызов_ (по крайней мере, для _внешних программ_, но вы сможете его использовать с _все_ формами команд), с которыми должно быть легко общаться.

Не слишком ли много для оболочки запустить ping 127.0.0.1 ?

ping 127.0.0.1 будет продолжать работать, но если вы хотите, чтобы bash -c 'echo "one two"' работало, вам придется использовать
&! bash -c 'echo "one two"'

Это прискорбно, но - учитывая, что поведение прямого вызова / & _ не может быть исправлено_ - это кажется мне лучшим решением.

К счастью, запомнить простое правило: _Если вы хотите, чтобы аргумент передавался во внешние программы, используйте &! _

Альтернативные формы подписки - конфигурация (через powershell.config.json ) или переменная предпочтений - гораздо более проблематичны:

  • Динамическое определение переменных предпочтений делает слишком легким непреднамеренное применение согласия к устаревшему коду, который не был разработан для этого - см. Https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • Конфигурация будет применяться ко всем сеансам в целом, что сделает отказ в целом невозможным.

  • В обоих случаях требуется действие, _отдельное_ от конкретного вызова, и просмотр данного вызова не скажет вам, как он будет себя вести - напротив, &! оставляет сомнений относительно того, какое поведение последует.

Для конечного пользователя это выглядит так же ужасно, как если бы мы запустили калькулятор и обнаружили там 5 кнопок для операции сложения. 😄

Если мы хотим улучшить его настолько, что готовы к таким осложнениям, то, возможно, этого будет достаточно, чтобы принять критическое изменение (для прямого вызова и & ), чтобы упростить поведение и выпустить V8.0.

Если наше желание не так велико, можно значительно улучшить диагностику ( prog.exe param1 param2 -Whatif и то же самое для Start-Process -WhatIf), чтобы она отображала то, что отображает программа testargs и рекомендации по тому, как правильно выполнить побег.

Разработчик скрипта может позволить себе потратить время на тестирование и использование отладчика. Но в интерактивном сеансе любой пользователь хочет простоты, краткости и ясности, а это перевешивает все.

@iSazonov :

тогда, возможно, этого достаточно, чтобы принять критическое изменение (для прямого вызова и &), чтобы упростить поведение и выпустить V8.0

У вас определенно есть _my_ голос, но поддерживают ли власти этот план ? [_update_ - см. № 13129]
Если мы действительно возьмем изменение такого масштаба, мы, наконец, сможем заняться всем историческим багажом - см. №6745

Если мы так хотим его улучшить

Есть две веские причины так сильно этого хотеть:

  • Чтобы положить конец многим страданиям из-за неработающей передачи аргументов, о чем свидетельствует # 1995, множество связанных проблем и бесчисленное множество вопросов о переполнении стека.

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

Исторически сложилось так, что во времена, когда только Windows использовалась только для Windows, нехватка способных внешних интерфейсов командной строки делала невозможность их правильного вызова менее проблематичной, и пользователи, которые оставались в огороженном саду PowerShell (вызывая только собственные команды PowerShell), не чувствовали боли. Но времена изменились:

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


Даже если появится версия 8.0 с разрешенной обратной совместимостью (скрещенные пальцы), до ее выпуска может потребоваться много времени, и нам _также_ потребуется временное решение.

Несмотря на то, что предлагаемые улучшения документации необходимы, я не думаю, что они сами по себе достаточно облегчают боль из-за отсутствия легкодоступного рабочего решения.

Я понимаю, что оператор некрасивый, но неразрывное решение _ требует_ дополнительного синтаксиса (больше набирать), независимо от его формы.

Альтернативой &! которая менее уродлива, легче набирается и не «загрязняет» язык, является поставка встроенной _функции_ с кратким именем, например iep function. (I nvoke- E xternal P rogram) , что маскирует все проблемы, показаны здесь .
(Обратите внимание, что мы уже поставляем функции, которые либо объединяют другие функции (например, pause ), или предназначены для пользователей cmd.exe (например, cd.. [sic]).)

Таким образом, чтобы заставить bash -c 'echo "one two"' работать правильно, вы должны вызвать iep bash -c 'echo "one two"'
Или, если использовать более реалистичный сценарий:

# Pass JSON to an external utility (Unix example)
# Without `iep`, the embedded double quotes are lost and *broken JSON* is passed.
iep /bin/echo '{ "foo": "bar" }'

Опять же, мы, в конечном счете, говорим о «компромиссе» в отношении сохранения обратной совместимости - это неизбежно доставляет неудобства конечным пользователям.


улучшите диагностику (prog.exe param1 param2 -Whatif и то же самое для Start-Process -WhatIf), чтобы она отображала, что программа testargs отображает, и рекомендации по правильному выполнению экранирования.

Поскольку прямой вызов на основе & не является вызовом командлета, а все аргументы считаются проходными аргументами, обработка -WhatIf как общего параметра приводит к критическому изменению (однако маловероятно, что есть на практике).

Если вы готовы принять такое изменение, мы могли бы также (также) поддержать переключатель -DoItRight (или основанный на символе, аналогичный --% ) в качестве подписки для фиксированное поведение - и это кажется мне хуже, чем подход iep -prefix / &! .

(Я полагаю, что неразрывной диагностической опцией было бы улучшение Invoke-Command , который в настоящее время не поддерживает -WhatIf - однако, если мы предоставим хорошо задокументированную подписку на правильный поведение, я не думаю, что это стоит делать.)

Я согласен с @iSazonov в том, что ситуация не идеальна для введения еще одной вещи, которая должна делать то, что должно работать, но, как упоминалось @ mklement0, ожидание
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606 верно?

@ mklement0 Я не уверен, как может работать программа iep / invoke-externalprogram, поскольку командлет подвержен тому же изменению параметров, которое мы пытаемся исправить. Это проблема парсера, верно? Если бы мы попытались скрыть все внутри командлета / функции, я бы подумал, что это будет ужасно сложно, учитывая, что придется отменить намерение изменения / восстановления. Или я что-то упускаю?

@oising :

Для ясности: связанная функция iep исправляет # 1995, а не то, о чем в первую очередь идет _это_ предложение (которое, как я думаю, _не_ заслуживает внимания, как утверждалось выше ), хотя обратите внимание, что заявленное намерение в OP состоит в том, чтобы _также_ решить № 1995.

1995 год - это возможность полагаться на синтаксис _PowerShell_ (а не на синтаксис другой оболочки) для правильной передачи аргументов внешним программам, как это дословно демонстрирует PowerShell после его собственного анализа_.

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

Напротив, передача аргументов _внешним программам_ плохо работает, и это исправляет iep .

Он не работает из-за некорректного _ повторного цитирования и экранирования_ аргументов _ за кулисами_, когда в конечном итоге используется командная строка.
https://github.com/PowerShell/PowerShell-RFC/pull/90 предлагает правильное исправление (без соглашения о том, как обрабатывать обратную совместимость, и с несколькими незначительными вопросами, на которые еще предстоит ответить), которое в своей основе полагается на недавно представил System.Diagnostics.ProcessStartInfo.ArgumentList для правильного повторного цитирования и экранирования для нас (мы просто передаем набор аргументов, полученных в результате собственного синтаксического анализа PowerShell), который, кстати, всегда требуется только _в Windows_; в Unix, разумно, _ здесь нет командной строки_ (вне оболочки), когда вы передаете аргументы при запуске процесса: вы просто передаете аргументы дословно в виде массива.

Коротко:

  • Предоставление iep в качестве встроенной функции было бы незначительным временным соглашением, предлагающим правильную передачу аргументов во внешние программы, пока не будет реализовано правильное исправление - в контексте критического изменения

  • Командлет Invoke-NativeShell ( ins ) - это правильное решение, а не временное решение, чтобы предоставить способ вызова _ другой оболочки_ (собственной оболочки) командных строк с использованием синтаксиса _its_ - см. Выше .

В настоящее время существуют случаи, когда вырезание и вставка команды PERL не выполняется должным образом в Python. Иди разбери. Это должно быть большим недостатком для всех тех резцов и пастеров. Я думаю, что в Python для этого должен быть специальный оператор.

Я не уверен, что вы поняли мое предложение; то, что вы говорите, _точно_ то, что я говорю. Позвольте мне быть более подробным с некоторыми стратегическими подсказками: D

# NEW SOLUTION: the entire pipeline AS ABOVE is sent to WSL, then the results are returned to powershell
# and piped, in powershell, to foreach-object
(& --% wsl ls | grep -i "foo") | % { "match {0}" -f $_ }

Что мне здесь не хватает? Если канал появляется после --% это просто еще один тупой символ: аргумент. Нет никакого возни с приоритетом операторов. Если он содержится во вложенном конвейере или подвыражении, снова запустите синтаксический анализ для PowerShell.

Мысли?

Чего вам не хватает, так это необходимости передавать закрывающую круглую скобку в качестве аргумента внешней программе. Это типичный подход DWIM, но PowerShell - это не HAL.

Спасибо всем за подробный отзыв. Говоря здесь как я. Несколько замечательных моментов, прежде чем я перейду к деталям:

  • Я все еще не использую vNext / 8.0, который решает нарушить целый ряд новых вещей. Обратная совместимость имеет значение. Я знаю, что это болезненно, и я бы предпочел жить в мире, где мы могли бы вернуться и исправить все ошибки, которые когда-либо допускал PowerShell, многие из которых были непреднамеренными. Но подавляющее большинство пользователей PS не торопятся с проблемами, по колено в крайних случаях. Они копировали / вставляли один и тот же обходной путь к чему-то в течение 4-10 лет, они не обязательно знают, почему это работает, и им все равно, что мы «исправляем ошибки», когда их сценарий ломается при обновлении. Они просто знают, что «обновление PS сломало меня, и я не могу доверять обновлениям», и в итоге мы получаем еще больше пользователей, которые замедляют свои обновления и внедрение.
  • Я думаю, что некоторые люди, участвующие в этой проблеме, могут возлагать на нее много разных надежд и желаний или рассматривать ее как вариант либо / либо с другими исправлениями. Я понимаю, что усугубил это, закрыв # 1995, и я сожалею об этом (хотя я скажу, я все еще не уверен, что это конкретно можно исправить, но мы посмотрим на это более внимательно), но это на самом деле это решение "лазейки", которое быстро выводит из строя как новых, так и опытных пользователей, не позволяя нарастать разочарование. Мы все еще можем вести более длительные переговоры об исправлениях в части синтаксического анализа PS, чтобы мы могли предложить действительно кроссплатформенное решение для всего.

Теперь комментарии, которые я читал:

  • Я с подозрением отношусь к предлагаемому желанию согласовать с #! (либо с &! либо с $! учитывая, что в случае по умолчанию пользователи, скорее всего, не будут указывать интерпретатор . Если я хочу сделать что-то вроде $! net <stuff> , net.exe - это не мой интерпретатор. А в не-Windows "нативная" вещь все равно передается на sh / bash / something. переход к исполняемому двоичному файлу. Например, я не хочу, чтобы Python анализировал *.py в строке $! /usr/bin/python *.py или даже $! python *.py . Я хочу, чтобы Bash выполнял подстановку, а затем передавал список совпадений с python .
  • Как и предположил @oising , я предпочитаю --% в качестве оператора, перегруженного для использования спереди. Обнаруживаемость - это проблема, и, учитывая, что мы уже 10 лет работаем над обнаружением этого ( теперь он доступен & ? Почему бы просто не начать строку с --% native.exe do /some $stuff ?
  • @rjmholt : Я не совсем понял ваш комментарий здесь , но я считаю, что мы, вероятно, не должны ничего менять с существующим поведением --% когда оно идет после исполняемого файла, но я думаю, что мы можем сделать ваш "новый поведение "с тем, где оно предшествует исполняемому файлу.
  • Прочтите это на @jazzdelightsme, и я полностью согласен:

    Это также для самых опытных пользователей, которые просто хотят скопировать / вставить команду из StackOverflow без необходимости вмешиваться и возиться со всем.

    На мой взгляд, это очень важный аспект сценария, возможно, самый важный. Это не просто SO, это документы из Интернета, которые предполагают, что вы находитесь в оболочке, подобной Bash.

  • Независимо от того, будут ли исправлены такие вещи, как # 1995, суть в том, что а) существует длинный хвост таких странных крайних случаев, и б) их действительно сложно исправить, не сломав людей. Это не обязательно верно в отношении всех них, но за последние пару лет мы достигли этого достаточно, и для меня очевидно, что нам нужна такая «лазейка», чтобы вывести людей из сложных ситуаций, когда они не хотят нырять. кроличья нора нашего вложенного побега (только чтобы обнаружить ошибку вроде # 1995, которая мешает им работать, даже если они действительно знают то, что знают).

Изучая комментарии, я думаю, что есть два открытых вопроса, на которые нам еще нужно ответить:

  1. Продолжаем ли мы анализировать разделители команд и все, что после них в PowerShell, или же разделитель команд также дословно передается в собственную оболочку?
    Я говорю, что мы должны передать все дословно в родную оболочку, поскольку в настоящее время это то, что вы не можете сделать, и что решение @oising в ведущей может быть решением, в котором мы все еще можем поддерживать каналы, цепочки конвейеров и т. .если вы хотите смешать / сопоставить нативный и PS-синтаксический анализ (хотя я хочу узнать мнение тех, кто ближе к парсеру).
  2. Какой оператор?
    Как упоминалось выше, мне нравится --% потому что он уже получил репутацию оператора «просто делай то, что он делал раньше», и нам придется выполнить сброс при обнаружении чего-либо нового. Я понимаю аргументы вроде @ mklement0 о том, что, если мы это сделаем, мы --% (и @ SteveL-MSFT тоже высказал мне этот аргумент), но я не думаю, что большинство людей в дикой природе думают о --% в терминах «он делает то, что раньше делал в cmd». Я не думаю, что будет преувеличением кодифицировать это предположение в определении оператора как «делайте то, что здесь делает собственная оболочка».

Ортогонально, просто подумайте о другом сценарии, который мы можем или не можем поддерживать.

В Windows cmd (эффективно) включает текущий каталог в свой поиск PATH . Однако PowerShell требует, чтобы пользователь предварительно отложил ./ для выполнения любых EXE (или чего-либо в PATHEXT ), если текущий каталог не находится в PATH .

Итак, мой открытый вопрос: действительно ли мы хотим передать всю командную строку в cmd? Я бы предположил, что, если мы действительно хотим осветить сценарий копирования / вставки с помощью этого оператора, ответ будет положительным, поскольку некоторые сторонние документы / учебные пособия не обязательно подробно описывают, как разместить инструменты, которые они ожидают от вас. в PATH . В любом случае это, вероятно , не имеет большого значения, но я хотел бы перечислить это здесь.

  • Я все еще не использую vNext / 8.0, который решает нарушить целый ряд новых вещей.

Да, я не знаю, как исправить эти проблемы, не взламывая, невероятно сложными способами. К тому же я не знаю, как вы все, но даже я не хочу жонглировать всеми этими сложными различиями в связке / парсере при написании для нескольких версий.

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

  • Как и предложил @oising , я предпочитаю --% в качестве оператора, перегруженного для использования спереди. (...) Почему бы просто не начать строку с --% native.exe do /some $stuff ?

Мне действительно это нравится. В идеале как можно ближе к вызову CreateProcess / exec (плюс обработка nix env var и перенаправление ввода / вывода или присоединение консоли). Или, может быть, полностью вернуться к cmd.exe / bash .

Я тоже не зацикливаюсь на том, чтобы держать & перед --% . Я могу оправдать идею обращения с необработанным --% как с директивой / оператором, по крайней мере, в моей голове. А что насчет многострочного? Разрешить --% с @" и "@ для подстановок и подвыражений var и одинарные кавычки @' и '@ для передачи литерала? Без строк здесь только одна строка?

Как и предположил @oising , я за -% как оператор

Работает на меня.

Однако вопрос: нужен ли нам здесь оператор & call?

Пока я все еще могу делать --% & $computedPathToNativeExe foo;@workspace тогда я буду в порядке. Нам действительно нужно иметь возможность использовать с этим оператор вызова.

Я говорю, что мы должны передать все дословно в родную оболочку, поскольку в настоящее время это то, что вы не можете сделать

Согласовано.

Я не думаю, что будет преувеличением кодифицировать это предположение в определении оператора (-%) как «делайте то, что здесь делает собственная оболочка».

Тоже согласился!

Кроме того, возможно, это способ включить функцию определения переменных env перед исполняемым файлом, которые определены только для сеанса исполняемого файла (# 3316):

--% JEKYLL_ENV=production jekyll build

Просто мысль.

@rkeithhill , идея с call native operator заключается в том, что строка не обрабатывается PowerShell и дословно отправляется в «оболочку по умолчанию» ОС. Итак, в этом случае ваш первый пример --% & $computerPathToNativeExe foo;@workspace не будет работать. Вам нужно будет использовать текущий метод и при необходимости уйти. Второй случай --% JEKYLL_ENV=productoin jekyll build должен работать.

строка не обрабатывается PowerShell и дословно отправляется в "оболочку по умолчанию" ОС

Хорошо, это кажется разумным. Однако этот --% JEKYLL_ENV=productoin jekyll build будет работать только в Linux / macOS, но не в Windows - по крайней мере, не без помощи PowerShell, верно?

@rkeithhill, как предлагается в настоящее время, никогда не будет работать в Windows, поскольку он полагается на оболочку ОС по умолчанию. Для PowerShell, объявляющего env var для процесса, у нас есть другая проблема, чтобы обсудить это.

@ PowerShell / комитет по PowerShell обсуждал это сегодня, я обновил исходное предложение, чтобы отразить результат этого обсуждения. Мы продолжим экспериментальную реализацию функции и получим больше отзывов от пользователей с рабочим кодом. Мы все еще открываем сигил для оператора, но пока используем --% .

где $foo - буквальное имя файла, а $PWD - текущий каталог в WSL. Обратите внимание, что в первом примере вам нужно знать, что нужно экранировать && чтобы он выполнялся в WSL, а не в PowerShell.

И $foo и $PWD являются ссылками на переменные, которые должны быть оценены sh .

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

$out = --% ls *.txt
$out | select-string hello

Обратите внимание, что в отличие от текущего оператора вызова & вы не можете использовать какой-либо синтаксис PowerShell, поэтому:

--% $commandline

попытается выполнить $commandline буквально (при cmd ) или после расширения оболочки (при sh ). PowerShell не будет пытаться разрешить эту переменную.

Проблема вырезания и вставки решается простой вставкой после ввода &n .

просто вставив после --%

Приведенный выше пример для wsl будет выглядеть так:

Который из?

Поскольку все аргументы после --% передаются в оболочку "по умолчанию", для поддержки конвейерной обработки в PowerShell вам потребуется использовать переменную:

Это повторение.

Если мы собираемся добавить что-то полезное, я предлагаю Pivot.

Почему у нас не может быть чего-то вроде литерала здесь.

@! ""! @

Таким образом, у нас может быть реальный способ сделать это вместо -%, у которого также есть свои собственные проблемы.

По крайней мере, таким образом мы можем избавиться от проблемы с помощью «лучшей» тактики проектирования.
И возможность обнаружения этих крайних случаев в About_Strings, где они должны быть решены.

Просто мысль.

Я не понимаю, чем @!"…"!@ лучше, чем Invoke-NativeShell @"…"@ . Мы пытаемся сделать PowerShell более похожим на Brainfuck?

@ yecril71pl

Я не понимаю, чем @!"…"!@ лучше, чем Invoke-NativeShell @"…"@ . Мы пытаемся сделать PowerShell более похожим на Brainfuck?

Я считаю, что @ romero126 означал предложить однострочный литерал здесь, чтобы можно было написать

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

вместо

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Если бы такая однострочная здесь строка была рассмотрена, я бы предложил использовать синтаксис, например @"""…"""@ и @'''…'''@ , это было бы легче набрать, чем @!"…"!@ .


@ romero126 Не могли бы вы пояснить, что вы имели в виду?

@ yecril71pl благодарим за обнаружение этих ошибок, исправлено верхнее сообщение.

Не указано, будет ли --% выполняться как командная строка или как пакетный файл.

Я считаю, что @ romero126 означал предложить однострочный литерал здесь, чтобы можно было написать

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@

вместо

Invoke-NativeShell @"
complicated native command with "quotes" and | pipe
"@

Если бы такая однострочная здесь строка была рассмотрена, я бы предложил использовать синтаксис, например @"""…"""@ и @'''…'''@ , это было бы легче набрать, чем @!"…"!@ .

@ romero126 Не могли бы вы пояснить, что вы имели в виду?

Я считаю, что если мы добавим Strings гибкости для обработки «почти сырых» неанализируемых данных, у нас не будет проблем с этим всплывающим окном.
@TSlivede Я почти полностью согласен с тем, что вы сказали. Только с небольшими вариациями. Он должен иметь возможность обрабатывать как однострочную, так и многострочную, без интерполяции чего-либо внутри кавычек. Какой бы синтаксис мы ни использовали, чтобы это произошло, я открыт. Я больше сосредоточен на концепции ..

В этих примерах он добавляет много гибкости, когда мне не нужно "Invoke-NativeShell", поскольку он просто возвращает строку. Это означает, что мы можем манипулировать им, как строкой. Так что это потенциально может стать основным продуктом того, на что похоже великое.

Подобный Pivot также означал бы, что нам не пришлось бы иметь дело с проблемами, описанными выше, когда мы вращаем колеса над почти решением, когда мы можем обрабатывать крайний случай, vs решает проблему в целом.

Быстрые примеры:

# For this scenario let @!"<text>"!@ be our HereString Verbatim

Invoke-NativeShell @!"complicated native command with "quotes" and | pipe"!@
# or
Invoke-NativeShell @!"
Complicated native command with "quotes" and pipe and <tabs>
"!@

#or 
$r = @!"
complicated native command with custom arguments: {0}
"!@ -format "foo"
Invoke-NativeShell $r

Это не решает проблему @ mklement0, покрывающую во второй части этого комментария . Дело в том, что мы не можем просто передать одну строку для каждой команды; во многих случаях их приходится разделять.

Я считаю, что если мы добавим Strings гибкости для обработки «почти сырых» неанализируемых данных, у нас не будет проблем с этим всплывающим окном.

Для этого нужны ' и @' .

В этих примерах он добавляет много гибкости, когда мне не нужно "Invoke-NativeShell", поскольку он просто возвращает строку. Это означает, что мы можем манипулировать им, как строкой. Так что это потенциально может стать основным продуктом того, на что похоже великое.

Собственные команды возвращают последовательность строк. @!" потенциально может стать основным продуктом того, на что похоже непонятное.

Я считаю, что если мы добавим Strings гибкости для обработки «почти сырых» неанализируемых данных, у нас не будет проблем с этим всплывающим окном.

Для этого нужны ' и @' .

В этих примерах он добавляет много гибкости, когда мне не нужно "Invoke-NativeShell", поскольку он просто возвращает строку. Это означает, что мы можем манипулировать им, как строкой. Так что это потенциально может стать основным продуктом того, на что похоже великое.

Собственные команды возвращают последовательность строк. @!" потенциально может стать основным продуктом того, на что похоже непонятное.

Я думаю, вы запутались. С тем, что я говорил.
Одиночные кавычки и двойные кавычки расширяют только переменную $ или нет. Он не экранирует символы автоматически. Это также ограничивает меня, поэтому я больше не могу использовать его определяющий характер. Так что вместо этого, чтобы он работал должным образом, как сейчас. Мне все равно пришлось бы экранировать символы, и это все, почему нам нужно было включить &! Командование.

Если у вас есть лучшее решение, я все уши. Я действительно считаю, что это больше касается крайних случаев, чем добавление &!

Одиночные кавычки и двойные кавычки расширяют только переменную $ или нет. Он не экранирует символы автоматически. Это также ограничивает меня, поэтому я больше не могу использовать его определяющий характер. Так что вместо этого, чтобы он работал должным образом, как сейчас. Мне все равно пришлось бы экранировать символы, и это все, почему нам нужно было включить &! Командование.

От каких персонажей вам придется сбежать и почему?

@ romero126 Теперь я не понимаю, что вы говорите. Вы знаете, что у PowerShell есть строка здесь ?

Раньше я думал, что синтаксис команд PowerShell может вас раздражать (по крайней мере, меня иногда раздражает, что в начале и в конце должна быть новая строка ...), но теперь я не совсем уверен, что вы говорите, не могли бы вы уточнить?

@TSlivede , согласился с тем, что строки здесь синтаксически несколько громоздки.

Существует существующее предложение # 2337, которое предлагает улучшения, и хотя оно сосредоточено на разрешении отступа закрывающего разделителя, похожий на Rust вариант _single-line _ (- также) в https: // github. com / PowerShell / PowerShell / issues / 2337 # issuecomment -391152107, что позволит нам, например, сделать следующее:

# `ins` is `Invoke-NativeShell`
ins @' python -c 'print("hi")' | cat -n '@

Необходимости в экранировании можно избежать, комбинируя _multiple_ ' с @ мере необходимости (например, @'' can use @' here ''@ .

Другими словами: это будет общее усовершенствование строковых литералов PowerShell, применимое везде, от чего Invoke-NativeShell также выиграет.

Учитывая, что № 2337 в первую очередь ориентирован на что-то другое, я создал конкретное предложение в № 13204 , и я предлагаю продолжить разговор на нем.

Потребности в побеге можно было бы избежать

Я видел это пару раз в этой теме.

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

Самая естественная форма этого сегодня - строки в одинарных кавычках:

  • ' : начать токенизацию одинарной кавычки
  • ' : конец токенизации в одинарных кавычках
  • '' : напишите буквальную одинарную кавычку внутри одинарной кавычки.

Я думаю, что причина, по которой мы говорим о побеге, несмотря на это:

  • Оператор --% определяет слишком много способов выхода из своего режима ( | , && , || , ; , новая строка) и разрешает строки внутри него (удаление их символов ' )
  • Herestrings и co разрешаются в один строковый аргумент, а не в их массив

Желание передать аргументы исполняемому вызову означает, что мы фактически говорим о синтаксисе массива:

  • Как запустить массив (запустить дословный режим)
  • Как разделить массив (передать один аргумент новому исполняемому файлу)
  • Как закончить массив (конец дословно)

(Это нативная концепция для * nix, поскольку exec ожидает массив. В Windows, я думаю, мы вынуждены брать массив, сериализовать его в определенный строковый синтаксис и передавать его через CommandLineToArgvW . Однако .NET предоставляет абстракцию на основе массивов для обоих из них, поэтому массивы по-прежнему имеют наибольший смысл)

Это означает, что нам нужно два выхода:

  • Литерал разделителя массива
  • Конечный литерал массива

Например, скажем:

  • --% перед тем, как команда станет новым дословным синтаксисом
  • Как закончить синтаксис? перевод строки и ; возможно?
  • Как разделить аргументы? может быть?

Тогда возникают следующие вопросы:

  • Как передать буквальный символ ; или новую строку?
  • Как передать буквальное значение ?

Собственная командная строка - это не массив. Это либо строка, либо AST. Поскольку PowerShell ничего не знает об AST в собственных оболочках, остается только строка.

Собственная командная строка - это не массив. Это либо строка, либо AST

Вы не представили никаких оснований для этого утверждения.

Здесь PowerShell создает AST для команды (любого вида, поскольку до времени выполнения она не знает, когда команда определяет, что это за команда):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Parser.cs#L6391 -L6634

Обратите внимание, что элементы команды (включая параметры и аргументы) добавляются в массив.

Вы можете увидеть это в действии в PowerShell следующим образом:

> { ping 192.168.0.1 }.Ast.EndBlock.Statements[0].PipelineElements[0].CommandElements

StringConstantType : BareWord
Value              : ping
StaticType         : System.String
Extent             : ping
Parent             : ping 192.168.0.1

StringConstantType : BareWord
Value              : 192.168.0.1
StaticType         : System.String
Extent             : 192.168.0.1
Parent             : ping 192.168.0.1

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

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L4127 -L4160

А затем это передается в вызов конвейера здесь (обратите внимание, что Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs) - это то место, где передаются аргументы):

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/parser/Compiler.cs#L3798 -L3805

Во время выполнения, когда скомпилированные выражения выполняются, это становится этим вызовом метода , который переходит к этому вызову, который решает, какой командный процессор использовать:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs#L29 -L312

В конце концов, когда вызывается конвейер, этот массив аргументов привязывается к собственной команде с помощью NativeCommandParameterBinder :

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandParameterBinder.cs#L69 -L137

Он принимает массив аргументов, который включает метаданные AST, например, какой тип строки использовался для каждого аргумента, и создает новую строку для вызова аргумента в NativeCommandProcessor здесь:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L1094 -L1166

Это сделано потому, что .NET API, который мы в настоящее время используем для вызова процессов, является старой версией «все аргументы в одной строке», а не новой версией .NET Core, которая позволяет передавать массив и позволяет .NET перестроить вызов. по мере необходимости в зависимости от платформы.

Но использование этого API - это деталь реализации. Например, мы могли бы вызвать fork / exec непосредственно на * nix, как мы это делаем здесь:

https://github.com/PowerShell/PowerShell/blob/f82f74d89811e91613a7450e536d652c5d8f784e/src/powershell/Program.cs#L268 -L330

Обратите внимание, что exec здесь принимает массив - мы сделали это, потому что это самый простой и самый прямой способ передачи аргументов системному вызову, что в конечном итоге является целью дословного синтаксиса аргументов.

@rjmholt :

_Функционально_ все потребности уже удовлетворяются существующими формами строковых литералов, _ если вы передаете встроенную командную строку как одну строку_, что я настоятельно рекомендую, с помощью нового командлета Invoke-NativeShell / ins .

Отправной точкой здесь является _один строка_, а не массив: это _ вся командная строка_, которая должна быть проанализирована _ целевой оболочкой_, и имеет смысл отразить это в PowerShell как таковом, а не пытаться неловко запихнуть другой синтаксис оболочки в PowerShell с чем-то вроде --%

Все, что нужно сделать Invoke-NativeShell , - это передать единственную строку, содержащую всю командную строку собственной оболочки, собственной оболочке через ее интерфейс командной строки:

  • в Unix это означает: передать строку как есть, _ как целую_, как второй элемент аргумента _array_, переданный в exec (где -c является первым элементом массива аргументов, а /bin/sh исполняемый файл); выражается в терминах .NET на простом примере, где printf "'%s'" foo | cat -n - это дословное содержание единственной строки, содержащей передаваемую командную строку (обратите внимание на использование .ArgumentList , а не .Arguments ; удвоение ' s артефакт только в этом примере):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • в Windows это означает: чтобы учесть причуды cmd , заполните аргумент lpCommandLine в CreateProcess следующим образом (при этом lpApplicationName предполагается равным значению переменная окружения ComSpec , которая указывает на cmd.exe ): "/c " + cmdLine , где cmdLine - это вся строка командной строки как есть; выражается в терминах .NET на простом примере, где echo a^&b & ver - это дословное содержимое единственной строки, содержащей передаваемую командную строку:

    • $psi = [Diagnostics.ProcessStartInfo]::new($env:ComSpec); $psi.Arguments = '/c ' + 'echo a^&b & ver'; [Diagnostics.Process]::start($psi).WaitForExit()

      • @ yecril71pl , это подразумевает ответ на ваш вопрос о том, следует ли использовать семантику командной строки или пакетного файла: это первая семантика, которая также используется другими языками сценариев, такими как Python с os.system() ; (возможно, неудачный) вывод состоит в том, что вы не сможете избежать символов % , и что переменные цикла for должны использовать один % (например, %i вместо %%i ); и что вы должны добавить к командам тела цикла префикс @ чтобы они не отображались эхом (например, for %i in (*.txt) do <strong i="56">@echo</strong> %i ).

Этот процесс будет _прозрачным для конечного пользователя_, поэтому все, на чем им нужно сосредоточиться, - это передать _verbatim_ в командной строке нативной оболочки, при этом необходимо только удовлетворить требования _PowerShell_ к синтаксису строкового литерала:

С формой ins @'<newline>...<newline>'@ вы уже _ можете_ использовать командные строки собственной оболочки как есть: см. Выше, как можно использовать различные существующие формы строковых литералов, которые - из-за наличия здесь _interpolating_ строковая форма ( @"<newline>...<newline>"@ ) - включает возможность использования переменных и выражений _PowerShell_ в командной строке (невозможность сделать это - один из основных недостатков --% ).

Мой предыдущий комментарий предлагает _новую общую форму необработанного строкового литерала_, который _отделяется от этого предложения_ (и логика его анализа (надеюсь) четко определена); хотя, как уже говорилось, вызовы Invoke-NativeShell были бы _более удобными_.

Вместо того, что вы уже можете сделать (при условии, что команда Invoke-NativeShell / ins ):

# ALREADY WORKS: Literal here-string
ins @'
python -c 'print("hi")' | cat -n
'@

с предлагаемым новым однострочным необработанным литералом вы можете упростить:

# PROPOSED, more convenient syntax: new raw literal.
ins @' python -c 'print("hi")' | cat -n '@

Если необходимо обсудить особенности этого нового синтаксиса необработанного строкового литерала, позвольте нам сделать это в # 13204, а не здесь.

Итак, перечитывая предложение, чтобы понять исходное намерение, я вижу, что на самом деле он просто требует лучшего синтаксиса для /bin/sh -c '<command>' + версии cmd, а также специальной логики escape-последовательности для каждой целевой оболочки. Я основывал свою первоначальную концепцию на особом синтаксисе и необходимости работать с синтаксическим анализатором и собственным командным процессором, а также на том, что свойство .NET ProcessStartInfo.Arguments очень сложно правильно использовать с точки зрения передачи цитат .

Но на самом деле строка Arguments просто передается дословно . Итак, все, что нам нужно сделать, это передать такую ​​строку напрямую.

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

Некоторые комментарии к комментарию @rjmholt https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

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

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

Оператор -% определяет слишком много способов выхода из своего режима (|, &&, ||,;, новая строка) и разрешает строки внутри него (удаляя их символы ')

Говоря о --% , вы имеете в виду существующий переключатель --% ? Если это так, лучше прояснить это в своем комментарии, поскольку теперь --% предлагается в качестве оператора вызова.

Желание передать аргументы исполняемому вызову означает, что мы фактически говорим о синтаксисе массива:

Предлагается вызвать собственную оболочку (sh или cmd) и передать все буквально после оператора вызова --% , например sh -c ... . Так что это не PowerShell передает аргументы целевому исполняемому файлу. В таком случае, я думаю, нам не нужен _синтаксис массива_, верно?

Как закончить синтаксис? новая строка и; может быть?
Как разделить аргументы? «космос» может быть?

Я думаю, что дословный синтаксический анализ заканчивается на новой строке. Чтобы передать новую строку как часть аргумента, используйте \n я думаю, точно так же, как вы делаете это напрямую в bash.

Собственная командная строка - это не массив. Это либо строка, либо AST

Вы не представили никаких оснований для этого утверждения.

Здесь PowerShell создает AST для команды (любого вида, поскольку до времени выполнения она не знает, когда команда определяет, что это за команда):

Если это предложение будет реализовано, в нем появится специальная конструкция, в которой PowerShell вообще не будет создавать AST.

Рад это слышать, @rjmholt.

У .NET есть некоторая логика для этого, которую мы будем вынуждены переопределить

Я не думаю, что нам действительно нужно что-то переопределить, поскольку команды в моем предыдущем комментарии показывают: в Windows используйте .Arguments для передачи всей строки командной строки в WinAPI; в Unix используйте .ArgumentList для создания аргумента _array_, который используется непосредственно с exec .

Если это предложение будет реализовано, в нем появится специальная конструкция, в которой PowerShell вообще не будет создавать AST.

Что ж, он построил бы другой тип AST, предназначенный для этой цели. Если это не часть AST, то это в основном то же самое, что и комментарий, ничего не происходит.

_Функционально_ все потребности уже удовлетворяются существующими формами строковых литералов, _ если вы передаете встроенную командную строку как одну строку_, что я настоятельно рекомендую, с помощью нового командлета Invoke-NativeShell / ins .

Мне лично нравится идея командлета Invoke-NativeShell , в некотором смысле лучше, чем оператор вызова --% . Основная проблема, которую я слышал об этой идее, заключается в том, что она больше набирает, чем --% + Ctrl + v, и отличие буквальной строки здесь от интерполяции строки здесь - еще одна нагрузка для пользователя (_ Я лично думаю, что ins набирать намного проще, чем --% _).

Основная проблема, которую я слышал об этой идее, заключается в том, что она больше набирает, чем --% + Ctrl + v, и отличие буквальной здесь-строки от интерполяции здесь-строки является еще одним бременем для пользователя (_ Я лично думаю, что ins намного проще напечатать, чем --% _).

Может ПСРЛ поможет. Подобно проблеме с обработчиком ключей для окружения вставленных путей, PSRL может прочитать CommandAst , увидеть его ins и правильно экранировать вставленную строку.

Что ж, он построил бы другой тип AST, предназначенный для этой цели. Если это не часть AST, то это в основном то же самое, что и комментарий, ничего не происходит.

Конечно, но дерево будет тривиальным. Он не будет отражать значение команд, которые должны быть выполнены.

Запустите процесс cmd.exe перенаправить stdin / stdio, и мы можем буквально использовать строку для вызова-NativeCommand так же, как и bash

Рад это слышать, @ daxian-dbw.
Обожаю идею,

различение буквальной строки здесь от интерполяции строки здесь - еще одна проблема для пользователя

Я думаю, что это _plus_, потому что - в отличие от --% - использование @"<newline>...<newline>"@ позволяет напрямую включать значения переменных и выражений _PowerShell_ в собственную командную строку.

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

Чтобы помочь пользователям вырезать и вставить, предлагаемая функция PSReadLine по умолчанию должна иметь значение @'<newline>...<newline>'@ (дословно).


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

Но чтобы побудить пользователей использовать _own_ синтаксис PowerShell, который по определению является _ кроссплатформенным_, он должен работать предсказуемо .
Для этого # 1995 должен быть исправлен , в идеале напрямую, допуская критическое изменение, или, если это не вариант, через минимальную церемонию _opt-in_.

Подвести итоги:

  • Что-то вроде --% (новая форма оператора, предлагаемая здесь), по определению _ специфичная для платформы_ функция, никоим образом не заменяет исправление # 1995 .

  • Invoke-NativeShell ( ins ) позволяет избежать всех ограничений предложенного оператора --% и предоставляет решение "люка", упомянутое @joeyaiello : быстрый способ выполнить написанную командную строку для собственной оболочки без необходимости адаптации ее к синтаксису PowerShell.

  • --% должна оставаться только в исходной форме _аргумента_ и должна оставаться функцией _Windows_ (что она _эффективно_, хотя и не _технически_) - другими словами: никаких изменений не требуется.

    • Как только # 1995 исправлен, единственная цель --% будет заключаться в том, чтобы приспособить случаи _edge в Windows_: позволяя вам контролировать явное цитирование командной строки, передаваемой мошенническим программам, таким как msiexec которые требуют очень специфические формы цитирования внутри аргументов.

Одна из других функций, которые мне действительно нужны в PS v7.x, - это возможность иметь собственную команду, выходящую с кодом ошибки, останавливающую мой скрипт, например set -e как обсуждается в этом RFC . Если решением будет что-то вроде Invoke-NativeCommand , не запутается ли это с Invoke-NativeShell ?

Просто пытаюсь воспроизвести фильм немного вперед, потому что, насколько мне нужна встроенная функция вызова, я действительно хочу, чтобы мои сценарии сборки и тестирования выдавали ошибки при вызове msbuild, cl, cmake, Conan, npm и т. Д., И эти собственные команды выходят с ненулевой код выхода без необходимости постоянно проверять $ LASTEXITCODE. Если реализация осуществляется через другую переменную предпочтений, например, $PSNativeCommandInErrorActionPreference тогда я полагаю, что это повлияет на вызов собственной оболочки - это собственная «команда» и все такое?

Основная проблема, которую я слышал об этой идее, заключается в том, что она больше набирает, чем --% + Ctrl + v, и отличие буквальной здесь-строки от интерполяции здесь-строки является еще одним бременем для пользователя (_ Я лично думаю, что ins намного проще напечатать, чем --% _).

Может ПСРЛ поможет. Подобно проблеме с обработчиком ключей для окружения вставленных путей, PSRL может прочитать CommandAst , увидеть его ins и правильно экранировать вставленную строку.

Но что насчет:

$c = gcm ins
& $c ...

PSRL будет трудно понять, что там происходит. Он должен быть с отслеживанием состояния, разрешить псевдонимы для invoke-nativeshell, затем выяснить, что вы хотите вызвать commandinfo, а затем удалить аргументы? Мне очень-очень не нравится идея особого исключения фальшивой команды.

различение буквальной строки здесь от интерполяции строки здесь - еще одна проблема для пользователя

Я думаю, что это _plus_, потому что - в отличие от --% - использование @"<newline>...<newline>"@ позволяет напрямую включать значения переменных и выражений _PowerShell_ в собственную командную строку.

Я не слежу за тобой @ mklement0. Почему нельзя научить --% этому? Я уже приводил это как пример использования.

Мне очень-очень не нравится идея особого исключения фальшивой команды.

Здесь нет поддельной команды, только добросовестный командлет Invoke-NativeShell псевдонимом ins .

gcm ins ( Get-Command ins ) предоставит информацию об псевдониме ins в виде экземпляра [System.Management.Automation.AliasInfo] , который позже можно передать & для вызова, как и с любой другой командой или псевдонимом.

Таким образом, ваш пример будет работать именно так, как предполагалось - просто если вы используете обход gcm ( Get-Command ), вам нужно будет указать соответствующий синтаксис строкового литерала _yourself_ без _ необязательные вспомогательные строительные леса_, которые PSRL _ может_ предоставить, если будет реализовано предложение @SeeminglyScience :

$c = gcm ins
& $c @'
python -c 'print("hi")' | cat -n
'@

Или, если реализован новый необработанный синтаксис строкового литерала, предложенный в # 13204:

$c = gcm ins
& $c @' python -c 'print("hi")' | cat -n '@

Конечно, если вы знакомы с синтаксисом строковых литералов PowerShell, подойдет и обычная строка в одинарных кавычках: просто удвойте встроенную ' внутри общей строки '...' :

$c = gcm ins
& $c 'python -c ''print("hi")'' | cat -n'

Почему нельзя научить --% этому?

  • --% отсутствует закрывающий разделитель, который не позволяет использовать его в конвейерах Powershell, что является недопустимым ограничением (обратите внимание, что (...) enclosure _не_ вариант, поскольку это подразумевает предварительный сбор всего вывода строк в памяти, а не _streaming_ обработка).

  • В Unix --% не может _обо_ поддерживать (без экранирования) $ как метасимвол PowerShell _и_ как метасимвол оболочки POSIX.

Опять же: нет _не_ оправдания и никакой пользы от внедрения _различного синтаксиса_ оболочки в PowerShell - передайте командную строку собственной оболочки _ в виде строки_ в ins , и все концептуальные проблемы исчезнут.

Invoke-NativeShell подразумевает, что PowerShell не является собственной оболочкой. Всегда ли это предположение верно и всегда будет верным?

@oising вариант использования - это кто-то копирует и вставляет собственную команду из README или чего-то еще. Если они делают что-то более сложное, чем ввод ins SPACE CTRL + v, они просто построят и экранируют свою собственную строку, как обычно.

@ mklement0

Но чтобы побудить пользователей использовать _own_ синтаксис PowerShell, который по определению является _ кроссплатформенным_, он должен работать предсказуемо .
Для этого # 1995 должен быть исправлен , в идеале напрямую, допуская критическое изменение, или, если это не вариант, через минимальную церемонию _opt-in_.

Это отдельно от обсуждения ins / --% верно? Если вы не согласны, можете ли вы пояснить, почему эта проблема повлияет на новую команду / синтаксис?

@SeeminglyScience , да, это отдельная тема, но в этой теме это неясно.
Единственная причина, по которой здесь возник # 1995, заключается в том, что он был изначально закрыт в пользу этой проблемы (хотя, к счастью, с тех пор был открыт снова), и здесь OP по-прежнему заявляет, что эта проблема исправит ее («Это также должно решить эти проблемы:») .
Поэтому я хотел подчеркнуть, что ins / --% _не_ адрес # 1995, который требует срочно собственного исправления.
(Только если собственная передача аргументов PowerShell работает должным образом, мы можем с чистой совестью рекомендовать пользователям полностью изучить и принять собственный синтаксис PowerShell, что нам следует.)

@ SP3269 , мы сможем перейти этот мост, когда доберемся до него 😁.

Но серьезно:

Если вы подумаете о «родном» как о «написанном специально для хост-платформы [семейства]», это имя все равно будет соответствовать, даже если PowerShell когда-либо станет системной оболочкой по умолчанию на данной платформе (здесь есть надежда, но я не думаю, что это реально, по крайней мере, не в обозримом будущем).

Что касается альтернатив:

  • Invoke-[System]DefaultShell было бы наиболее точным именем (хотя однозначно, что оно больше не будет применяться, если PowerShell когда-либо станет системной оболочкой по умолчанию), но «родной», кажется, более часто используемый термин в мире PowerShell.

  • Invoke-LegacyShell имеет какое-то оправдание в Windows, но я не думаю, что пользователям Unix этот термин понравится.

Я думаю, что начинаю распутывать полное замешательство, которое испытываю, пытаясь отследить все мотивы, идеи и проблемы здесь. Кажется, что мы (по крайней мере, @ mklement0 и я) смотрим на это как на два разных решения двух разных взглядов на одну и ту же концептуальную проблему. Я предлагал использовать --% в качестве подсказки для _parser_, чтобы изменить способ синтаксического анализа в сочетании с вызовом оператора & (не обязательно с использованием --% standalone.) С другой стороны, Майкл, похоже, смотрит на ins как на добавляемый командлет для замены собственного внутреннего встроенного брокера команд и синтаксического анализатора аргументов powershell, и вместо этого не пытается изменить синтаксический анализатор powershell, используя strings / here -strings для захвата предполагаемых параметров (которые, как я говорю, можно использовать также с --% .)

-% отсутствует закрывающий разделитель, что не позволяет использовать его в конвейерах Powershell

Этого я совсем не понимаю. Закрывающего разделителя не хватает не больше, чем у ins / invoke-nativecommand. Если вам нужно разграничить или подавать из LHS, используйте здесь-строки, иначе это будет рассматриваться как один оператор.

Независимо от того, какой способ выбран, кажется, что для отправки командной строки потребуется выбрать собственную оболочку. Я предлагаю использовать переменные среды COMSPEC в Windows (по умолчанию %systemroot%\system32\cmd.exe ) и SHELL в Linux (по умолчанию /bin/bash ) - если SHELL этого не делает. t существует, мы должны нацелить /bin/sh (что в большинстве случаев в любом случае является символической ссылкой на bash.)

-% отсутствует закрывающий разделитель, что не позволяет использовать его в конвейерах Powershell

Этого я совсем не понимаю. Закрывающего разделителя не хватает не больше, чем у ins / invoke-nativecommand. Если вам нужно разграничить или подавать из LHS, используйте здесь-строки, иначе это будет рассматриваться как один оператор.

По сути, вы не можете вызывать такие вещи, как cmd --% /c someapp | someOtherApp и заставлять cmd обрабатывать конвейер; PowerShell по-прежнему интерпретирует конвейер (и некоторые другие вещи, такие как && и || которые в противном случае были бы полезны для пользователей Unix) как токен PowerShell вместо того, чтобы передавать его собственной команде как часть строка аргумента.

и SHELL в Linux (по умолчанию /bin/bash )

Нет: _system_ оболочка на Unix-подобных платформах _invariably_ /bin/sh .
Это отличается от данной интерактивной оболочки _user_ (отраженной в $env:SHELL - но, к сожалению, в настоящее время нет, если _PowerShell_ является оболочкой пользователя, см. # 12150), которая (а) настраивается и (б) часто _defaults to_ /bin/bash , но даже это не является данностью, о чем свидетельствует недавний переход macOS на /bin/zsh в качестве интерактивной оболочки по умолчанию для новых пользователей.

Я предлагаю использовать переменные среды COMSPEC в Windows

Хороший момент, это лучший способ обратиться к системной оболочке (интерпретатору команд) в Windows - я соответствующим образом обновил образец команды выше .

Одна из других функций, которые мне действительно нужны в PS v7.x, - это возможность иметь собственную команду, выходящую с кодом ошибки, останавливающую мой скрипт, например set -e как обсуждается в этом RFC . Если решением будет что-то вроде Invoke-NativeCommand , не запутается ли это с Invoke-NativeShell ?

Просто пытаюсь воспроизвести фильм немного вперед, потому что, насколько мне нужна встроенная функция вызова, я действительно хочу, чтобы мои сценарии сборки и тестирования выдавали ошибки при вызове msbuild, cl, cmake, Conan, npm и т. Д., И эти собственные команды выходят с ненулевой код выхода без необходимости постоянно проверять $ LASTEXITCODE. Если реализация осуществляется через другую переменную предпочтений, например, $PSNativeCommandInErrorActionPreference тогда я полагаю, что это повлияет на вызов собственной оболочки - это собственная «команда» и все такое?

Для этого в нашем модуле build есть Start-NativeExecution .

Я это видел. Как именно вы бы скомбинировали Start-NativeExecution с Invoke-NativeShell если бы вы хотели, чтобы последний эффективно выдавал ненулевой код выхода? Я действительно надеюсь, что что-то вроде PR # 3523 войдет в состав этой функции вызова, потому что я хочу, чтобы они работали вместе. Я предполагаю, что если собственный вызов будет реализован как командлет (Invoke-NativeShell vs -%), тогда -ErrorAction Stop можно реализовать, чтобы он генерировал (завершающую ошибку) с ненулевым кодом выхода.

-% отсутствует закрывающий разделитель, что не позволяет использовать его в конвейерах Powershell

Этого я совсем не понимаю. Закрывающего разделителя не хватает не больше, чем у ins / invoke-nativecommand. Если вам нужно разграничить или подавать из LHS, используйте здесь-строки, иначе это будет рассматриваться как один оператор.

По сути, вы не можете вызывать такие вещи, как cmd --% /c someapp | someOtherApp и заставлять cmd обрабатывать конвейер; PowerShell по-прежнему интерпретирует конвейер (и некоторые другие вещи, такие как && и || которые в противном случае были бы полезны для пользователей Unix) как токен PowerShell вместо того, чтобы передавать его собственной команде как часть строка аргумента.

Да, я понимаю текущее поведение @ vexx32 . Но мы не говорим (по крайней мере, я не) о текущем поведении --% - мы говорим об улучшении его, не так ли? Почему у меня такое ощущение, что мы все здесь разговариваем друг с другом? :)

Как я уже сказал выше, мы могли бы улучшить & для работы с --% так что это возможно . Я также думаю, что нам нужно четко понимать, что такое неявный собственный exec и явный вызов оболочки с аргументами. Это постоянный источник путаницы - даже для опытных пользователей Windows - что каким-то образом cmd.exe (оболочка) необходим для «запуска» других исполняемых файлов; то же самое относится к linux / osx.

Да, я понимаю _current_ поведение @ vexx32 . Но мы не говорим (по крайней мере, я не) о _current_ поведении --% - мы говорим об улучшении его, не так ли? Почему у меня такое ощущение, что мы все здесь разговариваем друг с другом? :)

Таким образом, текущий шаг для настройки --% заключается в том, что если это то место, где обычно находится имя команды, все справа от него анализируется как есть (до новой строки) и отправляется непосредственно в bash / cmd. Здесь нет места синтаксису PowerShell, например, здесь-строкам и тому подобному, потому что тогда он имеет те же проблемы, что и --% и собственный командный процессор в настоящее время.

Я это видел. Как именно вы бы скомбинировали Start-NativeExecution с Invoke-NativeShell если бы вы хотели, чтобы последний эффективно выдавал ненулевой код выхода? Я действительно надеюсь, что что-то вроде PR # 3523 войдет в состав этой функции вызова, потому что я хочу, чтобы они работали вместе. Я предполагаю, что если собственный вызов будет реализован как командлет (Invoke-NativeShell vs -%), тогда -ErrorAction Stop можно реализовать, чтобы он генерировал (завершающую ошибку) с ненулевым кодом выхода.

В настоящее время это будет Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> } .

Этого я совсем не понимаю. Закрывающего разделителя не хватает не больше, чем у ins / invoke-nativecommand. Если вам нужно разграничить или подавать из LHS, используйте здесь-строки, иначе это будет рассматриваться как один оператор.

Invoke-NativeShell не _ нуждается_ в закрывающих разделителях, потому что это обычная команда, тогда как ужас --% - нет.

и SHELL в Linux (по умолчанию /bin/bash )

Нет: _system_ оболочка на Unix-подобных платформах _invariably_ /bin/sh .

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

Идея Invoke-NativeShell состоит в том, чтобы предоставить удобную оболочку для _CLI_ собственной оболочки.

  • В Unix этого вполне достаточно, и создание вспомогательного файла сценария не только приводит к дополнительным накладным расходам, но и сообщает случайное значение в $0 (путь к файлу сценария), тогда как вызов через CLI предсказуемо содержит /bin/sh и даже может быть установлен явно, следуя за аргументом кода другим аргументом (например, sh -c 'echo $0' foo )

    • (В стороне: создание сценария оболочки со строкой shebang #!/bin/sh означает нацеливание на системную оболочку по полному пути так же явно, как и прямой вызов /bin/sh ).
  • В Windows выполнение с помощью командного файла _ принесет_ преимущества (но не для делегирования задачи поиска системной оболочки, что предсказуемо делает $env:ComSpec ), поскольку это позволит избежать проблем с экранированием % и поведение for упомянутое выше .

Чтобы решить последний вопрос в качестве любезности (чтобы пользователям не приходилось писать свои собственные командные файлы вспомогательных файлов), для концептуальной ясности мы могли бы предложить
-AsBatchFile как _opt-in_.

Тем не менее, если существует консенсус, что большинство общедоступных командных строк cmd используемых для вырезания и вставки, написаны с учетом семантики _batch-file_ ( %%i а не %i как переменная цикла, возможность дословно экранировать % как %% ), возможно, неизменно используя aux. Пакетный файл _в Windows_ (при сохранении использования интерфейса командной строки в Unix) - лучшее решение - я не сильно думаю об этом, но он должен быть четко задокументирован, особенно учитывая, что это означает демонстрацию поведения, отличного от других языков сценариев.

@rkeithhill

Для меня RFC интеграции собственных ошибок, на который вы ссылаетесь, заслуживает внимания так же срочно, как и # 1995:

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

Быть первоклассным гражданином означает: _ прямой_ вызов (при этом & требуется только по _синтаксическим_ причинам, ситуативно), а не _ через командлет_.
Другими словами: никогда не должно быть командлета Invoke-NativeCommand или Start-NativeExecution .

(Кстати: Start-NativeExecution неправильно названо, как и многие функции в модуле build ; Start сигнализирует _асинхронную_ операцию, тогда как большинство этих функций _синхронные_ - см. Обсуждение Start-Sleep адресу https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474).

Следовательно, Invoke-NativeShell требует особого рассмотрения: PowerShell, как он уже делает, установит $LASTEXITCODE на основе кода выхода, сообщенного исполняемым процессом собственной оболочки ( cmd / sh ) вызывается.

Затем механизм, предложенный в RFC, будет воздействовать на него, как на непосредственно вызываемые исполняемые файлы (например, если установлено $PSNativeCommandErrorAction = 'Stop' , произойдет ошибка завершения сценария, если $LASTEXITCODE равно нулю. )

(Небольшое отступление - этому обсуждению здесь не место: насчет механизма _per-call_: что-то вроде
/bin/ls nosuch || $(throw 'ls failed) работает, как и /bin/ls nosuch || $(exit $LASTEXITCODE) (эквивалент /bin/ls nosuch || exit оболочки POSIX); # 10967 обсуждает, почему мы, к сожалению, не можем избежать $(...) (без серьезных изменений грамматики); точно так же, вероятно, нельзя избежать необходимости явно ссылаться на $LASTEXITCODE .)

(В стороне: создание сценария оболочки со строкой shebang #!/bin/sh означает нацеливание на системную оболочку по полному пути так же явно, как и прямой вызов /bin/sh ).

Я не это хотел сказать. Я хотел сказать, что PowerShell не нужно знать, что это за оболочка по умолчанию, потому что операционные системы имеют эту встроенную функцию. Linux знает, как выполнить исполняемый сценарий, если он не начинается с #! или начинается с что-то еще, например #!/usr/bin/env python и Windows знает, что делать, чтобы вызвать .CMD скрипт, поэтому нам не следует заглядывать в %COMSPEC% 'n'stuff ИМХО. Windows также может выполнять другие сценарии, но это основано на расширении файла сценария, поэтому очевидно, что это не относится к этому случаю.

Опять же, мандат Invoke-NativeShell должен вызывать _CLI_ собственной оболочки, а не создавать вспомогательные файлы сценариев (с побочными эффектами) за кулисами (за исключением различных причин, как описано выше ).

Что касается надежного определения системной оболочки, здесь нет проблемы, которую нужно решить: жесткое кодирование /bin/sh в Unix совершенно уместно, как и консультация с $env:ComSpec в Windows.

Нет, платформы Unix на уровне системных вызовов _не_ знают, как выполнять исполняемый файл с открытым текстом _без строки shebang_; то, что вы все еще _ можете_ вызывать такие файлы, является удобной функцией POSIX-подобных оболочек: они пробуют exec , а затем _ возвращаются_ к интерпретации таких файлов как написанных _ для них_; то есть любая запущенная оболочка, подобная POSIX, завершает выполнение файла - тогда как PowerShell просто _fails_, потому что он не реализует эту резервную копию любезности (вы получите Program 'foo' failed to run: Exec format error ).

Излишне говорить, что создавать такие сценарии не рекомендуется.

Нет, платформы Unix на уровне системных вызовов _не_ знают, как выполнять исполняемый файл с открытым текстом _без строки shebang_; то, что вы все еще _ можете_ вызывать такие файлы, является удобной функцией POSIX-подобных оболочек: они пробуют exec , а затем _ возвращаются_ к интерпретации таких файлов как написанных _ для них_; то есть любая запущенная оболочка, подобная POSIX, завершает выполнение файла - тогда как PowerShell просто _fails_, потому что он не реализует эту резервную копию любезности (вы получите Program 'foo' failed to run: Exec format error ).

csh не похож на POSIX, и он не дает сбоев в exec простой скрипт. Он вызывает sh . Так и perl .

Я надеюсь, что это очевидно, что это подтверждает мою точку зрения: _ каждая оболочка_ сама решает, что ей делать, а разные оболочки / языки сценариев делают разные вещи; PowerShell в настоящее время просто терпит неудачу - если ей нужно было сделать выбор, и вы хотели, чтобы этот выбор был /bin/sh , мы вернулись на круги своя: следует принять /bin/sh .

Я надеюсь, что это очевидно, что это подтверждает мою точку зрения: _ каждая оболочка_ сама решает, что ей делать, а разные оболочки / языки сценариев делают разные вещи; PowerShell в настоящее время просто терпит неудачу - если ей нужно было сделать выбор, и вы хотели, чтобы этот выбор был /bin/sh , мы вернулись на круги своя: следует принять /bin/sh .

exec в perl - это прямой системный вызов, perl никоим образом не изменяет его. В частности, он не _выбирает_ интерпретатор.

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

Может быть, это только я, но я бы обменял тысячу " на один -f .

@ yecril71pl

exec в perl - это прямой системный вызов, perl никоим образом не изменяет его. В частности, он не выбирает переводчика.

Это не может быть правдой, если он работает со скриптами без строки #! . Системный вызов exec в Linux не работает без #! , так как можно легко проверить:

user@Programming-PC:/tmp/exec_test$ ls
test.c  testscript
user@Programming-PC:/tmp/exec_test$ cat test.c
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv) {
  char *binaryPath = "./testscript";

  execl(binaryPath, binaryPath, NULL);

  perror("Error");

  return 0;
}
user@Programming-PC:/tmp/exec_test$ gcc test.c -o testexecutable
user@Programming-PC:/tmp/exec_test$ chmod +x testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
echo test from script
user@Programming-PC:/tmp/exec_test$ #works from shell:
user@Programming-PC:/tmp/exec_test$ ./testscript 
test from script
user@Programming-PC:/tmp/exec_test$ #doesn't work via exec syscall:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
Error: Exec format error
user@Programming-PC:/tmp/exec_test$ vim testscript 
user@Programming-PC:/tmp/exec_test$ cat testscript 
#!/bin/sh
echo test from script
user@Programming-PC:/tmp/exec_test$ #exec syscall works with #! line:
user@Programming-PC:/tmp/exec_test$ ./testexecutable 
test from script
user@Programming-PC:/tmp/exec_test$ uname -a
Linux Programming-PC 4.4.0-176-generic #206-Ubuntu SMP Fri Feb 28 05:02:04 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
user@Programming-PC:/tmp/exec_test$ 

Изменить: я только что заметил, что если вы используете библиотечную функцию execlp то согласно странице руководства:

Если заголовок файла не распознан (попытка execve (2) завершилась ошибкой ENOEXEC), эти функции выполнят оболочку (/ bin / sh) с путем к файлу в качестве первого аргумента. (Если эта попытка не удалась, дальнейший поиск не производится.)

Однако это особенность не "linux", а библиотеки gnu c - и опять же, согласно странице руководства, она явно использует / bin / sh, а не какую-то определяемую пользователем оболочку. (Зафиксировано в исходном коде posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) и sysdeps / unix / sysv / linux / paths.h (или sysdeps / generic / paths.h - я не знаю, какой заголовок использованный ...) ( #define _PATH_BSHELL "/bin/sh" ))

Однако это особенность не "linux", а библиотеки gnu c - и опять же, согласно странице руководства, она явно использует / bin / sh, а не какую-то определяемую пользователем оболочку. (Жестко закодировано в источнике posix/execvpe.c ( new_argv[0] = (char *) _PATH_BSHELL; ) и sysdeps / unix / sysv / linux / paths.h (или sysdeps / generic / paths.h - я не знаю, какой заголовок используется ...) ( #define _PATH_BSHELL "/bin/sh" ))

Поскольку он исходит от sysdeps/**/linux , это вещь Linux. Это также может быть общая вещь, но ее можно переопределить - очевидно, не локально, а для каждой ОС.

Да, я понимаю _current_ поведение @ vexx32 . Но мы не говорим (по крайней мере, я не) о _current_ поведении --% - мы говорим об улучшении его, не так ли? Почему у меня такое ощущение, что мы все здесь разговариваем друг с другом? :)

Таким образом, текущий шаг для настройки --% заключается в том, что если это то место, где обычно находится имя команды, все справа от него анализируется _ as is_ (до новой строки) и отправляется непосредственно в bash / cmd. Здесь нет места синтаксису PowerShell, например, здесь-строкам и тому подобному, потому что тогда он имеет те же проблемы, что и --% и собственный командный процессор в настоящее время.

Господи ... Все просто пытаются меня троллить? 😄 Нет, Патрик - если вы хотите использовать выражения PowerShell, используйте здесь-строки с --% . Если вы хотите многострочный, используйте здесь-строки. Если вам нужна многострочная строка без подстановки переменных, вы можете использовать одинарные кавычки here-strings.

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

изменить: последнее слово

Название этого пункта - «вызов собственного оператора». Я вижу упущенную возможность, когда у нас уже есть оператор вызова & и у нас уже есть токен, предназначенный для передачи аргументов собственным командам, --% . Прямо сейчас использование & с --% НЕ распознается и, как таковое, НЕ вносит критических изменений, если мы решим включить этот сценарий и дать ему конкретное поведение для устранения проблем, обсуждаемых здесь.

если вы хотите использовать выражения PowerShell, используйте здесь-строки с --% . Если вы хотите многострочный, используйте здесь-строки.

Суть оператора в том, что он не анализирует буквально какой-либо синтаксис PowerShell. Нет такой вещи, как здесь-струны. Не существует строковых констант в одинарных кавычках. Это "отправить текст справа как есть" не "оценить выражение справа, преобразовать в строку и отправить его значение"

Так, например, если у вас было это:

--% @'
echo my native command
'@

это будет переводить на:

cmd.exe /c "@'"
echo my native command
'@

За исключением того, что вы получите ошибку парсера, потому что '@ будет просто началом одиночной строковой константы в кавычках, содержащей @ .

Я думаю, что, возможно, вы представляете реализацию по-другому, так что это более или менее то же самое, что и презентация для ins , но выше обсуждается текущая презентация для --% .

Также стоит отметить, что так работает и существующий функционал. Этот пример работает в основном так же:

cmd /c --% @'
echo my native command
'@

Я повторяю:

Прямо сейчас использование & с -% НЕ распознается / не определено и, как таковое, НЕ внесет критических изменений, если мы решим включить этот сценарий и дать ему конкретное поведение для устранения проблем, обсуждаемых здесь .

Позвольте мне прояснить это еще яснее: разрешить здесь-строкам следовать за --% при использовании с &

Да, я пропустил это, извините @oising.

Что касается этой идеи, я думаю, что было бы немного запутать, если бы --% означало прекращение синтаксического анализа в некоторых контекстах и ​​«лучше справлялось бы с собственной привязкой аргументов» в других (аналогично с тем, чтобы заставить его работать как Invoke-NativeShell если это вы имеете в виду).

Спасибо, что копали глубже, о exec , @TSlivede. Позвольте мне попытаться подвести итог и, надеюсь, закрыть эту касательную; относится как к Linux, так и к macOS:

В Unix exec в их имени (большинство из них в качестве префикса).

  • _System_ функции ( man section 2 ), execve в его основе, _fail_, когда вы пытаетесь выполнить сценарий без shebang.

  • Среди _library_ функций, которые _страивают в системные функции_ ( man section 3 - man 3 exec ), только те, у которых есть p (для "пути" я Предположим) имеют запасной вариант перехода к системной оболочке (например, execlp ).

    • Реализации этих функций в библиотеке GNU жестко запрограммированы на /bin/sh , как показал @TSlivede , тогда как реализации на основе BSD используют только sh и полагаются на sh $env:PATH (хотя, что любопытно, на странице man указано /bin/sh ).

Как уже говорилось, для _direct Execute_ основные POSIX-подобные оболочки ( bash , dash , ksh и zsh ) возвращаются к выполнению сценариев без использования shebang. _сами_, что подразумевает, что они _не_ используют запасные варианты среди библиотечных функций exec ; в отличие от этого, perl для своей собственной функции exec полагался на запасной вариант; аналогично, exec _builtins_ основных POSIX-подобных оболочек полагаются на резерв, за исключением bash .

Поскольку в Unix использование файла сценария за кулисами не требуется, мы можем сделать те же предположения о пути к системной оболочке, что и функции резервной библиотеки, и я рекомендую /bin/sh вместо sh для безопасность и предсказуемость:

Несмотря на то, что спецификация POSIX _ не_ предписывает расположение sh (то есть, строго говоря, функции библиотеки BSD являются совместимыми):

  • /bin/sh можно с уверенностью предположить, потому что это _de facto_ стандартное расположение, не в последнюю очередь из-за того, что при написании переносимой оболочки Unix _necessitates_ ссылается на sh по его полному пути, учитывая, что строки shebang поддерживают только _full, literal_ пути ( #!/bin/sh ).

  • И наоборот, полагаться на поиск sh через $env:PATH может быть угрозой безопасности, учитывая, что $env:PATH можно манипулировать для вызова другого sh .

Тот же риск применяется к обнаружению cmd.exe через $env:ComSpec , кстати, поэтому, возможно, лучший способ - вызвать функцию GetSystemDirectory WinAPI и добавить \cmd.exe к его результат (нам все равно нужен запасной вариант, если $env:ComSpec не определено).

/bin/sh можно предположить с уверенностью, потому что это _de facto_ стандартное расположение, не в последнюю очередь потому, что при написании переносимой оболочки Unix _necessitates_ ссылается на sh по его полному пути, учитывая, что строки shebang поддерживают только _full, literal_ пути ( #!/bin/sh ).

Обычно вы говорите #!/usr/bin/env sh (кроме системных скриптов).

  • Обычно вы _d't_: поиск в Google "#! / Bin "#! / Usr / bin / env sh" дает около 34 100 совпадений.

  • Обычно вы _не должны_: вы хотите предсказуемо нацеливаться на _the_ системную оболочку, /bin/sh , а не то, что sh утилита оказывается первой в $env:PATH .

Единственная причина настроить таргетинг на исполняемый файл с именем sh - это переносимый таргетинг на оболочку _system_ только с наименьшим общим знаменателем-предположением-функциями POSIX, то есть /bin/sh .

Обычно вы _d't_: поиск в Google "#! / Bin "#! / Usr / bin / env sh" дает около 34 100 совпадений.

Невозможно ограничить веб-поиск пользовательскими сценариями, особенно потому, что многие пользовательские сценарии вообще не помечены как исполняемые, и даже если они помечены, они могут полагаться на execlp ; но даже если это сказано в большинстве пользовательских скриптов, мы не должны принимать _customary_ за _normal_. Сценарии, запускаемые PowerShell, являются пользовательскими сценариями; когда пользователям нужна оболочка, они вызывают sh , а не /bin/sh , если только они не параноики.

@oising

Передача командной строки собственной оболочки _ в виде одной строки_ (в любой строковой (-литеральной) форме проще всего) и _только_ в виде одной строки (за исключением дополнительных аргументов передачи в командную строку, поддерживаемых в Unix ( только) обсуждалось выше ) - если вы согласны с этим - звучит как общий язык.

Передача командной строки в виде _ одной строки_ является предварительным условием для:

  • возможность включить такой вызов в конвейер _PowerShell_.
  • возможность включения переменных и выражений _PowerShell_ в командную строку собственной оболочки.

Передача одной строки - это _прямое концептуальное выражение_ того, что будет делать вызов; Напротив, попытка каким-либо образом напрямую включить синтаксис другой оболочки непосредственно в PowerShell с _индивидуальными_ (необязательно голыми словами) аргументами приводит к неразрешимым проблемам, которые приводят к запутанному компромиссу с фундаментальными ограничениями, которые уже воплощены в существующем --% .


Хотя вы можете возразить, что тогда не так уж важно, выберем ли мы _operator_ или _cmdlet_ для передачи этой однострочной командной строки (за исключением вопросов _discoverability_), у меня есть концептуальные опасения по поводу использования обоих & и --% (которым предшествует & ):

  • Проблема по поводу & : если нам нужно передать строку, заключенную в кавычки, содержащую командную строку, мы фактически работаем в режиме _expression_, тогда как & настоящее время подразумевает режим _argument_ (что означает: _индивидуальные_ аргументы, цитирование только при необходимости). Таким образом, участие & сбивает с толку.

  • Проблема, связанная с --% связана с происхождением только для Windows и запутанной семантикой --% как символа остановки анализа; было бы особенно запутанно, если бы --% в качестве символа остановки анализа работал в режиме _argument_ (например,
    & /bin/ls --% dir1 dir2 ), тогда как & --% работает в режиме _expression_ (например, & --% '/bin/ls dir1 dir2 >out.txt' )


Позвольте мне сделать шаг назад и изучить --% , символ остановки синтаксического анализа, как он сейчас реализован:

Это была попытка решить две совершенно не связанные проблемы (в то время только для Windows):

  • (a) Вариант использования, который также применим к другим платформам: позвольте мне повторно использовать _командные строки_, написанные для cmd.exe / собственной оболочки как есть, поэтому мне не нужно адаптировать их к синтаксису PowerShell.

  • (b) Проблема, которая всегда связана только с Windows: позвольте мне вызвать _rogue CLI_ (то есть вызов _single_ внешнего консольного приложения), для которого требуется очень специфический стиль цитирования, который не может обеспечить универсальное закулисное повторное цитирование PowerShell. , позволив мне создать командную строку, которая в конечном итоге передается WinAPI для таких CLI _verbatim_. (Чтобы ограничить формулировку проблемы этим, предполагается, что повторное цитирование PowerShell обычно работает правильно, чего никогда не было - это тема # 1995).

--% был реализован как запутанная _конфляция_ попыток решения обоих (а) и (б), и в итоге ни одна из проблем не решена должным образом:

  • Он рекламировался как решение (а), но на самом деле имеет серьезные ограничения, потому что _ единственная функция cmd exe, которую он эмулирует, - это расширение ссылок на переменные среды в стиле %...% -стайл_:

    • Он поддерживает только команду _single_ cmd условии, что |
      && и || , даже если они не были реализованы в то время) неявно завершают часть pass-to- cmd .

    • _Single_ & - который является cmd s разделителем нескольких команд - _is_ прошел, но это тоже _не_ привело к поддержке нескольких команд, потому что - поскольку cmd isn ' t действительно задействован - & передается _verbatim целевой программе_; например
      echoArgs.exe --% a & b не отображает a затем вызывает b , передает дословные аргументы a , & и b в echoArgs .

    • cmd escape-символ ( ^ ) в аргументах без кавычек не учитывается.

    • Как следствие, токены %...% которые ссылаются на существующие переменные среды, _инвариабельно_ раскрываются; попытка предотвратить это с помощью ^ не работает должным образом (например,
      echoArgs.exe Don't expand %^USERNAME% , который в cmd предотвращает расширение _ и удаляет ^ _, сохраняет ^ при вызове из PowerShell как echoArgs.exe --% Don't expand %^USERNAME%

  • Как решение (b), он также не оправдал себя:

    • Как и в случае использования (a), нет возможности включать переменные и выражения _PowerShell_ в команду - кроме случаев, когда вы сначала неловко определяете _aux. environment_ variable_, на которую вы затем ссылаетесь через %...% после --% ; например
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

Правильным решением для (а) было бы Invoke-NativeShell / ins , как обсуждается здесь.

Правильным решением для (b) было бы:

  • поддерживать --% как особый только в качестве аргумента _first_
  • и, как и в случае с ins , требовать, чтобы за ней следовала _ единственная строка_, составляющая сквозную командную строку _ как целое_, опять же с возможностью включения переменных и выражений _PowerShell_ путем интерполяции строк (или другие способы построения строки)
  • например, msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" , где $loc содержит 'c:\foo\bar none' , приведет к передаче дословной командной строки /i installer.msi INSTALLLOCATION="c:\foo\bar none" , удовлетворяющей нестандартному требованию msiexec что в аргументах <prop>=<value> только часть <value> заключена в двойные кавычки.

Хотя это не _удобно_, это цена, которую приходится платить за надежное решение анархии, которое заключается в передаче аргументов на основе командной строки в Windows.

@ mklement0 Спасибо за терпеливый и вдумчивый ответ (как всегда.)

Итак, я полностью согласен с;

Правильным решением для (b) было бы:

  • support -% как специальный только первый аргумент
  • и, как и в случае с ins, требует, чтобы за ней следовала единственная строка, которая составляет сквозную командную строку в целом,> - снова с возможностью включения переменных и выражений PowerShell посредством интерполяции строк (или других способов построение строки)

    • например, msiexec -% "/ i installer.msi INSTALLLOCATION = "$loc " ", где $ loc содержит 'c: foobar none', приведет к дословной командной строке / i installer.msi INSTALLLOCATION =" c: foobar ни один "не проходит, удовлетворяя нестандартное требование msiexec, что взнак равноаргументы толькочасть должна быть заключена в двойные кавычки.

Это то, что я пытался изложить как обобщенное решение как (а), так и (б), за исключением того, что я до сих пор не понимаю, что (а) должно существовать как независимая проблема, которую нужно решить? Я действительно борюсь с тем, что если (b) реализован _специально_ для & , почему это покрытие (a) тоже не может? Например, что ins ... дает, а & cmd --% ... не может? И это не будет критическим изменением, кроме как запретить & передавать --% в качестве аргумента (что кажется смехотворно маловероятным). Бонус в том, что вам не нужно беспокоиться о comspec, shells или что угодно. Пусть решает звонящий.

@ PowerShell / комитет по PowerShell рассмотрел это:

  • Это будет экспериментальная функция для получения реальных отзывов об использовании; мы все еще открыты для действительной сигилы; мы откладываем /bin/sh vs /bin/env sh на PR-обзор
  • Мы продвинемся вперед с --% в качестве нового оператора специально для сценария "stackoverflow", когда пользователь вырезает и вставляет командную строку в PowerShell, и она должна просто работать (конечно, с оператором new, предшествующим ей. )
  • Командлет Invoke-NativeShell отделен от этого предложения и может быть опубликован сообществом в PowerShellGallery; кроме того, требование здесь-строки не решает проблему взаимодействия с пользователем, если не знать заранее, чтобы обернуть ее как здесь-строку
  • Мы не хотим вносить критические изменения в переключатель --% если существующие пользователи могут зависеть от этого поведения.
  • Пользователи, которым требуется потоковая передача, например переключатель --% должны продолжать использовать этот переключатель и при необходимости экранировать специальные символы.
  • Пользователи все еще не могут узнать о --% (или любом решении).
  • В настоящее время мы не вносим изменения в PSReadLine, чтобы изменить вставляемый контент как строку, пытающуюся предсказать намерение пользователя.
  • Мы не рассматриваем несколько решений, которые допускают сценарии здесь-строка и не-здесь-строка (раскрытие); например, & --% vs --%

Я также хочу добавить, что, несмотря на то, что @ SteveL-MSFT и я закрыли # 1995 одновременно с открытием этого, мы действительно не собирались сообщать это как окончательное решение этой проблемы.

Я считаю, что преимущества этой конкретной функции намного более значительны, чем влияние № 1995. Я думаю, что есть МНОГО примеров StackOverflow и примеров документации для инструментов, не связанных с PS, которые люди хотели бы вырезать / вставить (включая меня).

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

  • Мы продвинемся вперед с новым оператором --%

Извините, что затянул это, и я не ищу дополнительных обсуждений, но что за оператор? (Меня в первую очередь интересует синтаксис.)

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Кому достанется трубка? Что происходит с $ ? Аналогично & , && и || .

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

Благодарю.

Извините, что затянул это, и я не ищу дополнительных обсуждений, но что за оператор? (Меня в первую очередь интересует синтаксис.)

С точки зрения AST он, вероятно, технически не будет оператором, а будет собственным типом AST. Если он будет реализован как оператор, это будет унарный оператор.

$a = --% find . -iname *$pdf -print0 | xargs -0 ls -ltr

Кому достанется трубка? Что происходит с $ ? Аналогично & , && и || .

Строка find . -iname *$pdf -print0 | xargs -0 ls -ltr будет отправлена ​​как есть в собственную оболочку. $a все равно будет заполнен, так что вы можете в следующей строке передать это чему-то.

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

Если ~ в первом слоте элемента команды ~ это в начале оператора, вероятно, нет. Это одна из привлекательных сторон нового синтаксиса. Если не ~ в первом слоте ~ в начале, он будет продолжать работать, как сегодня.

(@ daxian-dbw @ SteveL-MSFT, если что-то не так, исправьте ❤️)

@SeeminglyScience совершенно права

Если в первом слоте командного элемента, вероятно, нет. Это одна из привлекательных сторон нового синтаксиса. Если не в первом слоте, он продолжит работать, как сегодня.

Может быть сложно передать выходные данные восходящей команды собственной команде, которая будет вызываться --% . --% основном просто передает строку как ее родной оболочке, например bash , но вывод команды восходящего потока не может быть просто передан bash , но должен быть передан сама собственная команда, например find .

Я думаю, что потоковая передача не является предметом рассмотрения этой функции, поскольку она предназначена в основном для сценария "StackOverflow". Поэтому я думаю, нам лучше просто сделать --% специальным оператором ast, чтобы было ясно, что он не будет работать с конвейерами _PowerShell_.

Ах, я знал, что есть причина, по которой "слот командного элемента" был неправильным. Я имел ввиду в начале заявления. Я исправлю, спасибо!

вывод команды восходящего потока нельзя просто передать на bash

_Upstream_ означает _us_? Думаю сделаю: 'ls' | bash . Не очень умно, но возможно.

Обсуждая это с @jpsnover, он предложил использовать в качестве сигилы shebang #! . В этом случае вам достаточно ввести:

#!/bin/sh ls | grep foo
#!sh | grep foo

(где второй случай предполагает, что sh находится на пути). В этом случае нам нужно будет решить, требуется ли указывать оболочку или мы всегда запускаем ее под оболочкой по умолчанию, поэтому в этом примере sh запускается дважды. Однако с этим возникает проблема из-за того, как файлы shebang работают сегодня. Они работают в PowerShell, потому что shebang игнорируется как комментарий. Если мы решим игнорировать первую строку в сценарии, если это комментарий, тогда у нас все еще будет проблема в интерактивной оболочке, где каждый новый набор строк является новым сценарием, и сегодня нет никакого способа определить, запускается ли этот сценарий как сценарий или интерактивно.

Альтернативой может быть заимствование синтаксиса уценки:

powershell ```bash ls | grep foo ```

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

В этом случае это потенциально может сработать:

powershell $a = ```bash ls | grep foo ``` | select-string bar

@ SteveL-MSFT Markdown Syntax имеет большой смысл. Если мы сможем уточнить. другие типы, такие как python или, возможно, расширение?

Шебанг #! Будет мешать существующие комментарии.

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

И да, нам не нужно запутывать людей строками, которые _ должны_ быть комментариями, а не превращаться в комментарии. #Requires уже выглядит странно, полная чушь собьет с толку в лучшем случае людей с Windows и, вероятно, людей с Linux тоже.

Я лично предпочел бы не смешивать эту функцию с shebang, на мой взгляд, это сбивает с толку.
Кроме того, если мы начнем использовать синтаксис блока кода уценки, то для людей будет просто вопросом времени запросить запуск произвольного языкового кода непосредственно в PowerShell. Я не думаю, что мы хотим идти этим путем ...

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

Я бы буквально никогда не перестал просить встроенный C #, хотя я знаю, что это плохая идея.

Синтаксис уценки не вводит произвольный языковой код, потому что встроенный скрипт будет выполняться в дочернем процессе. Теперь вы можете поместить произвольный языковой код внутри @' и ничего плохого не произойдет.

В групповом обсуждении, а также с Джеффри, мы не должны выступать против людей, желающих запускать другие языки в сценарии PowerShell. Как отметил @ yecril71pl , мы не поддерживаем напрямую другие языки, но полагаемся на другой процесс для интерпретации и выполнения этого кода. Сценарий здесь заключается в том, что пользователь видит блок сценария python или bash и просто хочет использовать его как есть в своем сценарии PowerShell. От людей ничего не требуется. Они могут решить перенести этот другой сценарий в PowerShell, если захотят. Это просто дает пользователям возможность вырезать и вставлять в PowerShell для выполнения работы.

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

Я бы предпочел отказаться от однострочного материала. Острова чужого кода должны быть заметны, иначе никто не поймет, что происходит.

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

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

Абсолютно 100%, но это не значит, что его нужно встраивать непосредственно в язык.

Как отметил @ yecril71pl , мы не поддерживаем напрямую другие языки, но полагаемся на другой процесс для интерпретации и выполнения этого кода. Сценарий здесь заключается в том, что пользователь видит блок сценария python или bash и просто хочет использовать его как есть в своем сценарии PowerShell.

Это уже можно сделать с помощью here-strings, верно? Не могли бы вы подробнее рассказать, какие преимущества это дает по сравнению с существующими методами?

Также зачем останавливаться на python и bash? Почему не C #, CIL, C ++?

От людей ничего не требуется. Они могут решить перенести этот другой сценарий в PowerShell, если захотят. Это просто дает пользователям возможность вырезать и вставлять в PowerShell для выполнения работы.

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


На самом деле, это довольно сильно отличается от исходной темы этой ветки с гораздо более широкими последствиями. Я бы порекомендовал создать для него новый выпуск.

Это уже можно сделать с помощью here-strings, верно? Не могли бы вы подробнее рассказать, какие преимущества это дает по сравнению с существующими методами?

Универсальный редактор кода сможет обнаружить их и соответствующим образом оформить, тогда как с @' ничего не поделаешь, потому что редактор не знает, что внутри.

Также зачем останавливаться на python и bash? Почему не C #, CIL, C ++?

Потому что это не языки сценариев?

Универсальный редактор кода сможет обнаружить их и соответствующим образом оформить, тогда как с @' ничего не поделаешь, потому что редактор не знает, что внутри.

Редактор может сказать, что bash -c @' содержит bash так же легко, как и ограждение кода. Кроме того, сложная часть заключается не в определении того, на каком языке находится часть кода, а в манипуляциях с языковыми серверами и подсветчиками синтаксиса. Единственный способ, которым это будет работать без значительных затрат времени и усилий, - это по существу отрендерить его, как будто это строка здесь.

Потому что это не языки сценариев?

И C #, и C ++ имеют подмножества, ориентированные на сценарии. Даже если бы они этого не сделали, «язык сценариев» субъективен без реального определения. Это бесполезно в качестве окончательной границы для того, что будет и не будет рассматриваться для включения.

И C #, и C ++ имеют подмножества, ориентированные на сценарии. Даже если бы они этого не сделали, «язык сценариев» субъективен без реального определения.

Язык - это (автономный) язык сценариев, когда вы обычно вызываете interpet options… script arguments… и этот вызов не оставляет ничего, кроме того, что он должен был оставить, за исключением временного и частного для интерпретатора. Это также означает, что обычно для запуска требуется копия интерпретатора. Существуют встроенные языки сценариев, которые нельзя запустить таким образом.

@SeeminglyScience

Это уже можно сделать с помощью here-strings, верно? Не могли бы вы подробнее рассказать, какие преимущества это дает по сравнению с существующими методами?

Люди могут использовать здесь-строки сегодня или они могут избежать всех аргументов, передаваемых нативным командам, если они могут понять, как это сделать правильно. Цель здесь - упростить для новых пользователей сценарии вырезания и вставки (Stackoverflow).

Также зачем останавливаться на python и bash? Почему не C #, CIL, C ++?

Поддерживаемые здесь сценарии представляют собой блок текста, переданный собственной команде. Может поддерживаться любой язык, если он поддерживает это соглашение о вызовах. Нет предложения создать временный исходный файл и скомпилировать его.

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

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

На самом деле, это довольно сильно отличается от исходной темы этой ветки с гораздо более широкими последствиями. Я бы порекомендовал создать для него новый выпуск.

Предлагаемая реализация отличается от исходной проблемы, но проблема та же, что и в сценарии вырезания и вставки и «просто работы».

Люди могут использовать здесь-строки сегодня или они могут избежать всех аргументов, передаваемых нативным командам, если они могут понять, как это сделать правильно. Цель здесь - упростить для новых пользователей сценарии вырезания и вставки (Stackoverflow).

Верно, но как это на самом деле проще? Я понимаю, что это субъективно, но мне это не кажется чем-то особенным:

~~~ PowerShell
$ a = `` Баш
находить . -iname * $ pdf -print0 | xargs -0 ls -ltr

~~~

vs

```powershell
$a = bash -c @'
   find . -iname *$pdf -print0 | xargs -0 ls -ltr
'@

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

Поддерживаемые здесь сценарии представляют собой блок текста, переданный собственной команде. Может поддерживаться любой язык, если он поддерживает это соглашение о вызовах. Нет предложения создать временный исходный файл и скомпилировать его.

Что ж, C # не понадобится временный исходный файл. Может быть, и не CIL (не знаю о C ++). В любом случае, хотя ограждение кода, строго являющееся синтаксическим сахаром для вызова исполняемого файла (например, bash / python / cmd), сбивает меня с толку. Ограничения кода подразумевают языковую поддержку imo.

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

Для парсеров на основе tmLanguage это все равно будет кошмар. Они едва справляются с синтаксисом, которого ожидают от atm. Более того, если это так, я не понимаю, почему он отличается от строки здесь.

Предлагаемая реализация отличается от исходной проблемы, но проблема та же, что и в сценарии вырезания и вставки и «просто работы».

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

Что ж, C # не понадобится временный исходный файл. Может быть, и не CIL (не знаю о C ++). В любом случае, хотя ограждение кода, строго являющееся синтаксическим сахаром для вызова исполняемого файла (например, bash / python / cmd), сбивает меня с толку. Ограничения кода подразумевают языковую поддержку imo.

Я просто хочу повторить комментарий @SeeminglyScience по этому поводу. Мы можем запустить сценарий C # без компиляции с библиотекой Microsoft.CodeAnalysis.CSharp.Scripting . Это то, что dotnet-interactive использует в ядре C # Jupyter. Так что для пользователей может быть разумным просить о поддержке сценариев C # или даже сценариев F # с таким синтаксисом.

Можно сказать, что синтаксис ограничения кода в PowerShell предназначен для вызова собственной команды, что теоретически позволяет это:

    ```c:\mypath\MyArbitraryExe
        args to my arbitrary executable
    ```

но тогда это отличается от установленного пользователями понимания этого от markdown, который, как заметил @SeeminglyScience , подразумевает языковую поддержку. На мой взгляд, использование похожего синтаксиса с другой семантикой сбивает с толку.

что теоретически позволяет это

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

Вы помещаете внутрь сценарий, а не аргументы, и сценарий эквивалентен входному потоку

Скрипт является аргументом для bash / cmd / python.

Кроме того, если это предназначено для поддержки SO, полные пути не должны допускаться.

Затем переключите его на этот пример:

~ PowerShell$ a = ipconfig /all ~

Или сначала добавьте произвольный exe в путь.

Вы помещаете внутрь сценарий, а не аргументы, и сценарий эквивалентен входному потоку

Скрипт является аргументом для bash / cmd / python.

Ни один из них не позволяет передавать скрипт в качестве позиционного аргумента. CMD даже не разрешает командный файл.

Кроме того, если это предназначено для поддержки SO, полные пути не должны допускаться.

Затем переключите его на этот пример:

$ a = `` ipconfig
/все

Это не сработает.

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

Кроме того, основная причина, по которой это поддерживается в Markdown, - это подсветка синтаксиса. Это будет постоянная просьба, если мы добавим в язык что-то подобное.

Это не эффективное решение.

Обсуждая это с @jpsnover, он предложил использовать шебанг #! в качестве сигилы.

просто чтобы добавить свои 2 цента, я думаю, что было бы ошибкой возобновлять это обсуждение после того, как было объявлено решение. и лично мне не нравятся синтаксис shebang и markdown (и оба ломают изменения).

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

_Если мы (и PowerShell) знаем, что запускаем собственный исполняемый файл, зачем нам вообще нужен «вызов собственного оператора»? _

Если PowerShell проанализирует входную строку и получит CommandAst, то PowerShell обнаружит команду.
Если команда является собственной командой, PowerShell преобразует ast в NativeCommandProcessor.
Мы можем реализовать новый (экспериментальный NativeCommandProcessor) для случая, который повторно проанализирует экстент Ast (это входная строка), чтобы получить собственное имя / путь исполняемого файла и остальную часть строки в качестве аргумента или аргументов по правилам ОС.

Если пользователь хочет скопировать и вставить из оболочки, он должен ввести "cmd""или" баш"- это будет работать без редактирования.
Если пользователь хочет скопировать и вставить собственную командную строкутоже будет работать как g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Если пользователь хочет скопировать и вставить из оболочки

Для этого у нас есть Get-/Set-Clipboard .

для случая, который повторно проанализирует экстент Ast (то есть входную строку), чтобы получить собственное имя / путь исполняемого файла и остальную часть строки в качестве аргумента или аргументов по правилам ОС.

Я думаю, что при этом вы:

  • Неэффективно проанализировать AST команды, только чтобы позже повторно проанализировать экстент как строку и выбросить исходный AST, ИЛИ
  • Вы берете AST и пытаетесь преобразовать его обратно в строку (как это делает текущий NativeCommandProcessor), но при этом все будет потеряно

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

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

Так:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'

будет токенизироваться как:

--% ps -o pid,args -C bash | awk '/running_script/ { print $1 }'
|-||-----------------------------------------------------------|
 |                                  \
Native call operator        Invocation to be passed to native shell

(Обратите внимание, что мы отправляем даже пробел непосредственно после --% в собственную оболочку, поскольку строка начинается сразу после --% )

Затем этот токен упаковывается в очень простой AST, например:

// We need to carefully work out what AST this inherits from
// This syntax has almost no interoperability with PowerShell by design,
// so can't implement fields like redirections or backgrounding faithfully.
// But in order to participate in pipelines and similar, would need to extend a more concrete class
public class NativeCallInvocationAst : StatementAst
{
    public string NativeInvocation { get; }
}

Затем, после того, как это скомпилировано и отправлено в конвейер, он использует новый собственный командный процессор для отправки строки непосредственно в качестве аргумента собственной оболочке.

Хотя все это кажется реализуемым, у меня возникают следующие вопросы:

  • Настраивается ли собственная оболочка? Если нет, то как это оправдать? Если да, то должно ли оно быть явным как часть синтаксиса (усложняя синтаксис) или реализовано как предпочтительная переменная (что затрудняет обнаружение и управление)?
  • Будет ли такая реализация обнаруживаемой и не запутывает ли людей? Поймут ли пользователи, как мало здесь делает PowerShell, или что происходит, когда они используют | или & или даже строки?
  • Сколько сценариев здесь будет обслуживаться однострочной дословной строкой? Собираемся ли мы реализовать это только для того, чтобы узнать, что многим нужны многострочные вызовы? Достаточно ли велика необходимость избегать чего-то вроде экранирования одной кавычки, чтобы добавить эти ограничения?
  • Это похоже на очень специфический сценарий для совершенно нового синтаксиса языка. Если мы создаем новый дословный строковый синтаксис, почему мы связываем его напрямую с концепцией нативного вызова? Если мы создаем новый механизм вызова, почему мы связываем его напрямую с концепцией других оболочек? Хороший синтаксис и хорошие языковые конструкции составляют, как синтаксически, так и функционально, и стараются не вплетать слишком много в сам язык (вместо этого предоставляя платформу элементов для сборки пользователем в качестве своей программы) - соответствует ли предлагаемый нами синтаксис собственного вызова планке для создания нового элементарного синтаксиса в программах? Есть ли лучший альтернативный подход, который решает эту проблему простым способом, но при этом позволяет нам не связывать строительные блоки, чтобы их можно было составить для решения других проблем?
  • Будет ли это реализовано в других оболочках или других языках или уже есть? На уровне синтаксиса или на любом другом уровне? Если да, то как?

Я думаю, что ты тоже

@rjmholt CommandAst имеет свойство Extent с входной строкой. Повторный синтаксический анализ так же прост, как разделение по первому пробелу в Windows или разделение по пробелам в Unix. Так что я думаю, что мы ничего не потеряли в цепочке и добились хороших результатов.

Как бы это ни было реализовано, я думаю, нам нужен набор сценариев использования для тестирования. Позвольте мне «предложить» следующее:

  • Вариант использования 1: я хочу, чтобы вызов одного литерала exe (без интерполяции) работал, не зная о правилах цитирования / экранирования, например: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 . Текущий обходной путь для этого - выяснить, что цитировать, например: g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11 Более сложный пример может включать командные строки, охватывающие несколько строк, например:
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

Текущий обходной путь (заменить обратную кавычку на \ ), например:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • Пример использования 1a: я хочу, чтобы работал один вызов exe с интерполяцией PS, например: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 . Текущий обходной путь для этого - выяснить, что заключать в кавычки (одинарные или двойные) и чего избегать, например: g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11 Более сложный пример может включать командные строки, охватывающие несколько строк, например:
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

Текущий обходной путь, первый аргумент заключен в двойные кавычки, обратная кавычка заменена на \ и экранируется $ в начале `$ (date + ...).

  • Случай 2: я хочу выполнить буквальную конвейерную команду в другой оболочке, например: ps -o pid,args -C bash | awk '/running_script/ { print $1 }' и wsl ls $foo && echo $PWD . _Я не уверен, должен ли этот вариант использования быть отдельным от предыдущего. _ Текущий обходной путь заключается в цитировании команды bash, например: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' . Этот вариант использования также может включать многострочные строки, например:
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • Вариант использования 2a: я хочу выполнить конвейерную команду в другой оболочке с некоторой интерполяцией PS - это вариант использования?

  • Пример использования 3: мне нужно npm run $scriptName чтобы продолжить работу, т.е. интерполировать переменные . (это может быть неявным и не требует указания).

Возникает вопрос, должны ли быть варианты использования для interpolating строк, т.е. g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 или мы просто набросимся на это и скажем, что текущие правила экранирования охватывают это?

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

Кроме того, мне кажется, что второй вариант использования требует «вызова оболочки». Хотя я не уверен, что первый. Похоже, что exe можно вызвать напрямую.

@rkeithhill Не могли бы вы добавить существующие обходные пути для полноты в своем предыдущем посте?

@oising , я тоже ценю ваш вдумчивый ответ.

Что дает ins ... чего & cmd --% ... не может?

--% в его текущей форме _parameter_ (символ) - поведение которого, я полагаю, мы хотим сохранить - нельзя использовать для cmd /c --% , потому что он не поддерживает _multi-command_ cmd command lines (а также ^ в качестве escape-символа), учитывая, что первый не заключенный в кавычки | будет интерпретироваться как оператор канала _PowerShell_ и что & передается дословно.
То же самое применимо к Unix, где --% обычно почти бесполезен, а с sh -c вообще не работает, потому что sh ожидает, что командная строка будет _single_ аргументом (например, sh -c --% echo hi возвращает пустую строку, потому что только echo выполняется как команда).

ins , с его требованием ограничить сквозную командную строку, передав ее _ как одну строку_, решает эти проблемы и, как указано, также позволяет использовать интерполяцию строк для включения переменных и значений выражений _PowerShell_.

И последнее подводит нас к тому, почему предложенное --% _statement_ - это улица мертвого конца :

  • Нет _ нет пути перехода_ от выполнения уже существующей командной строки _ точно как есть_ к включению в нее переменной _PowerShell_ и значений выражений.

    • Единственный - непонятный и громоздкий - способ добиться этого - определить _дополнительные переменные среды_, которые хранят интересующие значения PowerShell и на которые вы можете ссылаться (соответственно с помощью %...% или $... ) в собственная командная строка.
  • У вас возникает неловкость, связанная с невозможностью включить оператор --% в более крупный конвейер PowerShell.

    • Обратите внимание, что есть законные причины _non_-вырезать-и-вставить для вызова собственной оболочки, где вы можете ожидать этой интеграции; в частности, передача _raw байтовых данных_ через (промежуточный) конвейер и отправка _strings без завершающей новой строки_ требуют использования собственного канала - см. этот ответ SO для реального примера.
  • Существует также неловкость того, что параметр --% _parameter_ практически бесполезен в Unix, что приводит к странной асимметрии: если --% _statement_ работает и в Unix, почему бы не --% _parameter_ ? (например,
    /bin/echo --% 'hi "noon"' дает дословно 'hi noon' (а не ожидаемое hi "noon" ) из-за того, что echo принимает _two_ аргументов, дословно 'hi и noon' с сохранением одинарных кавычек как данных и удалением двойных кавычек).


ins , как предлагаемая альтернатива оператору --% , в идеале будет просто _оберткой_ для удобства_ вокруг cmd /c и sh -c , но на самом деле это _required_:

  • По крайней мере _временно_ в Unix, если # 1995 не исправлено, потому что прямой вызов sh -c '...' настоящее время не передает аргументы со встроенным " должным образом.

  • _Invariably_ в Windows, потому что вам нужно передать командную строку _as-is_ в cmd через параметр lpCommandLine CreateProcess (что параметр --% не может сделать из-за того, что он ограничен командой _one_); в качестве альтернативы попытка передать командную строку в виде _ одной строки_, заключенной в "..." снова не работает надежно из-за # 1995.


Проблем с передачей _аргумента_ можно избежать, предоставив командную строку для выполнения _via stdin_ (который ins может и должен поддерживать), то есть _pipe_ текст командной строки / сценария в исполняемый файл оболочки:

  • В Unix это работает нормально, потому что передача кода через _stdin_ в sh по сути такая же, как передача _script file_ для выполнения (точно так же, как sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Увы, в Windows cmd плохо обрабатывает такой ввод: он печатает свой логотип и повторяет каждую команду перед выполнением вместе со строкой приглашения:
PSonWin> 'date /t & ver' | cmd

Microsoft Windows [Version 10.0.18363.836]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\jdoe>date /t & ver
Sun 07/26/2020

Microsoft Windows [Version 10.0.18363.836]

C:\Users\jdoe>

К сожалению, сама оболочка PowerShell демонстрирует такое же бесполезное поведение: см. № 3223


Так же, как sh и все основные POSIX-подобные оболочки ( dash , bash , ksh , zsh ), большинство языков сценариев _do_ правильно поддержка ввода stdin-as-script, например python , node , ruby и perl .

PS> 'print("hi")' | python

hi

Для включения значений переменных и выражений _PowerShell_ у вас снова есть возможность использовать интерполяцию / конкатенацию строк:

PS> $foo = 'bar'; "print(`"$foo`")" | python

bar

или используя функцию передачи аргументов интерпретатора:

PS> @'
import sys
print(sys.argv[1])
'@ | python - bar

bar

Я надеюсь, что из приведенного выше ясно видно, что в специальном синтаксисе нет _не_ необходимости вообще - ни выражение --% , ни выражение #! , ни блоки кода Markdown - все из которых снова не будут иметь возможность включать значения из вызывающего сценария PowerShell.

Что касается конфигурируемости использования собственной оболочки: я не думаю, что мы должны предлагать ее для предсказуемости; пользователи, которые хотят настроить таргетинг на другую оболочку, могут просто передать командную строку конкретному исполняемому файлу этой оболочки или, как только # 1995 будет исправлен, передать его в качестве аргумента.

  • Спорным вариантом является нацеливание на /bin/bash (с (маловероятным) откатом на /bin/sh ) - хотя можно было бы надеяться, что опубликованные командные строки полагаются только на функции, требуемые POSIX, командные строки с Bash-isms (функции, не относящиеся к POSIX, которые, как можно ожидать, будут поддерживать не все реализации /bin/sh ), вероятно, там будут, учитывая распространенность bash .

@joeyaiello

Я считаю, что преимущества этой конкретной функции намного более значительны, чем влияние № 1995.

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

но я не вижу, чтобы люди массово ударили # 1995 в дикой природе

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

Опять же, я бы сказал, что оболочка, которая выполняет следующие действия, как в настоящее время PowerShell, имеет серьезную проблему :

# On Unix
PS> /bin/echo '{ "foo": "bar" }'

{ foo: bar }

@rjmholt CommandAst имеет свойство Extent с входной строкой. Повторный синтаксический анализ так же прост, как разделение по первому пробелу в Windows или разделение по пробелам в Unix. Так что я думаю, что мы ничего не потеряли в цепочке и добились хороших результатов.

Да, я понимаю здесь ничью, и, возможно, распределения AST в этом сценарии не ужасны (хотя я бы сказал, что язык не должен генерировать ненужные выделения для простого синтаксиса). Но все, что вам нужно сделать, это представить ввод, который bash / cmd видит как строку, а PowerShell не запускает проблемы:

Не анализирует текущую оболочку PowerShell:

--% my_command "\" "

Разбирает, но разделение пробелами дает неверный результат:

--% my_command "\"    "\"

(Мы должны отправить 4 пробела в строке, но PowerShell видит две строки, разделенные в массиве)

@rjmholt , все замечательные вопросы в конце вашего комментария выше , которые мне снова подсказывают, что правильный ответ: нет необходимости в специальном синтаксисе.

Я отвечал на вопрос о том, какой исполняемый файл собственной оболочки использовать в моем предыдущем комментарии , но что касается:

Будет ли это реализовано в других оболочках или других языках или уже есть? На уровне синтаксиса или на любом другом уровне? Если да, то как?

Я не знаю, чтобы какой-либо другой язык делал это на уровне синтаксиса без использования _explicit delimiters_ и разрешения _interpolation_.
Например, perl имеет `...` для запуска команд оболочки, поэтому вы можете запустить следующую команду из сценария в Unix, например:
my $foo='/'; print `ls $foo | cat -n`

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

Как указывалось ранее, мы _ могли_ использовать этот подход для нового _оператора_ (или какой-то разновидности синтаксиса, основанного на разделителях, необязательно с интерполяцией), но я не думаю, что это того стоит, учитывая, что
ins "ls $foo | cat -n" (или вариант со строкой здесь) подойдет, не обременяя язык дополнительной сложностью.

perl предлагает (правильное) решение на уровне синтаксиса, _ потому что оно само не является оболочкой_, но хочет сделать вызовы оболочки настолько удобными, насколько это возможно.

Напротив, мы _являемся_ оболочкой (тоже) и предлагаем вызовы в стиле оболочки _directly_, без какого-либо дополнительного синтаксиса (хотя в настоящее время и с ошибками - см. № 1995).
Предоставление специального синтаксиса для максимального упрощения _вызова_ различных команд_оболочки кажется ненужным.

@iSazonov

_Если мы (и PowerShell) знаем, что запускаем собственный исполняемый файл, зачем нам вообще нужен «вызов собственного оператора»? _

Если PowerShell проанализирует входную строку и получит CommandAst, то PowerShell обнаружит команду.
Если команда является собственной командой, PowerShell преобразует ast в NativeCommandProcessor.
Мы можем реализовать новый (экспериментальный NativeCommandProcessor) для случая, который повторно проанализирует экстент Ast (это входная строка), чтобы получить собственное имя / путь исполняемого файла и остальную часть строки в качестве аргумента или аргументов по правилам ОС.

Если пользователь хочет скопировать и вставить из оболочки, он должен ввести «cmd» или «bash» - это будет работать без редактирования.
Если пользователь хочет скопировать и вставить, собственная командная строка тоже будет работать как g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Но подождите, разве вы никогда не смогли бы использовать какой-либо синтаксис PowerShell с собственными командами, если бы мы все это сделали? Так как sc.exe query $serviceName передаст $serviceName как буквальную строку?

Но подождите, разве вы никогда не смогли бы использовать какой-либо синтаксис PowerShell с собственными командами, если бы мы все это сделали? Так как sc.exe query $serviceName передаст $serviceName как буквальную строку?

Параметр --% не предназначен для решения этой проблемы.

sc.exe query $serviceName обрабатывается &"<cmd>"

Параметр --% не предназначен для удовлетворения этой потребности.

sc.exe query $serviceName обрабатывается &"<cmd>"

Да, конечно, я не думаю, что @iSazonov имеет в виду именно это. Я читаю это так, как будто они предлагают общее изменение того, как привязка параметров работает для собственных команд.

Да, конечно, я не думаю, что @iSazonov имеет в виду именно это. Я читаю это так, как будто они предлагают общее изменение того, как привязка параметров работает для собственных команд.

Господи, надеюсь, что нет.

Господи, надеюсь, что нет.

Эх, иногда, когда вы размышляете над абстрактным, легко забыть об очевидном. Честно говоря, мы все прочитали это и сразу начали думать о реализации, не понимая, насколько это сломается.

Или я упускаю что-то очевидное, и это совсем не то, что предлагается 😁

@essentialexch , для ясности, re:

sc.exe query $serviceName обрабатывается &"<cmd>"

& "<cmd>" работает, только если <cmd> оценивается как простая команда _name или path_, _не включая аргументы_ - аргументы должны передаваться отдельно, индивидуально - и они сами по себе не требуют "...." цитирование расширяемых ссылок на переменные (например,
$exe = 'findstr'; & "where.exe $exe" не работает по дизайну; должно быть & "where.exe" $exe (двойные кавычки здесь необязательны; если они опущены, & тоже необязательны))

Да, конечно, я не думаю, что @iSazonov имеет в виду именно это. Я читаю это так, как будто они предлагают общее изменение того, как привязка параметров работает для собственных команд.

Мое предложение - исправить № 1995 (с критическими изменениями) как можно проще. Конечно, интерполяция не работает. Конечно, это не окончательное предложение - это демонстрация того, как мы можем реализовать исправление за считанные минуты. И это в основном демонстрирует, что нам вообще не нужен собственный оператор вызова, как указывает @ mklement0 .

Разбирает, но разделение пробелами дает неверный результат:

@rjmholt Да, но я думаю, вы
Первым дочерним элементом CommandAst является StringConstantExpressionAst с собственной командой BareWord. Мы можем вырезать простое слово из CommandAst Extent и получить список параметров без изменения ast-s. Но мы могли бы добавить новый ast, чтобы сохранить список параметров.
Я трачу время на объяснение, потому что ниже я хочу подвести итог обсуждения, и я чувствую, что нам нужно будет реализовать некоторые улучшения, которые выглядят сложными, но я чувствую, что они могут быть реализованы просто и __ с легким отказом от обратной совместимости__.


Я считаю, что намерение @SteveL-MSFT первоначального предложения состояло в том, чтобы избежать критического изменения.
Я согласен с @TSlivede и @ mklement0, что это обсуждение показало, что новый синтаксис не нужен, поскольку он не решает всех _fundamental_ проблем, таких как # 1995 и # 12975. С другой стороны, это усложняет язык, в то время как мы хотим, наоборот, _упростить_ работу с собственными приложениями.

__Единственный способ двигаться вперед - это внести критическое изменение или больше .__ (После этого новый оператор или командлет также может быть полезен в некоторых сценариях.)


Мы должны начать с обобщения вариантов использования и ожиданий пользователей.
См. Отличный пост @rkeithhill выше

  1. Пользователи хотят включить / отключить интерполяцию.
    В PowerShell уже есть строки / здесь-строки с одинарными и двойными кавычками.
    Наша цель - понять, как применить / уточнить / улучшить его.
  2. Пользователи хотят запускать любой исполняемый файл или оболочку

    • любой исполняемый файл - команда начинается с имени приложения и следует за аргументами

      • с / без интерполяции

      • на одной строке / на нескольких строках

    • оболочка, вероятно, является особым случаем, потому что аргумент (ы) - это _script_

      • в сценарии копирования-вставки пользователи не хотят интерполяции и хотят одно- и многострочные

      • в сценарии сценария пользователи хотят включить / отключить интерполяцию и хотят, чтобы одна или несколько строк

    Мысли:
    Это заставляет нас думать, что сценарий копирования и вставки должен быть по умолчанию.
    Знаки конца строки зависят от исполняемого файла (в bash, в PowerShell). Это заставляет нас думать, что:
    (1) оболочка должна быть явно указана как любой исполняемый файл,
    (2) возможно, любой исполняемый файл с несколькими строками должен вызываться с помощью оболочки, или мы должны выполнить специальный синтаксический анализ (чтобы удалить терминатор и построить список аргументов, возвращающийся к проблеме с экранированием)
    (3) мы могли бы обратиться к сценарию копирования и вставки в PSReadline. Он мог преобразовать буфер в правильную команду PowerShell.

  3. Пользователям нужны однострочные и многострочные.

    • возможно, нам нужно улучшить здесь-строки для одной строки, чтобы решить некоторые сценарии, как обсуждалось ранее.
    • см. 2 - (2) - всегда используйте оболочку для многострочных или реализуйте специальный синтаксический анализ

Я не хочу писать больше, потому что @TSlivede и @ mklement0 могут обновить свои мысли и выводы, сделанные ранее, но теперь они могут быть основаны на принятии критических изменений. Я бы попросил открыть новую проблему (и закрыть все старые), перечислить все варианты использования (начиная с @ @rkeithhill и добавить еще) и, возможно, создать тесты Pester - это было бы отличным шагом для решения проблемы.

Я хочу только добавить, что мы могли бы рассмотреть новые командлеты для упрощения усвоения пользователями, такие как Invoke-Shell (Invoke-NativeShell), Test-Arguments (например, echoargs.exe, чтобы показать, что собственное приложение получает и показывает помощь для пользователей), Convert-Arguments ( для преобразования пользовательского ввода в аргументы, экранированные справа).

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

Знаки конца строки зависят от исполняемого файла (в bash, в PowerShell).

\ не является символом конца строки в bash .

Конечно, интерполяция не работает. Конечно, это не окончательное предложение - это демонстрация того, как мы можем реализовать исправление за считанные минуты. И это в основном демонстрирует, что нам вообще не нужен собственный оператор вызова, как указывает @ mklement0 .

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

Даже если это гипотетически, я не понимаю, насколько это полезно.

@SeeminglyScience Похоже, мы находимся в разных контекстах. Я говорю, что мы можем реализовать исправление для однострочного нативного вызова как критическое изменение, но без нарушения работы Parser. Другими словами, мы можем даже переключать поведение на лету, независимо от того, какое новое поведение мы реализуем.

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

Я думаю, мне не хватает того, как вы переключите это поведение на лету, не запрашивая его явно (например, с помощью собственного оператора вызова).

@iSazonov

_Если мы (и PowerShell) знаем, что запускаем собственный исполняемый файл, зачем нам вообще нужен «вызов собственного оператора»? _

Если PowerShell проанализирует входную строку и получит CommandAst, то PowerShell обнаружит команду.
Если команда является собственной командой, PowerShell преобразует ast в NativeCommandProcessor.
Мы можем реализовать новый (экспериментальный NativeCommandProcessor) для случая, который повторно проанализирует экстент Ast (это входная строка), чтобы получить собственное имя / путь исполняемого файла и остальную часть строки в качестве аргумента или аргументов по правилам ОС.

Если пользователь хочет скопировать и вставить из оболочки, он должен ввести «cmd» или «bash» - это будет работать без редактирования.
Если пользователь хочет скопировать и вставить, собственная командная строка тоже будет работать как g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 .

Я хочу подтвердить, что вы имеете в виду: вы предлагаете по существу игнорировать весь синтаксис PowerShell при вызове внешних исполняемых файлов?

Конкретно: Вы предлагаете, что, например, работает

/bin/echo (1..5)

в powershell больше не будет выводить put 1 2 3 4 5 а вместо этого должен выводить литерал (1..5) ?

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

Это не упрощает ситуацию (внешние исполняемые файлы теперь ведут себя синтаксически иначе, чем внутренние командлеты), и это также не должно иметь ничего общего с # 1995 IMHO ...

@rjmholt
Из выше :

  • Будет ли это реализовано в других оболочках или других языках или уже есть? На уровне синтаксиса или на любом другом уровне? Если да, то как?

Единственное, что мне приходит в голову, это ipythons ! operator :

Запуск !ps -'fax' || true в выходных данных ipython:

  261 pts/0    Sl     0:00          \_ /usr/bin/python /usr/bin/ipython
  311 pts/0    S      0:00              \_ /bin/bash -c ps -'fax' || true
  312 pts/0    R      0:00                  \_ ps -fax

( ps не показывает кавычки вокруг аргументов , но ps -'fax' || true действительно передается в bash как один единственный аргумент.)

Однако эта функция по-прежнему позволяет интерполировать переменные Python в эту команду ( {pyvar} ).

И похоже на @ mklement0 «s , например , IPython реализует только этот

потому что это не оболочка

Если бы ipython разрешал только синтаксис python, он не был бы очень полезен в качестве оболочки, поэтому они позволяют этому синтаксису перенаправлять команды в bash, чтобы их можно было использовать в качестве оболочки. Powershell на других претензиях ручных быть оболочками (до # 1995 не будет решено, я не буду говорить , что это оболочка), так что было бы довольно странно , чтобы добавить специальный синтаксис для вызова других оболочек.

Если что-то подобное будет реализовано по какой-либо причине (надеюсь, нет), я бы предложил использовать ! вместо --% в качестве «оператора оболочки вызова».

! - это то, о чем люди могут догадаться (потому что они знают ! для этой цели из ipython (или из командного режима vim, или из less или из ftp ) )

--% с другой стороны, труднее набрать, вряд ли можно будет догадаться и, что наиболее важно: текущее использование --% , на мой взгляд, имеет очень мало общего с «передачей данных в другую оболочку» поведение. Текущий --% эмулирует ровно один элемент синтаксиса cmd: подстановку переменных (и то не полностью). Он не поддерживает escape-символ ^ , не разрешает перенаправление к файлам и так далее. Единственная полезная вещь в текущем --% - это возможность дословно передавать содержимое в lpCommandLine но это то, что предложенный «оператор оболочки вызова» не подходит.

Еще одно различие (о котором уже упоминалось) между предлагаемым «оператором оболочки вызова» и текущим --% : один заканчивается трубами, другой - нет, что очень сбивает с толку новых пользователей. ! с другой стороны, перенаправляет | в оболочку во всех приложениях, которые я тестировал до сих пор (ipython, vim, less, ed и т. Д.).

HELP about_Logical_Operators -S

Да, ! само по себе проблематично, потому что это оператор логического отрицания в языке.

Кстати, одним из альтернативных предложений было &! - своего рода оператор «call native / shell».

Мне жаль, что @TSlivede не акцентировал внимание на части «надеюсь, нет», когда он сказал: «Если что-то подобное будет реализовано по какой-либо причине (надеюсь, нет)»

  • Никакой оператор / инструкция _с отдельными аргументами_ не может полностью решить проблему «я хочу-использовать-значения-значения-PowerShell-в-собственной-командной строке», учитывая, что $ используется в качестве символа переменной в _both_ Оболочки PowerShell и POSIX.

  • Никакой оператор / оператор _ без явных разделителей вокруг всей собственной командной строки_ не может решить проблему «где-то-в-родной-командной-строке» (которая может вас волновать, а может и не волновать).

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

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

182 комментария!

Это было отличное обсуждение! Я буду продолжать отслеживать любые новые комментарии по этой проблеме. Мы по-прежнему призываем сообщество реализовать командлет Invoke-NativeCommand type, опубликованный в PSGallery, независимо от этой проблемы.

Я думаю, что это прискорбно, когда у вас было компромиссное, но очень полезное решение на https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -662732627, чтобы отказаться от этого. Я знаю, что вам нужен консенсус, но иногда компромисс - лучшее, что может случиться. Нет, это не идеально, но однострочное решение SO было бы чрезвычайно полезным. По крайней мере, по мнению этого человека.

@essentialexch, чтобы прояснить, у нас даже нет консенсуса в команде PowerShell

Поскольку это отложено, то как насчет реализации помощников

мы могли бы рассмотреть новые командлеты для упрощения усвоения пользователями, такие как Invoke-Shell (Invoke-NativeShell), Test-Arguments (например, echoargs.exe, чтобы показать, что собственное приложение получает и показывает справку для пользователей), Convert-Arguments (для преобразования пользовательского ввода в аргументы, экранированные справа).

Я думаю, что Test-Arguments может быть очень полезным.

Я только что опубликовал модуль Native , ( Install-Module Native -Scope CurrentUser ), который я приглашаю всех вас попробовать и чьи команды я буду использовать ниже ; упоминаемые числовые варианты использования взяты из комментария @rkeithhill выше .

Если мы когда-нибудь захотим рассеять концептуальный туман, я думаю, нам нужно иначе сформулировать варианты использования.
_Ни один из них не требует изменений в парсинге_.

Пример использования [вызов собственной оболочки]:

Вы хотите выполнить _индивидуальную команду_ (вариант использования 1) или всю _многокомандную командную строку_ (вариант использования 2), написанную для собственной оболочки платформы (эта проблема):

  • Поскольку вы используете другой синтаксис оболочки, вы передаете команду (строку) _ как одну строку_ целевой оболочке, чтобы _it_ выполнял синтаксический анализ; Интерполяция строк PowerShell предлагает возможность встраивать значения PowerShell в переданную строку (вариант использования 2a).
  • ins ( Invoke-NativeShell ) охватывает эти варианты использования.
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

# Use case 2: Entire Bash command line (also with line continuation)
@'
ps -o pid,args | awk \
  '/pwsh/ { print $1 }'
'@ | ins

# Use case 2a: Entire Bash command line (also with line continuation) with string interpolation.
# Note the double-quoted here-string and the need to escape the $ that is for Bash as `$
$fields = 'pid,args'
@"
ps -o $fields | awk \
  '/pwsh/ { print `$1 }'
"@ | ins

# Alternative to use case 2a: pass the PowerShell value *as a pass-through argument*,
# which allows passing the script verbatim.
# Bash sees the pass-through arguments as $1, ... (note that the `awk` $1 is unrelated).
@'
ps -o $1 | awk \
  '/pwsh/ { print $1 }'
'@ | ins - 'pid,args'

Синтаксис здесь-строки не самый удобный (отсюда и предложение реализовать встроенный вариант - см. # 13204), но вы не _должны_ его использовать; для дословных команд вы можете использовать '...' , что требует только _doubling_ embedded ' , если он присутствует.

Кроме того, вот разумная замена оператору StackOverflow :

Если вы поместите следующий обработчик клавиш PSReadLine в свой файл $PROFILE , вы сможете использовать Alt-v для формирования вызова ins с дословной строкой в который вставляется текущий текст из буфера обмена.Enter отправляет звонок.

# Scaffolds an ins (Invoke-NativeShell) call with a verbatim here-string
# and pastes the text on the clipboard into the here-string.
Set-PSReadLineKeyHandler 'alt+v' -ScriptBlock {
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n`n'@ | ins ")
  foreach ($i in 1..10) { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardChar() }
  # Comment the following statement out if you don't want to paste from the clipboard.
  [Microsoft.PowerShell.PSConsoleReadLine]::Insert((Get-Clipboard))
}

Пример использования [прямой вызов исполняемого файла]:

Вы хотите вызвать _один внешний исполняемый файл_, используя синтаксис _PowerShell_, с _индивидуальными аргументами_ (варианты использования 1a и 3).

  • Это основная задача любой оболочки, и жизненно важно, чтобы она работала надежно.

    • Вы должны быть уверены в том, что PowerShell сможет передавать любые аргументы, возникающие в результате _its_ синтаксического анализа _as-is_ в целевой исполняемый файл. В настоящее время это не так - см. № 1995 .
  • Это требует от вас понимания синтаксиса _PowerShell_ и метасимволов _its_ режима аргументов.

    • Вы должны знать, что ` используется как escape-символ и для продолжения строки.
    • Вы должны знать, что PowerShell имеет дополнительные метасимволы, которые требуют цитирования / экранирования для дословного использования; это (обратите внимание, что @ проблематично только как первый символ аргумента.):

      • для POSIX-подобных оболочек (например, Bash): @ { } `$ , если вы хотите предотвратить предварительное расширение с помощью PowerShell)
      • для cmd.exe : ( ) @ { } # `
      • Индивидуально ` экранирование таких символов. достаточно (например, printf %s `@list.txt ).

Пока мы ждем исправления # 1995, функция ie (for i nvoke (external) e xecutable) заполняет пробел, просто добавляя к прямому вызову; например, вместо следующего вызова:

# This command is currently broken, because the '{ "name": "foo" }' argument isn't properly passed.
curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

вы бы использовали следующее:

# OK, thanks to `ie`
ie curl.exe -u jdoe  'https://api.github.com/user/repos' -d '{ "name": "foo" }'

В модуле Native есть что-то вроде echoArgs.exe , команды dbea ( Debug-ExecutableArguments ); пример вывода в Windows:

# Note the missing first argument, the missing " chars., and the erroneous argument boundaries.
PS> dbea '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
7 argument(s) received (enclosed in <...> for delineation):

  <a&b>
  <3 of snow Nat>
  <King>
  <Cole c:\temp>
  <1\ a>
  <">
  <b>

Command line (helper executable omitted):

  a&b 3" of snow "Nat "King" Cole" "c:\temp 1\\" "a \" b"

Используя переключатель -UseIe ( -ie ), вы можете указать dbea передать аргументы через ie , что демонстрирует, что он устраняет проблемы:

# OK, thanks to -UseIe
PS> dbea -UseIe '' 'a&b' '3" of snow' 'Nat "King" Cole' 'c:\temp 1\' 'a \" b'
6 argument(s) received (enclosed in <...> for delineation):

  <>
  <a&b>
  <3" of snow>
  <Nat "King" Cole>
  <c:\temp 1\>
  <a \" b>

Command line (helper executable omitted):

  "" a&b "3\" of snow" "Nat \"King\" Cole" "c:\temp 1\\" "a \\\" b"

Примечание. После исправления # 1995 ie больше не требуется; в интересах прямой совместимости ie предназначен для обнаружения и автоматического откладывания исправления; то есть, как только исправление будет внесено, ie перестанет применять свои обходные пути и будет эффективно действовать как прямой вызов / & .

Пример использования [прямой вызов вредоносного исполняемого файла Windows]:

Есть две основные проблемы, которые возникают только в _Windows_:

  • Вы вызываете «мошеннический» исполняемый файл, требования к цитированию которого отличаются от наиболее широко используемых соглашений ; например, msiexec.exe и msdeploy.exe требуют, чтобы prop="<value with spaces>" цитировалось именно таким образом - цитирование только вокруг _value_ части - даже если "prop=<value with spaces>" - цитирование всего аргумент - _должен_ быть эквивалентным (последнее - то, что PowerShell вполне оправданно делает за кулисами).

  • Вы вызываете _ пакетный файл_ с _ аргументом, который не содержит пробелов, но содержит метасимволы cmd.exe например & , ^ или | ; например:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell - оправданно - передает http://example.org/a&b _unquoted_ (поскольку нет встроенных пробелов), но это прерывает вызов пакетного файла, потому что cmd.exe - необоснованно - передает аргументы, переданные пакетному файлу, в такое же правило синтаксического анализа, которое будет применяться внутри cmd.exe вместо того, чтобы принимать их как литералы.

    • Примечание. Хотя использование пакетных файлов для _directly_ реализации функций, вероятно, сокращается, использование их в качестве _CLI точек входа_ по-прежнему очень распространено, например, Azure az CLI, который реализован как пакетный файл az.cmd .

Примечание. ie автоматически обрабатывает эти сценарии для командных файлов и для msiexec.exe и msdeploy.exe , поэтому для вызовов _most_ не требуется дополнительных усилий ; однако невозможно предвидеть _все_ "мошеннические" исполняемые файлы.

Есть два способа решить эту проблему:

  • Учитывая, что cmd.exe , после применения своей собственной интерпретации к аргументам в командной строке _должно_ сохранить указанное цитирование, вы можете делегировать вызов ins в Windows:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • Если вы используете строку _expandable_, это снова позволяет вам встраивать значения PowerShell в командную строку.

      • $url = 'http://example.org/a&b'; ins ".\someBatchFile.cmd `"$url`""

  • В качестве альтернативы используйте текущую реализацию --% , но помните о ее ограничениях:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • Учитывая, как реализован --% , единственный способ встраивать значения PowerShell - это - неудобно - определить _aux. переменная среды_ и укажите на нее синтаксис %...% :

      • $env:url = 'http://example.org/a&b'; .\someBatchFile.cmd --% "%url%"


Обработка ошибок

Ожидающий рассмотрения https://github.com/PowerShell/PowerShell-RFC/pull/88 обеспечит лучшую интеграцию обработки ошибок с внешними (собственными) исполняемыми файлами.

Между тем, модуль Native поставляется с двумя функциями для специального согласия на обработку ненулевого кода выхода как ошибки завершения сценария :

  • ins поддерживает переключатель -e / -ErrorOnFailure , который выдает ошибку, если $LASTEXITCODE равно нулю после вызова собственной оболочки.

  • iee - это функция-оболочка для ie которая аналогично выдает ошибку, если $LASTEXITCODE равно нулю после вызова внешнего исполняемого файла.

Команды, поставляемые с модулем, имеют множество тонкостей.
Мы надеемся, что довольно обширная помощь на основе комментариев достаточно их охватывает.

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

Большой! То, что я опубликовал ранее, было просто для того, чтобы люди задумались о создании большего количества лучших вариантов использования.

Я должен прояснить несколько важных аспектов ins ( Invoke-NativeShell ), которые отличаются от того, что я предлагал ранее; цель заключалась в том, чтобы учесть повсеместное распространение bash и обеспечить согласованный интерфейс командной строки для разных платформ.

  • В Unix я решил называть /bin/bash а не /bin/sh , учитывая повсеместность Bash, но вы можете выбрать использование /bin/sh с -UseSh ( -sh ) ( /bin/sh также служит запасным вариантом в том маловероятном случае, когда /bin/bash отсутствует).

    • В интересах единообразия работы с формами вызова и платформами я скрыл возможность установки $0 , имени вызова; то есть любые передаваемые аргументы начинаются с $1 , что согласуется с тем, как аргументы передаются при передаче скрипта из конвейера, и, вероятно, с ожиданиями пользователей:
# Script as argument
PSonUnix> ins 'echo $# arguments; echo "[$1] [$2]"' one two
2 arguments
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonUnix> 'echo $# arguments; echo "[$1] [$2]"' | ins - one two
2 arguments
[one] [two]

# As an aside: script as argument, with pipeline input *as data*:
PSonUnix> 'one', 'two' | ins 'cat -n'
     1  one
     2  two
  • В Windows мне пришлось использовать временный _batch-файл_ за кулисами по техническим причинам, но я думаю, что использование синтаксиса пакетного файла в любом случае является лучшим выбором ( %%i а не %i в for переменные цикла, возможность экранировать % как %% ).

    • Использование пакетного файла также включает поддержку аргументов передачи, как в Unix:
# Script as argument
PSonWin> ins 'echo [%1] [%2]' one two
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonWin> 'echo [%1] [%2]' | ins - one two
[one] [two]
Была ли эта страница полезной?
0 / 5 - 0 рейтинги