Powershell: Не требовать {...} вокруг имен переменных для доступа к членам с нулевым условием

Созданный на 17 дек. 2019  ·  53Комментарии  ·  Источник: PowerShell/PowerShell

Продолжение # 3240.

Был реализован нулевой условный доступ к члену ( ?. ), который будет доступен в качестве _экспериментальной_ функции в 7.0.

Однако в интересах обратной совместимости реализация в настоящее время требует, чтобы имя переменной было заключено в {...} , потому что ? технически является допустимым символом в имени переменной, использование которого на удивление _не_ требует {...} .

То есть, если вы хотите игнорировать попытку вызова $obj.Method() если $obj равно $null (или undefined), вы ожидаете следующего, аналогичного C #:

# Does NOT work as intended.
# 'obj?' as a whole is interpreted as the variable name.
$obj?.Method()

Вместо этого в настоящее время вы должны использовать:

# !! Must enclose 'obj' in {...} to signal that '?' is a syntactic element as part of '?.'
${obj}?.Method()

_Update_: Объединение нулей - $var??='default и $a??$b - и тернарный оператор - $var?1:0 - затронуты аналогичным образом (хотя и используется пробел перед ? решает вопрос).

Это требование является (а) неожиданным и (б) громоздким:

  • Новые пользователи не будут ожидать необходимости в {...} и не обязательно будут знать, что _it_ это то, что требуется, когда следующее не работает (возможно, _undetected_, если не действует Set-StrictMode -Version 2 или выше): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • Даже если пользователи _do_ узнают о необходимости {...} :

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

Использование {...} не обязательно, и, хотя оно не требуется, означает _поворотное_ изменение, оно, возможно, попадает в корзину 3: Маловероятная серая зона .


Почему это изменение следует считать приемлемым:

Примечание: @rjmholt обсуждает, почему изменение синтаксиса PowerShell очень проблематично _ в целом_ в этом комментарии , но по причинам, представленным ниже, я думаю, что здесь стоит сделать исключение.

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

Да, интерпретация $var?.foo в старых скриптах, которые использовали $var? в качестве имени переменной, не работает, но:

  • ? никогда не должно было быть разрешено как часть идентификатора _ без включения его в {...} _.

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

    • Даже Ruby разрешает только ?! ) в _end_ идентификаторов, и только идентификаторы _method_, и я подозреваю, что большинство пользователей Ruby знают, что они не должны _не_ предполагать, что другие языки поддерживают тоже самое.

Итак, прагматично говоря:

  • Подавляющее большинство существующего кода не будет затронуто - токен, такой как $var?.foo , просто не встретится.

  • Если вы напишете $var?.foo с новой семантикой, тогда да, запуск этого в более старых версиях может привести к другому поведению (а не к очевидному нарушению), в зависимости от того, какой строгий режим действует - но _вы всегда должны в любом случае обеспечить минимальную версию, необходимую для запуска вашего кода по назначению_ ( #requires -Version , ключи манифеста модуля).

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

Committee-Reviewed Issue-Enhancement WG-Language

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

@rjmholt , хотя я думаю, что мы все понимаем, насколько сложно подобное изменение _ в принципе_, в данном конкретном случае это не имеет значения _ на практике_, и я не думаю, что правило PSSA вообще необходимо:

  • Анализ @ThomasNieto показал, что - если мы полагаем, что корпус является репрезентативным для всего кода PowerShell - что _виртуально никто не использует переменные с именами, заканчивающимися на ? _.

  • Если бы я догадывался, большинство людей даже не подумали бы об использовании таких имен переменных, потому что они не ожидали бы, что они будут работать (включая меня), и, вероятно, даже не заметят того исключения, что автоматический $? variable составляет (имя которой также является исключением в POSIX-совместимых оболочках, таких как bash ).

    • Фактически, если присмотреться к анализу @ThomasNieto, то среди перечисленных файлов 8 :

      • 5 содержат только _ ложные срабатывания_, а именно переменные, которые используются внутри _строки_, такие как "Are you sure you want to kill $vParam?" - на самом деле, это использование _сломано_ и показывает, что авторы сценария _не_ ожидали, что ? будет часть имени переменной.

      • 2 этих файлов больше не являются общедоступными.

      • Только _1_ из них является добросовестным использованием ? -кончающих переменных - в качестве _Boolean_ переменных (например, $key1? ), которые, помимо прочего, _не_ объединены с оператором доступа к члену. , . / cc @stevenayers.

Исходя из вышесказанного, мне кажется, что это учебное ведро 3: Маловероятное изменение

_Документирования_ изменения поведения (с обоснованием) должно быть достаточно.

Конечно:

  1. это основано на (а) правильности анализа и (б) репрезентативности общедоступного корпуса.

  2. это резко расходится с утверждением комитета о том, что «довольно часто использовались имена переменных, оканчивающиеся знаком вопроса» (что, конечно, было бы проблемой только в том случае, если такие имена переменных не заключены в {...} ).

Исходя из предположения, что 1. верно (мы не слышали ничего, что поддерживало бы 2.), у нас есть два варианта:

  • Сорвите пластырь и просто запретите ? в именах переменных в дальнейшем, если только они не заключены в {...} (за очевидным исключением $? ) - это сломает исчезающе маленький доля скриптов, которые в настоящее время полагаются на это.

    • То есть что-то вроде $key1? , за которым не следует ни . ни другое ? ( $key1??'else' ), ни тернарное выражение ( $key1?1:0 ) вызовет _parser error_.

      • Как показывают примеры объединения нулей и тернарных операторов, они тоже выиграют от этого изменения - в настоящее время вам нужен либо _space_ - $key1 ?1:0 / $key1 ??'else' - или {...} - ${key1}?1:0 / ${key1}??'else'

    • Внутри _expandable strings_ ? больше не будет считаться частью (не {...} -enclosed) имени переменной, поэтому мы фактически _fix_ существующие скрипты, которые по ошибке использовали строки, такие как "Are you sure you want to kill $vParam?" . 😁
  • Если мы действительно чувствуем, что нам нужно избегать нарушения исчезающе малой части существующих скриптов, мы _ могли бы_ рассмотреть логику, предложенную @ ExE-Boss , но я не думаю, что мы хотим вводить эту концептуальную сложность - не говоря уже о сложности реализации (которую я не могу говорить с).

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

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

@ mklement0

? никогда не должно было быть разрешено как часть идентификатора без заключения его в {...}.

Уммм - исходным базовым языком для PowerShell была Posix Shell, которая использует $? для кода выхода qed '?' допускается в именах переменных без кавычек.

он не может (значимо) работать на более старых версиях

Конечно, может:

PS>  $abc?=@{a=1; b=2; c=3}
PS>  $abc?.b                                                                                        1
PS>  2                      

Конечно, может:

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

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

Первоначальным базовым языком для PowerShell была Posix Shell, которая использует $? для кода выхода.

$? - это _exception_ в POSIX-подобных оболочках; вы не можете _не_ делать следующее:

# bash, ksh, zsh, dash (all common /bin/sh implementations)
foo?='bar' # BOOM! E.g. "bash: foo?=bar: command not found"

(И, очевидно, никто не просит упразднить $? в PowerShell.)

И, конечно же, следует отметить, что _все другие_ автоматические переменные в том же пространстве, что и $? (т.е. $^ и $$ ), явно являются специальными: заключен в токенизатор. И, собственно говоря, так и $? , хотя (в настоящее время) это не обязательно.

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

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

Также @ vexx32 , причина того, что $? имеет специальный регистр в токенизаторе, заключается в том, что вы не можете создать переменную с именем, которое начинается с ? . например, $??? = 'foo' не будет анализировать. Имена переменных должны начинаться с буквенно-цифровых символов или символа подчеркивания и в настоящее время могут содержать буквенно-цифровые символы, символы подчеркивания или вопросительные знаки. Я согласен с тем, что ?. следует анализировать как оператор доступа к члену с нулевым условием, но я хотел прояснить, почему ? имеет специальный регистр в токенизаторе.

Спасибо, что привлекли мое внимание к этому чату.

Как бы то ни было, у меня есть несколько мыслей:

  1. Хотя эти переменные встречаются редко, никто не хочет играть в крота, когда обратная совместимость нарушает их скрипты.
  2. Если не считать глубоких фанатиков, я бы ожидал, что $ {obj} ?. Method () или $ obj? .Method () получит небольшой всплеск. В сети есть рекомендации, посвященные тому, как управлять нулевыми значениями уже на протяжении десятилетия, и я не вижу, чтобы так много людей переходили с if ($ obj) {$ obj.Method ()} _, чтобы просто встать на подножку v7_
  3. Кроме того, большинство людей, у которых возникают проблемы с неожиданными именами переменных, изучают синтаксис $ {} (вы уже естественным образом отметили его знаком подчеркивания). Я ожидаю, что совпадение на диаграмме Венна "людей, которые хотят использовать эту функцию" с "людьми, которые уже знают синтаксис $ {}", будет около 100%.
  4. Поскольку указанный синтаксис не такой мощный, как полное подвыражение, а полные подвыражения работают как в ядре, так и нет, я ожидаю, что большинство людей продолжат использовать его в будущем.

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

Учитывая такую ​​потенциальную выгоду от подбрасывания монеты, я перефразирую Роберта Макнамара: «Если нас проклинают, если мы делаем, и проклинаем, если нет, я выберу чертовски, если мы этого не сделаем».

Я предлагаю сделать это так, чтобы $obj?.Method() по умолчанию работал как $<var i="6">obj</var> ?. <var i="9">Method</var>() и возвращался только к $<var i="11">obj?</var> . <var i="14">Method</var>() когда $obj не существует в области видимости, но $obj? существует, а #requires ‑Version не является v7 +.

@ ExE-Boss, похоже, есть много исключений из этого правила.

Я повторяю свой вопрос о ценности этого конкретного правила.

После этого возникает вопрос: «Что еще мы не знаем о синтаксисе переменных, что мы могли бы нарушить?»

Если консенсус заключается в том, чтобы принудительно использовать синтаксис ${...} для этого, здесь будет приветствоваться поддержка OptionalFeatures, так что люди, подобные мне, которые будут использовать этот синтаксис (я буду использовать его _a много_), могут отказаться от шума .

Поскольку мы здесь перефразируем:
«Логика ясно диктует, что потребности многих перевешивают потребности немногих». - Спок

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

@StartAutomating : Есть несколько причин, и они конкретны, а не расплывчаты. Кроме того, одна из целей этого - простота кода. Необходимость заключать имена переменных в {} работает прямо против этой цели.

Я с @ mklement0 и @KirkMunro по этому ${...}? неестественным и ненужным сложным. У нас уже были критические изменения, и в этом случае это небольшая цена в обмен на ясность.

@StartAutomating

  1. Кроме того, большинство людей, у которых возникают проблемы с неожиданными именами переменных, изучают синтаксис $ {} ( вы уже естественным образом отметили

Это не выдерживает. Подчеркивания - допустимые символы в именах переменных.

@ vexx32

Мне кажется очевидным, что во время написания кода это ожидалось.

Неа.

Поменять местами символы? $foo.?Property выглядит неконфликтным, потому что имена свойств, начинающиеся с вопросительных знаков, уже нуждаются в цитировании:

PS C:\> $foo = [pscustomobject]@{'?'=1; '?name'=2}
PS C:\> $foo.?name
At line:1 char:6
+ $foo.?name
+      ~
Missing property name after reference operator.
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : MissingPropertyName
PS C:\> $foo.'?name'
2

@HumanEquivalentUnit , к сожалению, вызовет вечную путаницу с давно устоявшимся синтаксисом ?. используемым в C # и некоторых других языках (кроме того, вам нужно будет найти альтернативную форму для индексатора ( ${arr}?[0] ), а что-то вроде $arr[0]? вызовет путаницу с тернарным оператором)

Нет подходящего решения.

  1. Неизвестное количество людей пользуется? в конце имен переменных. Хотя это дополнительная функция, можно сказать: «Не включайте ее, если вы запускаете эти сценарии», но человек, включающий ее, должен проверить текущие и будущие сценарии, которые они получают из любого источника.
  2. Альтернативный синтаксис - не лучший вариант, потому что он импортирует что-то не очень мощное из C #.
  3. Изменение поведения на основе #requires не работает вне сохраненного сценария и завершится ошибкой, как только кто-то скопирует код из длинного сценария без установки тега.
    4. (dir *.ps2)?.count и (dir *.ps1)?.count показывают, что это операнд не обязательно должен быть переменной.
    ($Host)?.toString() / ($null)?.toString() устраняет необходимость заключать имя в фигурные скобки. Это все еще лишние нажатия клавиш, но это более логично и менее уродливо.
  1. Изменение поведения на основе #requires не работает вне сохраненного сценария и завершится ошибкой, как только кто-то скопирует код из длинного сценария без установки тега.

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

(Надеюсь, в более старом коде действует как минимум Set-StrictMode -Version 1 , и в этом случае $var?.foo выйдет из строя из-за отсутствия переменной с именем var? ).

  1. избавляет от необходимости заключать имя в фигурные скобки.

Замена (...) на {...} - это лишь незначительное улучшение.

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

  1. Изменение поведения на основе #requires не работает вне сохраненного сценария и завершится ошибкой, как только кто-то скопирует код из длинного сценария без установки тега.

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

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

(Надеюсь, в более старом коде действует как минимум Set-StrictMode -Version 1 , и в этом случае $var?.foo выйдет из строя из-за отсутствия переменной с именем var? ).

По моему опыту, не так часто используется Set-Strictmode. Потому что (а) предполагается, что значение по умолчанию без этого «правильное» и (б) частота использования вещей, которые полагаются на его отключение, слишком высока. Это обычное использование этих операторов. В любом случае проблема в более старом коде, где $ var? существует и имеет свойство foo, но теперь код ищет $ var ... Возможно, новая обработка вызовет ошибку, ЕСЛИ установлен строгий режим, потому что $ var не существует ....

  1. избавляет от необходимости заключать имя в фигурные скобки.

Замена (...) на {...} - это лишь незначительное улучшение.

да. ($ x) немного лучше, чем $ {x}, но ненамного. ( Get-user "Bob")?.disable() лучше, чем

$u = get-user "bob" 
($u)?.disable

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

да.
$var. foo С пробелом работает. ${foo} ?.bar() не потому что? разрешено в имени функции или псевдонима (?. разрешено для обоих). Встраивание фрагментов синтаксиса C # в существующие правила синтаксиса без нарушения правил не всегда практично. Иногда «Это доступно в C # ...» действительно заслуживает ответа «Да. Вы знаете, где находится C #, если хотите».
:-)

Для обеспечения совместимости с SemVer это должно быть сделано в основной версии (предпочтительно 7.0.0 ).

Также см. Мое предложение выше ( https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ).

@ ExE-Boss Да, это был ваш комментарий, о котором я имел в виду, когда сказал, что в зависимости от #requires поначалу это может быть не самой хорошей идеей.
И да, если кто-то хочет сломать какие-либо существующие скрипты, самое время это сделать, когда изменится основная версия, поэтому это нужно сделать быстро или дождаться V8.

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

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

Я часто обнаруживал, что многие решения, связанные с синтаксисом PowerShell, не интуитивно понятны людям, которые в первую очередь не являются разработчиками PowerShell. В последний раз, когда я использовал PowerShell для чего-то серьезного, я был потрясен отсутствием оператора объединения null или даже тернарного оператора, который я мог бы использовать вместо него, что привело к тому, что я опубликовал этот популярный ответ PowerShell на Stack Overflow . Это было в 2013 году, и это одна из трех главных причин, по которым я с тех пор избегаю PowerShell. Вся суть языка сценариев в том, что мне нужно что-то быстрое и грязное с большим количеством синтаксического сахара, а не подробные инструкции if-else.

@StartAutomating сказал:

Если не считать глубоких фанатиков, я бы ожидал, что $ {obj} ?. Method () или $ obj? .Method () получит небольшой всплеск. В сети есть десятилетнее руководство по управлению нулевыми значениями, и я не вижу, чтобы так много людей переключались с if ($ obj) {$ obj.Method ()} только для того, чтобы принять участие в подножке v7.

Я просто n = 1, но как разработчик, не использующий PowerShell, я категорически не согласен. Новый ? Синтаксис - это то, о чем я узнаю довольно быстро, даже когда кто-то прикоснется к PowerShell, может быть, раз в несколько месяцев, и это определенно повысит вероятность того, что я буду использовать PowerShell в будущем вместо альтернатив. Если я затем увижу, что для обратной совместимости требуется какой-то сумасшедший синтаксис {}, я закатываю глаза и возвращаюсь к тому, что уже использовал.

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

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

Я предполагал, что $ {} не будет _таким странным для разработчиков PowerShell_, потому что им уже пришлось бы знать эту синтаксическую форму, если бы они когда-либо встраивали переменную с? или и подчеркивание в строке.

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

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

Третья часть моей обратной связи, кажется, преувеличена. Выступая от имени одного из людей, которые иногда использовали переменные с именами типа $ IsThisRight? = Test-Something, моей первой реакцией на потенциальные новости об этом ограничении переменных было «ну, думаю, мне придется переименовать некоторые переменные, я все равно не получил за это так много».

Итак, чтобы попытаться повторить и завершить поток:

  1. ИМХО, синтаксис $ {}, вероятно, не будет таким уж странным для _существующих_ разработчиков PowerShell.
  2. Вы можете думать, что это классная функция; для меня это лекарство для знакомства с языком, и я настоятельно рекомендую всем, кто читает эту ветку, узнать, как качать назначение ifs / foreaches / whiles и т. д.
  3. Как один из затронутых авторов, я не получаю чего-то столь важного от? в конце моих переменных, что я был бы против изменения моего кода.

Или, если быть кратким:

Сделайте это изменение, если хотите. У меня маленькая лошадь в этой гонке. Если вы все же решите сделать это, сообщите об изменении в явной форме, чтобы любой, кто назвал переменные с помощью? можно обновить соответственно.

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

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

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

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

Лучшая обработка null - это не единственное, что мне нужно, чтобы больше начать использовать PowerShell: это второе. Первым была кроссплатформенная поддержка, и я очень впечатлен тем, как далеко она зашла. Лично для меня синтаксический сахар имеет большое значение, когда я выбираю язык для выполнения данной задачи: я хочу, чтобы синтаксис упростил то, что я делаю. Если я пишу что-то, что нужно поддерживать множеству людей, я, вероятно, захочу, чтобы это было более подробным; но если я пишу быстрый сценарий для административных задач или задач DevOps, мне просто нужен быстрый способ проверить такие вещи, как null.

Третья часть моей обратной связи, кажется, преувеличена. Выступая от имени одного из людей, которые иногда использовали переменные с именами типа $ IsThisRight? = Test-Something, моей первой реакцией на потенциальные новости об этом ограничении переменных было «ну, думаю, мне придется переименовать некоторые переменные, я все равно не получил за это так много».

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

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

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

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

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

Тогда вы пришли на нужный язык! PowerShell - это определенно сахар с синтаксическим сахаром.

Чтобы обучить тех, кто не знает, вы можете использовать "null coalese" в PowerShell несколькими способами:
~$ ListWhereNotNull = $ null, 1, 2, $ null, 3 -ne $ null # Это проверит, что каждый элемент не является нулем, и вернет список 1,2,3$ FirstNonNull = @ ($ null, 1, $ null, 2 -ne $ null) [0] # Это вернет первое ненулевое значение$ lastNonNull = @ ($ null, 1, 2, $ null -ne $ null) [- 1] # Это вернет последнее ненулевое значение с использованием отрицательного индекса$ TheObjectPipelineWay = $ null, 1, $ null, 2 | Select-Object -First 1$ TheAssigningForeachWay = foreach ($ item in $ null, 1, 2, $ null, 3, 4) {если ($ item -ne $ null) {$ item;



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

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

В этом вы ошибаетесь. Core на самом деле имеет очень мало изменений, нарушающих синтаксис, и больше модулей, чем вы думаете, отлично работают с ядром (+ - некоторые проблемы с Linux и Windows, путями / новой строкой). Говоря более практическим языком, ваш модуль с меньшей вероятностью будет работать в Core, если у вас есть какая-то зависимость API от платформы Windows (например, я не собираюсь задерживать дыхание, чтобы мои оболочки WPF работали в Linux).

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

Re: Синтаксический сахар, мой саркастический ум цитирует фильм "Snatch": "Я уже достаточно сладкий"
Re: нарушение потенциала изменений в Core: Сейчас у нас их мало, и давайте сохраним их так, как только сможем.

@StartAutomating : Ты говоришь, я не умею стрелять? потому что, я думаю, еще есть место для большего количества сахара

Чтобы обучить тех, кто не знает, вы можете использовать "null coalese" в PowerShell несколькими способами:

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

В этом вы ошибаетесь. Core на самом деле имеет очень мало изменений, нарушающих синтаксис, и больше модулей, чем вы думаете, отлично работают с ядром (+ - некоторые проблемы с Linux и Windows, путями / новой строкой). Говоря более практическим языком, ваш модуль с меньшей вероятностью будет работать в Core, если у вас есть какая-то зависимость API от платформы Windows (например, я не собираюсь задерживать дыхание, чтобы мои оболочки WPF работали в Linux).

Я просто говорю из своего опыта в этом отношении, а не собираю надлежащую статистику. Например, в pre-Core я мог бы сгенерировать пароль с помощью [System.Web.Security.Membership]::GeneratePassword(32, 6) , но System.Web недоступен в Core. (Я знаю, что, вероятно, смогу загрузить старую System.Web.dll, но я бы предпочел не делать этого, если мне это не нужно.) На самом деле изменения синтаксиса для меня легче решить, чем отсутствие API-интерфейсов. Создание набора секретов Docker для devkit кажется отличным местом для использования быстрого скрипта, но на удивление более подробным в PowerShell Core, чем в bash. Конечно, решения bash часто выглядят довольно некрасиво.

Re: Синтаксический сахар, мой саркастический ум цитирует фильм "Snatch": "Я уже достаточно сладкий"

Это зависит. Иногда мне кажется, что PowerShell необычайно приятен. Работа с null - одна из тех ключевых областей, в которых я обычно не чувствую этого, хотя я бы сказал то же самое для многих языков. Я также считаю традиционный встроенный оператор if-else в PowerShell громоздким по сравнению с тернарным оператором или цепочкой && и || bash. Поскольку это одна из ключевых проблем, которая заставила меня несколько лет назад отойти от PowerShell, я подумал, что об этом стоит упомянуть.

С другой стороны, я ежедневно сталкиваюсь с самыми разными языками, и что бы я ни писал, я всегда думаю: «На языке X было бы намного проще!» - а потом, когда Я использую язык X, я думаю, "это было бы намного проще на языке Y!" Но послушайте, если у меня будет возможность сказать это вслух и, возможно, изменить направление языка, я воспользуюсь ею.

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

@StartAutomating : Ты говоришь, я не умею стрелять? потому что, я думаю, еще есть место для большего количества сахара

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

@Zenexer : Мне очень нравится Powershell , я люблю. Мне повезло, что я активно и ежедневно занимаюсь PS на работе. Мне нравится гибкость, особенно симбиоз с .NET, о боже! Вот почему меня волнует его направление, и поэтому мне так трудно принять синтаксис ${...}? .

Хорошие моменты, но позвольте мне кое-что прояснить.

Хорошо это или плохо, но PowerShell на сегодняшний день не использует семантическое управление версиями.
Новая основная версия принесла только _новые_ функции с твердой приверженностью обратной совместимости.
Критические изменения начали закрадываться в Core, отчасти из-за необходимости перехода на кроссплатформенность.
Основная версия v7.0 была выбрана, чтобы сигнализировать об _увеличении_ обратной совместимости: попытке предоставить жизнеспособную замену Windows PowerShell.

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

Как утверждается в OP, данный случай кажется мне изменением ведра 3; хотя мы знаем, что _некоторые_ скрипты сломаются ( @StartAutomating знает, как исправить свои скрипты), данные говорят о том, что такие случаи довольно редки.

Почему редко?

Поскольку использование ? в именах переменных вообще не приходит в голову большинству людей, и если это так, они не должны ожидать, что это будет работать _без {...} _: Если все следующее работает только с {...} , почему можно ожидать, что он будет другим для ? ?: . , ; ( : вообще не может использоваться, потому что он всегда интерпретируется как разделитель привода)

Разрешение имен, таких как $var? или $va?r без требования {...} было вседозволенностью, которая требовала проблем, и проблема пришла: она мешает эволюции язык, как в данном случае.

Есть 3 новые связанные функции (по крайней мере, связанные в синтаксической форме), вдохновленные C #:

  • Тернарные условные выражения - будут добросовестной функцией в 7.0

    • (Get-Date).Second % 2 ? 'odd' : 'even'

  • Оператор Null-coalescing - будет добросовестной функцией в 7.0

    • $var ?? 'default' и $var ??= 'default'

  • Нулевой условный оператор - проблема под рукой - будет _экспериментальной_ функцией в 7.0.

    • $possiblyNull?.ToString()

Все они имеют ? как часть их синтаксиса, и все они подвержены бремени устаревшего парсинга $var? , хотя ?. наиболее очевидно так:

  • Хотя вы можете утверждать, что что-то вроде $var?1:0 представляет интерес только для программистов, играющих в гольф, оно будет работать нормально, если ? имеет синтаксическую функцию - в настоящее время вы должны использовать _space_ перед ?

  • Напротив, мне кажется разумным, что кто-то попытается $var??='bar' (возможно, меньше $baz = $foo??$bar ), что в настоящее время также требует пробела.

То есть, если мы придерживаемся текущего анализа $var? , мы загромождаем _три_ новых функций (хотя и в разной степени), 2 из которых уже общедоступны (не экспериментальные).

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

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

@ mklement0: Это неестественно / нелогичным / вы-имя-это. Скобки жесткие, фигурные скобки еще сложнее. В этом случае они не нужны.

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

Я еще не полностью увлечен добавлением конструкций C # в PowerShell. Во многих местах у нас уже есть проблемы, связанные с нехваткой символов на клавиатуре. ">" "Больше чем" или "перенаправить на"? Присваивание "=" или проверка-равенство (в разных языках используются: = для присвоения или == для равенства). Люди, переключающиеся между языками, забудут использовать -eq или -gt и мы застряли на этом ... Is + арифметическое сложение или конкатенация строки / массива. * - это одновременно умножение и подстановочный знак. % - это модуль и псевдоним для forEach-object.

Бедных ? Псевдоним для Where-Object. И имя автоматической переменной. Но попробуйте сделать Get-Alias ? или Get-Variable ? ... и тогда он выполнит работу подстановочного знака.

Сделайте Get-PSdrive и вы увидите, что «переменная» и т. Д. Не имеет символа «:» в конце имени, добавление единицы: к имени заставляет его рассматривать как диск. Если, конечно, вы не написали "$drive="C" и после него "$drive:temp" Любой, кто не знаком с PowerShell (и некоторые старые руки, когда они не думают об этом), будут сбиты с толку, узнав, что они ' Мы только что изобрели именованный «привод» осциллографа.

Итак, против этого ... можно было бы сказать: «Создайте новый синтаксис с дальнейшим использованием для": "и"? " ты не в своем уме?'
IIRC в твиттере @BrucePay описал тернарный оператор как «Не очень PowerShelly», и я склонен согласиться. Но голос «Сделать это больше похожим на C #» победил ...

Большинство людей написали бы $PSEdition.length -gt 2
но $PSEdition. length-gt2 также работает (вы даже можете сделать разрыв строки между двумя половинами.) "." также перегружен, выступая как «текущий каталог», «запуск в этой области» и «член». Однако в PS V1-V5 я не могу придумать никаких операторов, для которых требуется начальный пробел; "." странно ломается, если присутствует один _is_. Но для синтаксического анализа тернарному оператору _might_ может потребоваться начальный пробел
$host.debuggerEnabled?0:1 в порядке, потому что "?" предполагается _не_ частью собственности
$IsCoreCLR?0:1 не потому, что "?" считается частью имени переменной.

То же самое верно и для нулевых операторов объединения.
$null?? "default" нужен пробел, $true.length?? "default" нет.

Отложите на мгновение исходный вариант использования в верхней части проблемы. Приведенные выше случаи показывают, что что-то не совсем работает. Было бы исправлено, если бы имена переменных содержали "?" необходимо заключать в фигурные скобки, как имена, содержащие "." или "+" или ":", и @StartAutomating, кто является основным пользователем "?" in names думает, что это _break, с которым можно было бы справиться_. Еще до того, как мы дойдем до нулевых членов, это звучит как «найти способ сделать это уже»

Я думаю, что есть короткий промежуток времени, чтобы добавить экспериментальную функцию, требующую? должны быть заключены в скобки в именах, и если эта функция включена - что должно быть по умолчанию - все новое? операторы должны анализировать соответственно. Только если эта функция выключена, синтаксическому анализатору потребуются пробелы или фигурные скобки с этими операторами. Это критическое изменение, некоторые сценарии (небольшое не поддающееся количественному определению число) будут нарушены, если не будет установлен параметр (и запрет имени переменной должен предотвратить $ IsIt ? .tostring () является первой ошибкой в ​​сценарии, поэтому сценарии должны завершаться ошибкой, а не запускаться опасно). и в идеале этого не должно происходить с точечным выпуском (вы правы в том, что PowerShell на самом деле не использует sem-ver, но это все еще хороший принцип)

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

Это хороший момент, и в основном это то, что меня раздражало бы в отношении ${...?} на практике. Даже если я знаю, что нужно использовать {} , есть большая вероятность, что я в конечном итоге забуду, и это может привести к незаметным ошибкам, которые поначалу не очевидны, так как это, вероятно, все еще будет синтаксически правильным. $var??='bar' - хороший пример этого, хотя я обычно очень осторожен с интервалом.

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

  • разные контексты (например, * как оператор против * как подстановочный знак).

    • Различные контексты для ? действительно имеют что-то общее на определенном уровне абстракции: вы можете представить ? в Get-ChildItem | ? Target , $var?.Target , $? , $var ?? 'none' поскольку все они включают в себя своего рода _вопрос_ и, следовательно, _тест_ или _условное поведение_.

  • разумное полиморфное поведение (например, + выполняет конкатенацию со строкой).

Проблемным примером является & (оператор вызова против фонового оператора), который используется в одном контексте с разной семантикой, в зависимости только от его _position_.

Было бы исправлено, если бы имена переменных содержали "?" необходимо заключать в фигурные скобки, как имена, содержащие "." или "+" или ":"

Действительно, и это стоит повторить: возможность использовать ? в именах переменных никуда не исчезнет, ​​но вам придется использовать {...} будущем: ${var?} = @{ foo=1 }; ${var?}?.foo

Как вы демонстрируете, с точки зрения того, кто разумно _не_ ожидает, что ? будет действительным символом. . для точечного источника является оправданным исключением; ? в качестве Where-Object псевдоним, требующий пробела после, может вызывать удивление (например, gci|?target не работает), но имена команд _do_ необходимо отделять от их аргументов пробелом).

Возможность ставить пробелы между . и именем члена ( $obj. foo ) в первую очередь имеет смысл в многострочных операторах, хотя, возможно, более читабельная форма - знакомая по другим языкам - (также) разрешить пробелы _перед_ . :

# This does NOT work - the . must be *immediately after* the expression / member
(Get-Date)
  .ToUniversalTime()  
  .TimeOfDay

Возможность сделать это будет отражать недавно добавленную возможность разместить | в строке _next_:

 # Already works in PS Core
Get-Date
  | % ToUniversalTime
  | % TimeOfDay

Однако это приведет к неразрешимой неоднозначности с . в качестве оператора поиска точки и именами команд, которые начинаются с . маловероятно. (например, . foo в новой строке может быть доступом к свойству или командой, которая использует точечный источник функции или скрипта с именем foo (который должен быть в $env:PATH ) ; .foo может быть именем _команды_).

Многострочное продолжение RFC @KirkMunro, хотя и сосредоточено на том, чтобы _commands_ могло охватывать несколько строк, дало бы нам следующее лучшее решение - см. Https://github.com/PowerShell/PowerShell-RFC/pull/179#issuecomment -498734875.

Что касается экспериментальной особенности является по умолчанию: К счастью, _preview_ релизы - но не освобождают кандидаты - теперь включить _все_ экспериментальные функции по умолчанию; однако обратите внимание, что это постоянно переопределяется, если вы когда-либо запускали Enable-ExperimentalFeature либо _without_ -Scope или с -Scope CurrentUser с любыми функциями, которые вы там включили.

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

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

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

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

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

Спасибо, @ SteveL-MSFT - не могли бы вы поделиться результатами этого анализа?

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

Я также хотел бы увидеть результаты и используемый метод. Вчера вечером я провел собственный анализ корпуса PowerShell с помощью AST, который на моей машине занял почти 6 часов. Небольшое количество файлов не удалось проанализировать из-за того, что Защитник Windows поместил файл в карантин.

Я нашел 11 уникальных имен переменных в 8 файлах, оканчивающихся вопросительным знаком (за исключением $?, Которое появилось 8846 раз). Всего было 1 895 983 уникальных переменных в 408 423 файлах. Это означает, что 0,0006% всех уникальных переменных PowerShell в корпусе имеют имя переменной, оканчивающееся знаком вопроса.

Вот 8 файлов и 11 уникальных переменных:

File                 : C:\Temp\PowerShellCorpus\Github\alanrenouf_PowerActions\Kill-VM.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\anthonywlee_PowerShell\Send_Notification.ps1
QuestionMarkVars     : To?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\aspear_My-PowerCLI-scripts\my-labotomize-vms.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\jlundstrom_Scripts\Powershell\7Zip SFX Creator\Build.ps1
QuestionMarkVars     : Title?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\pslaughter_Working\Setup-Host.ps1
QuestionMarkVars     : HostIp?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\Github\stevenayers_PowerShell-Scripts\Random Functions\Add-OutlookConferenceRegionNumber.ps1
QuestionMarkVars     : {key1?, key2?, key3?, key4?}
QuestionMarkVarCount : 4

File                 : C:\Temp\PowerShellCorpus\Github\unixboy_powershell-stufff\stopvm.ps1
QuestionMarkVars     : vParam?
QuestionMarkVarCount : 1

File                 : C:\Temp\PowerShellCorpus\PowerShellGallery\PsHg\0.6.2\PsHg.psm1
QuestionMarkVars     : PsHg?
QuestionMarkVarCount : 1

Это сценарий, используемый для создания отчета:

Get-ChildItem -Path C:\Temp\PowerShellCorpus -Include *.ps1, *.psm1 -File -Recurse | ForEach-Object -Parallel {
    try {
        $content = $PSItem | Get-Content

        $ast = [System.Management.Automation.Language.Parser]::ParseInput($content, [ref]$null, [ref]$null)
        $variables = $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst ]}, $true)

        # First query because I forgot to exclude $? variables
        # $nonQuestionMark = $variables | Where-Object VariablePath -notmatch '\?$' | Select-Object -Unique
        # $QuestionMark = $variables | Where-Object VariablePath -match '\?$' | Select-Object -Unique

        $nonQuestionMark = $variables |
        Where-Object VariablePath -notmatch '\?$' |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $QuestionMark = $variables |
        Where-Object { $_.VariablePath -match '\?$' -and $_.VariablePath.UserPath -ne '?' } |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $output = [pscustomobject]@{
            File = $PSItem
            QuestionMarkVars = $QuestionMark
            QuestionMarkVarCount = $QuestionMark.Count
            NonQuestionMarkVars = $nonQuestionMark
            NonQuestionMarkVarCount = $nonQuestionMark.Count
        }

        $output
    }
    catch {
        throw
    }
}

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

$abc = 'test'
$isAwesome? = $true
$?

$test = {
    $?
    $isabc? = { $adce? = 'test' }
    ${def?} = 123
    ${is a bad var name?} = $isabc?
}

Результаты в:

File                    : C:\temp\variable.ps1
QuestionMarkVars        : {isAwesome?, isabc?, adce?, def?, is a bad var name?}
QuestionMarkVarCount    : 5
NonQuestionMarkVars     : {abc, true, test}
NonQuestionMarkVarCount : 3

Как говорят в классе математики, «покажите, пожалуйста, свою работу» 😄

Отличное расследование ,

Я думаю, вы потенциально можете исключить еще больше случаев, учитывая, что форма ${...} будет продолжать работать с предложенным изменением - например, нам нужно беспокоиться только о $isAwesome? , а не о ${isAwesome?}

В вашем примере файла:

$variables.Extent.Text.Where({ $_ -match '(?<!\$)\?$' }) | Select-Object -Unique

мы получаем только:

$isAwesome?
$isabc?
$adce?

То есть не нужно учитывать ${def?} и ${is a bad var name?} .

PS: Вероятно, также лучше использовать $PSItem | Get-Content -Raw для чтения всего скрипта как одной строки.

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

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

File                    : C:\temp\variable.ps1
QuestionMarkVars        : {isAwesome?, isabc?, adce?}
QuestionMarkVarCount    : 3
NonQuestionMarkVars     : {abc, true, test}
NonQuestionMarkVarCount : 3

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

Обновленный скрипт:

Get-ChildItem -Path C:\Temp\PowerShellCorpus -Include *.ps1, *.psm1 -File -Recurse | ForEach-Object -Parallel {
    try {
        $content = $PSItem | Get-Content -Raw

        $ast = [System.Management.Automation.Language.Parser]::ParseInput($content, [ref]$null, [ref]$null)
        $variables = $ast.FindAll({$args[0] -is [System.Management.Automation.Language.VariableExpressionAst ]}, $true)

        $nonQuestionMark = $variables |
        Where-Object VariablePath -notmatch '\?$' |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $QuestionMark = $variables |
        Where-Object { $_.VariablePath -match '\?$' -and $_.VariablePath.UserPath -ne '?' -and $_.Extent.Text -match '(?<!\$)\?$' } |
        ForEach-Object { $_.VariablePath.UserPath.ToString() } |
        Select-Object -Unique

        $output = [pscustomobject]@{
            File = $PSItem
            QuestionMarkVars = $QuestionMark
            QuestionMarkVarCount = $QuestionMark.Count
            NonQuestionMarkVars = $nonQuestionMark
            NonQuestionMarkVarCount = $nonQuestionMark.Count
        }

        $output
    }
    catch {
        throw
    }
}

Спасибо, @ThomasNieto - я надеялся, что мы сможем снизить процент до 0,0005% ... (шучу).

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

Итак, помимо решения проблемы, возникает вопрос: нуждается ли общедоступный корпус в обновлении? Поддерживается ли он официально (в настоящее время от 26 июля 2017 г.)?

@ SteveL-MSFT, не могли бы вы уточнить?

@ mklement0 Я

В https://github.com/PowerShell/PowerShell-RFC/pull/223#discussion_r318340339 @adityapatwardhan провел анализ с использованием того же общедоступного корпуса, который я использовал, и

Глядя на связанный комментарий, я думаю, что @adityapatwardhan говорит, что _среди всех переменных, которые имеют ? в имени_ 62%, есть ? в качестве _последнего_ символа имени.

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

На данный момент, похоже, комитет просто говорит: «Мы не хотим», и если это так, зачем вообще использовать эту функцию, если в результате она будет неуклюжей и неиспользуемой?

@ThomasNieto аналогично я провел анализ этих проблем и пришел к результатам, аналогичным вашим.

@mburszley Спасибо за ваш анализ. Я хотел использовать другой метод (ast vs regex), чтобы подтвердить результаты, полученные в прошлом году.

Я слышу тебя, @mburszley , но ты:

комитет просто говорит "мы не хотим"

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

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

Как насчет моего предложения в https://github.com/PowerShell/PowerShell/issues/11379#issuecomment-566756120 ?

Я предлагаю сделать это так, чтобы $obj?.Method() по умолчанию работал как $<var i="9">obj</var> ?. <var i="12">Method</var>() и возвращался только к $<var i="14">obj?</var> . <var i="17">Method</var>() когда $<var i="19">obj</var> не существует в области видимости, но $<var i="21">obj?</var> существует, а #requires ‑Version не является v7 +.

Я ожидал, что это решит большинство проблем с обратной совместимостью.

@ThomasNieto

@ mklement0 верен, я имел в виду, что среди переменных, использующих ? , 62% используют его в конце.

Из результатов, показанных в комментарии здесь, кажется, что все переменные, которые используют ? используют его в конце. Отсюда и предложение сделать неразрывное изменение.

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

Конечно ... но анализ показывает, что практически никто ими не пользуется.

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

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

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

Разве вас не беспокоят люди, использующие ?. как они это делают в C / C ++ / C #, и получают неправильный результат без каких-либо указаний на то, что они сделали что-то не так? Например:

PS> $res = "My length is 15"
PS> $res?.Length
0

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

Возможно, PowerShell потребуется эквивалент Enable-ExperimentalFeature назовем его Enable-PSFeature , для функций, которые больше не находятся в экспериментальном состоянии, но не готовы к включению по умолчанию . Для скриптов, которые выполняются с таким кодом, как этот $?.GetType() вы можете начать выдавать предупреждения о том, что в будущем это выйдет из строя и вместо этого вы должны использовать ${?}.GetType() . Отдельные сценарии могут включать эту функцию с помощью #requires -version 7.1 -feature PSNullConditionalOperators . Тогда, возможно, всякий раз, когда попадет PS 8, эта функция может быть включена по умолчанию.

Пример @rkeithhill переходит прямо к
Забудьте на мгновение то, что вы знаете о том, как работает PowerShell сегодня. Когда ты видишь

PS> $res?.Length

Это похоже на _name, operator, name_ - что, конечно же, есть.
Что могло бы иметь больше смысла: _ "?" является частью operator_ или _ "?" является частью первого name-element_?
Если эти операторы необходимы (а я думаю, что они «не PowerShelly»), это выбор между тем, что будет использоваться неправильно, и небольшим количеством поломок.

Я бы начал вводить правила в psScriptAnalyzer _now_, чтобы сказать, что использование определенных [разрешенных] символов в именах переменных - плохой стиль.

Мне не нравится Enable-feature . Кто-то вставляет его в скрипт, чтобы заставить работать то, что они хотят использовать, и старый скрипт ломается, если запускается после нового скрипта (но работает в остальное время). Я видел, как сценарий третьей стороны X завершался неудачно после запуска сценария Y_, потому что Y включает строгий режим, а X полагается, что он выключен; авторы обоих сценариев числятся у меня в плохих книжках.

Я бы сейчас начал вводить правила в psScriptAnalyzer, чтобы сказать, что использование определенных [разрешенных] символов в именах переменных - плохой стиль.

Вот проблема: если вы измените способ токенизации переменных PowerShell, PSScriptAnalyzer тоже будет их видеть по-другому. Если у вас есть переменная типа $res? , то посмотрите на что-то вроде этого:

{
$res?.Length
}.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Expression.VariablePath.UserPath

дает тебе

res?

НО, если вы измените способ анализа PowerShell, скажем, в 7.2, вы получите


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

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

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

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

@rjmholt , хотя я думаю, что мы все понимаем, насколько сложно подобное изменение _ в принципе_, в данном конкретном случае это не имеет значения _ на практике_, и я не думаю, что правило PSSA вообще необходимо:

  • Анализ @ThomasNieto показал, что - если мы полагаем, что корпус является репрезентативным для всего кода PowerShell - что _виртуально никто не использует переменные с именами, заканчивающимися на ? _.

  • Если бы я догадывался, большинство людей даже не подумали бы об использовании таких имен переменных, потому что они не ожидали бы, что они будут работать (включая меня), и, вероятно, даже не заметят того исключения, что автоматический $? variable составляет (имя которой также является исключением в POSIX-совместимых оболочках, таких как bash ).

    • Фактически, если присмотреться к анализу @ThomasNieto, то среди перечисленных файлов 8 :

      • 5 содержат только _ ложные срабатывания_, а именно переменные, которые используются внутри _строки_, такие как "Are you sure you want to kill $vParam?" - на самом деле, это использование _сломано_ и показывает, что авторы сценария _не_ ожидали, что ? будет часть имени переменной.

      • 2 этих файлов больше не являются общедоступными.

      • Только _1_ из них является добросовестным использованием ? -кончающих переменных - в качестве _Boolean_ переменных (например, $key1? ), которые, помимо прочего, _не_ объединены с оператором доступа к члену. , . / cc @stevenayers.

Исходя из вышесказанного, мне кажется, что это учебное ведро 3: Маловероятное изменение

_Документирования_ изменения поведения (с обоснованием) должно быть достаточно.

Конечно:

  1. это основано на (а) правильности анализа и (б) репрезентативности общедоступного корпуса.

  2. это резко расходится с утверждением комитета о том, что «довольно часто использовались имена переменных, оканчивающиеся знаком вопроса» (что, конечно, было бы проблемой только в том случае, если такие имена переменных не заключены в {...} ).

Исходя из предположения, что 1. верно (мы не слышали ничего, что поддерживало бы 2.), у нас есть два варианта:

  • Сорвите пластырь и просто запретите ? в именах переменных в дальнейшем, если только они не заключены в {...} (за очевидным исключением $? ) - это сломает исчезающе маленький доля скриптов, которые в настоящее время полагаются на это.

    • То есть что-то вроде $key1? , за которым не следует ни . ни другое ? ( $key1??'else' ), ни тернарное выражение ( $key1?1:0 ) вызовет _parser error_.

      • Как показывают примеры объединения нулей и тернарных операторов, они тоже выиграют от этого изменения - в настоящее время вам нужен либо _space_ - $key1 ?1:0 / $key1 ??'else' - или {...} - ${key1}?1:0 / ${key1}??'else'

    • Внутри _expandable strings_ ? больше не будет считаться частью (не {...} -enclosed) имени переменной, поэтому мы фактически _fix_ существующие скрипты, которые по ошибке использовали строки, такие как "Are you sure you want to kill $vParam?" . 😁
  • Если мы действительно чувствуем, что нам нужно избегать нарушения исчезающе малой части существующих скриптов, мы _ могли бы_ рассмотреть логику, предложенную @ ExE-Boss , но я не думаю, что мы хотим вводить эту концептуальную сложность - не говоря уже о сложности реализации (которую я не могу говорить с).

@ SteveL-MSFT

Благодарим за обсуждение этой проблемы на сентябрьском звонке сообщества (https://aka.ms/PSCommunityCall; на момент написания этой статьи запись сентябрьского звонка там не размещалась, но доступна по адресу https://aka.ms/JoinPSCall, до следующего звонка), начиная с 46:00, на основе предложения @ThomasNieto в предыдущем запросе тем для обсуждения на https://github.com/PowerShell/PowerShell-RFC/issues/260 :

(Я надеялся предоставить прямую ссылку на соответствующую часть записи, но это нужно будет подождать, пока она не будет размещена на YouTube и связана с https://aka.ms/PSCommunityCall.)

На основании ваших замечаний в записи:

  • Учитывая, что вы выразили готовность вернуться к этому вопросу, повторно откройте эту проблему и повторно пометьте ее как Review - Committee .

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

    • Я не думаю, что кто-то здесь _не_ знает, что {...} _can_ и иногда _должен_ использоваться для устранения неоднозначности имен переменных.
    • Речь также идет не об _эстетике_, а в первую очередь о _недании ожидаемого поведения_ таким образом, что _пошла незаметно_, из-за _не ожидаемого использования {...} в этом случае_; Второстепенная проблема - неудобство набора текста.
GitHub
Документы RFC (Request for Comments) для отзывов сообщества об изменениях дизайна и улучшениях экосистемы PowerShell - PowerShell / PowerShell-RFC
Команды Microsoft

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

Была ли эта страница полезной?
0 / 5 - 0 рейтинги