Powershell: 不需要 { ... } 围绕变量名进行空条件成员访问

创建于 2019-12-17  ·  53评论  ·  资料来源: PowerShell/PowerShell

来自#3240 的跟进。

Null 条件成员访问 ( ?. ) 已实施,并将作为 7.0 中的 _experimental_ 功能提供

然而,为了向后兼容,实现目前要求将变量名括在{...} ,因为?在技​​术上是变量名中的合法字符,其使用令人惊讶地_doesn't_ require {...}

也就是说,如果您想忽略在$obj$null (或未定义)的情况下调用$obj.Method()的尝试,您会期望以下内容,类似于 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_: Null-coalescing - $var??='default$a??$b - 和三元运算符 - $var?1:0 - 同样受到影响(尽管在?之前使用空格解决问题)。

这个要求是 (a) 出乎意料和 (b) 繁琐的:

  • 新用户不会期望需要{...} ,也不一定知道 _it_ 是以下失败时所需要的(可能是 _undetected_,除非Set-StrictMode -Version 2或更高版本有效): $o = [pscustomobject] @{ one = 1 }; $o?.one

  • 即使用户 _do_ 知道需要{...}

    • 他们有时会忘记使用它,因为它违反直觉。
    • 当他们记住时,这个看似人为的要求将是一个持续的挫折来源,特别是因为{...}很难输入。

使用{...}不应该是必要的,虽然不需要它相当于 _break_ 更改,但可以说它属于第三类:不太可能的灰色区域


为什么这种变化应该被认为是可以接受的:

注意: @rjmholt讨论了为什么更改 PowerShell 的语法在此评论中_一般_有很大问题,但由于下面介绍的原因,我认为值得在这里破例。

?.是一个 _new_ 句法特征; 根据定义,使用它的脚本_不能​​_(有意义地)在旧版本上运行 - 除非您专门添加提供与旧版本兼容的代码路径的条件,但这似乎不值得 - 然后您将只使用旧功能。

是的,在使用$var?作为变量名的旧脚本中$var?.foo的解释会中断,但是:

  • ?不应该被允许作为标识符的一部分 _ 而不将它包含在{...} _ 中。

  • 由于能够这样做是出乎意料的,甚至可能对许多人来说是 _unknown_,因此根据个人经验,此类变量名称在野外极为罕见,但@mburszley的分析提供了更切实的证据

    • 甚至 Ruby 只允许? (和! )在标识符的 _end_ 处,并且只有 _method_ 标识符,我怀疑大多数 Ruby 用户都知道他们应该_不_假设其他语言支持同样的事情。

所以,从实用的角度来说:

  • 绝大多数现有代码不会受到影响——根本不会遇到诸如$var?.foo类的令牌。

  • 如果您使用新语义编写$var?.foo ,那么是的,在旧版本上运行它可能会导致不同的行为(而不是以明显的方式破坏),具体取决于有效的严格模式 - 但 _you 应该始终强制执行按预期运行代码所需的最低版本_( #requires -Version ,模块清单键)。

总而言之,对我来说,这是 3 级更改的明显案例:技术上的重大更改在提供真正好处的同时破坏很少的现有代码(或者,相反,避免由于意外行为而导致的长期头痛)。

Committee-Reviewed Issue-Enhancement WG-Language

最有用的评论

@rjmholt虽然我认为我们都可以理解这样的改变_原则上_有多棘手,但在这种特殊情况下_实践_并不重要,我认为甚至不需要 PSSA 规则:

  • @ThomasNieto的分析表明——如果我们相信语料库代表所有 PowerShell 代码——几乎没有人使用名称以? _ 结尾的变量。

  • 如果我猜的话,大多数人甚至不会_认为_使用这样的变量名,因为他们不希望它们起作用(包括我在内),甚至可能没有注意到自动$?变量构成(其名称在 POSIX 兼容的 shell 中同样是一个例外,例如bash )。

    • 事实上,仔细观察@ThomasNieto的分析会发现,在列出的8文件中:

      • 5只包含_误报_,即在_strings_ 内部使用的变量,例如"Are you sure you want to kill $vParam?" - 事实上,这些使用是_broken_,并表明脚本作者确实_不_期望?变量名的一部分。

      • 这些文件中的2不再公开可用。

      • 其中只有 _1_ 个真正使用了?结尾的$key1? ),作为旁白,因此 _not_ 与成员访问运算符结合使用, . /cc @stevenayers。

基于上述,这让我觉得这是一本教科书桶 3:不太可能的灰色区域变化,因此是_可以接受的_ - 我认为这种变化的_好处_已经有说服力的争论。

_记录_行为的改变(有理由)就足够了。

当然:

  1. 这是基于 (a) 分析正确和 (b) 公开可用的语料库具有代表性。

  2. 这与委员会声称“有相当多的以问号结尾的变量名的用法”的说法完全不一致(当然,如果这些变量名没有包含在{...} ,这只会是一个问题

继续假设 1. 是真的(我们没有听到任何支持 2. 的消息),我们有两个选择:

  • 撕掉创可贴,然后简单地禁止?在变量名中,除非包含在{...} (明显的例外$? ) - 这将打破消失的小目前依赖于此的脚本比例。

    • 也就是说,像$key1?这样的东西既不是.也不是另一个? ( $key1??'else' ) 也不是三元表达式 ( $key1?1:0 )会导致_解析器错误_。

      • 正如空合并和三元运算符示例所示,它们也将从这一变化中受益 - 目前,您需要一个 _space_ - $key1 ?1:0 / $key1 ??'else' - 或{...} - ${key1}?1:0 / ${key1}??'else'

    • 在 _expandable strings_ 中, ?将不再被视为(非{...}封闭)变量名的一部分,因此我们实际上会_修复_错误地使用诸如"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,它使用$?作为退出代码

$?是 POSIX-like shell 中的 _exception_; 您可以_不_执行以下操作:

# 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() } _just 加入 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 将不胜感激,以便像我这样将使用此语法的人(我将使用它_很多_)可以选择退出噪音.

由于我们在这里解释:
“逻辑清楚地表明,多数人的需求大于少数人的需求。” - 史波克

因此,通过数学计算,_不_这样做有几个具体的原因,而这样做的原因只有一个模糊的(对于某些用户来说_可能_更容易)。

@StartAutomating :有多种原因,它们是具体的,而不是模糊的。 此外,这样做的目标之一是代码简单。 必须用{}包装变量名直接违背了这个目标。

我和@mklement0@KirkMunro 在一起。 我个人认为${...}?语法不自然且不必要的复杂。 我们已经进行了重大更改,在这种情况下,为换取清晰度而付出的代价很小。

@StartAutomating

  1. 此外,大多数遇到意外变量名称问题的人都学习了 ${} 语法(您已经用下划线自然地命中了它)。 我希望“想要使用此功能的人”与“已经知道 ${} 语法的人”的维恩图重叠接近 100%

这站不住脚。 下划线是变量名中的有效字符。

@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 的更改行为在保存的脚本之外不起作用,并且一旦有人从长脚本中复制一些代码而没有设置标签,就会失败。

这适用于使用早期版本不支持的功能的 _any_ 代码。
如果这是最重要的问题,则不会引入新的语言功能或新的 cmdlet / cmdlet 参数。

(希望在旧代码中您至少有Set-StrictMode -Version 1有效,在这种情况下$var?.foo会大声失败,因为不存在名为var?变量)。

  1. 消除了在名称周围放置大括号的需要。

(...)替换{...}只是微不足道的改进。

无论使用什么额外的语法,这里的问题是_需要_额外的语法-对于应该_原样_工作的东西-不仅是为了打字方便,而且是为了满足_合理的期望_。

  1. 基于#requires 的更改行为在保存的脚本之外不起作用,并且一旦有人从长脚本中复制一些代码而没有设置标签,就会失败。

这适用于使用早期版本不支持的功能的 _any_ 代码。
如果这是最重要的问题,则不会引入新的语言功能或新的 cmdlet / cmdlet 参数。

那不是我的意思。 进一步有人建议,如果#requires 指定 pwsh 7 那么处理可能会有所不同。 仅在您输入 #requires 时才点亮新行为不好。

(希望在旧代码中您至少有Set-StrictMode -Version 1有效,在这种情况下$var?.foo会大声失败,因为不存在名为var?变量)。

根据我的经验,很少使用 Set-Strictmode。 因为(a)假设没有它的默认值是“正确的”,并且(b)使用依赖于它的东西的频率太高了。 这是这些运算符的常见用途。 无论如何,问题是旧代码在哪里 $var? 确实存在并且确实有一个 foo 属性,但是现在代码查找 $var ... 可能新的处理会导致错误 IF strictmode 被设置,因为 $var 不存在......

  1. 消除了在名称周围放置大括号的需要。

(...)替换{...}只是微不足道的改进。

是的。 ($x) 比 ${x} 稍微好一点,但只是稍微好一点。 ( Get-user "Bob")?.disable()

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

无论使用什么额外的语法,这里的问题是_需要_额外的语法-对于应该_原样_工作的东西-不仅是为了打字方便,而且是为了满足_合理的期望_。

是的。
$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 用于严肃的事情时,我对缺少空合并运算符感到震惊,甚至没有我可以使用的三元运算符 - 这导致我在 Stack Overflow 上发布了

@StartAutomating说:

除了极客,我希望 ${obj}?.Method() 或 $obj?.Method() 不会有什么上升。 网络上有十年关于如何管理空值的指导,我没有看到很多人从 if ($obj) { $obj.Method() } 切换到 v7 的潮流

我只是 n=1,但作为非 PowerShell 开发人员,我强烈反对。 新的 ? 语法是我很快就会发现的东西,即使是每隔几个月接触一次 PowerShell 的人,它肯定会增加我将来使用 PowerShell 代替替代方案的几率。 如果我发现向后兼容需要一些疯狂的 {} 语法,我会翻白眼回到我已经在使用的任何东西。

我不明白这里对向后兼容性的强调。 PowerShell Core 已经破坏了我现有的大部分脚本,因为它们使用了 .NET Core 中没有的部分 .NET。 (我不是在抱怨这一点——.NET Core 很棒,但如果你要打破向后兼容性,现在是你的机会。)

首先,我相信我最初的反馈造成了误解。

我的假设是 ${} 对 PowerShell 开发人员来说不会_那么奇怪_,因为如果他们曾经将变量嵌入到 ? 或 并在字符串中下划线。

感谢您多次提醒,这种语法对于非 PowerShell 开发人员来说可能很笨拙。

我的反馈的第二部分可能在混合中丢失了:因为空合并的其他替代方案在 PowerShell 中已经存在了更多时间并被广泛使用,我不知道该功能会_与现有的 PowerShell 用户_相比有多少提升。 你想进来说“这是我开始使用 PowerShell 需要做的一件事”,我无法反驳你的意见(尽管我可能会说你错过了寻找语言树的酷特性森林熟悉度)。

我的第三点反馈似乎被夸大了。 作为偶尔使用名为 $IsThisRight 之类的变量的人之一发言? = Test-Something ,听到有关此变量限制的潜在消息时,我的第一反应是“哦,好吧,我想我必须重命名一些变量,反正我没有得到那么多。”

因此,尝试重述并结束线程:

  1. 恕我直言,${} 对于 _existing_ 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 确实是带有语法糖的糖精。

为了教育那些不知道的人,您可以在 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 | 选择对象 -First 1$TheAssigningForeachWay = foreach ($null, 1, 2, $null, 3, 4 中的$item) {if ($item -ne $null) { $item;



现在已经足够了,但它应该证明语言语法具有很大的灵活性。 所有这些演示都将一直运行到 PowerShell 1.0

然而,在这种特殊情况下,似乎很多 PowerShell 脚本都处于由于向 Core 过渡而崩溃的边缘。

在这一点上你是不正确的。 Core 实际上很少有语法破坏性更改,而且更多的模块比您认为在 core 上工作得很好(+-Linux 与 Windows 路径/换行符的一些问题)。 在更实际的情况下,如果您有某种 Windows 平台特定的 API 依赖项(例如,我不会屏住呼吸让我的 WPF 包装器在 Linux 上工作),那么您的模块不太可能在 Core 上工作。

无论哪种方式,我仍然对语法更改感到满意:我只是觉得最新回复中的两个断言需要解决和调用。

回复:语法糖,我讽刺的心引用电影《抢夺》:“我已经够甜了”
回复:在 Core 中打破变革潜力:我们现在很少,让我们尽可能多地保持这种状态。

@StartAutomating你是说我不能开枪? 因为我觉得还有地方放更多的糖😜

为了教育那些不知道的人,您可以在 PowerShell 中通过多种方式“空合并”:

我确实弄明白了(这是我自己写的答案),但感觉像是一团糟。 这是一个很酷的想法,具有很大的潜力,但正如 Stack Overflow 回答的一位评论者指出的那样,评估不会短路。 ?.运算符意味着在链接调用/属性时这通常不再是问题。 然而,早在 2013 年,它就无法使用——即便如此,这些示例中还有很多事情要做,你可以在评论中看到人们是如何发现这令人困惑的。 我必须逐个符号地详细分解这些示例的作用。

在这一点上你是不正确的。 Core 实际上很少有语法破坏性更改,而且更多的模块比您认为在 core 上工作得很好(+-Linux 与 Windows 路径/换行符的一些问题)。 在更实际的情况下,如果您有某种 Windows 平台特定的 API 依赖项(例如,我不会屏住呼吸让我的 WPF 包装器在 Linux 上工作),那么您的模块不太可能在 Core 上工作。

我只是从这方面的经验谈起,而不是实际进行和收集适当的统计数据。 例如,在 Core 之前,我可以使用[System.Web.Security.Membership]::GeneratePassword(32, 6)生成密码,但是System.Web在 Core 中不可用。 (我知道我可能会加载旧的 System.Web.dll,但如果我没有必要,我宁愿不这样做。)对我来说,语法更改实际上比缺少 API 更容易解决。 为 devkit 生成一堆 Docker 秘密似乎是使用快速脚本的好地方,但令人惊讶的是,它在 PowerShell Core 中比在 bash 中更加冗长。 当然,bash 的解决方案通常看起来很丑陋。

回复:语法糖,我讽刺的心引用电影《抢夺》:“我已经够甜了”

这取决于。 有时我觉得 PowerShell 非常令人愉快。 处理 null 是我通常不这么认为的关键领域之一——尽管我对很多语言都这么说。 我还发现,与三元运算符或 bash 的&&||链接相比,传统的内联 PowerShell if-else 语句很麻烦。 由于这是多年前导致我远离 PowerShell 的关键问题之一,因此我认为值得一提。

另一方面,我每天都要处理各种各样的语言,无论我在写什么,我总是在想,“这在 X 语言中会容易得多!”--然后当我正在使用语言 X,我在想,“这在语言 Y 中会容易得多!” 但是,嘿,如果我有机会大声说出来并且可能会影响语言的方向,我会接受的。

我真的不想在这里改变任何想法,只是添加另一个观点。

@StartAutomating :你是说我不能开枪? 因为我觉得还有地方放更多的糖😜

我总是爱我一些新的语法糖! 我选择编程语言就像一个孩子在万圣节上捣蛋。

@Zenexer :我真的很喜欢Powershell ,我喜欢。 我有幸每天在工作中广泛地做PS 。 我喜欢它的灵活性,尤其是与 .NET 的共生关系,天哪! 这就是我关心它的方向的原因,也是我难以接受${...}?语法的原因。

好点,但让我澄清一些事情。

不管是好是坏,PowerShell 到目前为止还没有使用语义版本控制。
新的主要版本只带来了_新_功能,并坚定地致力于向后兼容。
突破性的变化开始在 Core 中蔓延,部分原因是由于跨平台。
选择主要版本 v7.0 来表示向后兼容性的 _increase_:尝试为 Windows PowerShell 提供可行的替代品。

虽然许多历史问题在不严重破坏现有代码的情况下无法修复,因此只能作为可选(选择加入)功能提供,
桶 3破坏性更改应该始终是一个选项:进行修复/改进,具有破坏现有代码的 _ 潜力,但在实践中不太可能; 总的来说,改进语言是值得的。

正如在 OP 中所争论的那样,手头的案例让我印象深刻,因为它是 3 号桶的变化; 虽然我们知道 _some_ 脚本会中断( @StartAutomating会知道如何修复他的脚本),但数据表明这种情况非常罕见。

为什么很少见?

因为在变量名中使用?对大多数人来说完全不会发生,如果发生了,他们不应该期望它在没有{...}的情况下工作 _:如果以下所有内容仅适用于{...} ,为什么期望它与? ?: . , ;:根本不能使用,因为它总是被解释为驱动分离器)

允许诸如$var?$va?r而不要求{...}是一种自找麻烦的宽容,而麻烦已经到来:它阻碍了语言,在这种情况下。

受 C# 启发,有 3 个新的相关功能(至少在句法形式上相关):

  • 三元条件 - 将成为 7.0 中的真正功能

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

  • 空合并运算符 - 将是 7.0 中的真正功能

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

  • 空条件运算符 - 手头的问题 - 将是 7.0 中的 _experimental_ 功能

    • $possiblyNull?.ToString()

它们都将?作为语法的一部分,并且都受到遗留$var?解析负担的影响,尽管?.最明显的是:

  • 虽然您可能会争辩说$var?1:0类的东西只对代码高尔夫球手感兴趣,但如果?具有句法函数,它就会工作得很好 - 目前您必须在?之前使用 _space_

  • 相比之下,我认为有人尝试$var??='bar' (可能不那么$baz = $foo??$bar )是合理的,目前也需要一个空格。

也就是说,如果我们坚持当前对$var?解析,我们就会阻碍 _three_ 新功能(尽管程度不同),其中 2 个已经普遍可用(非实验性)。

要求{...}的问题_不_只是打字的不便:首先是_不期望需要它_,然后_忘记需要它_(可能存在细微的故障)-因为这太违反直觉了。

@ExE-Boss,我同意@jhoneill 的观点,即实现版本条件行为会出现问题。 在这种情况下,我认为没有必要用向后兼容的特殊外壳来阻碍引擎。

@mklement0 :这不自然的/违反直觉的/你的名字。 括号很难,大括号更难。 在这种情况下不需要它们。

@mklement0因为协议似乎正在破裂......我认为这是一个很好的总结。

通过向 PowerShell 添加 C# 结构,我还没有完全被说服。 我们已经在很多地方遇到了键盘上没有足够符号的问题。 ">" 是“大于”还是“重定向到”? 是“=”赋值还是test-equality(不同的语言使用:= 表示赋值或== 表示相等)。 人们在语言之间切换时会忘记使用-eq-gt而我们坚持使用...是+算术加法,还是字符串/数组连接。 * 既是乘法又是通配符。 % 是模数和forEach-object.的别名

贫穷的 ? 是Where-Object.的别名 并且是自动变量的名称。 但是尝试执行Get-Alias ?Get-Variable ? ... 然后它完成了通配符的工作。

执行Get-PSdrive并且您会看到“变量”等名称末尾没有“:”,在名称上添加一个 : 会使其被视为驱动器。 当然,除非你提到已经写了"$drive="C" 并跟随它"$drive:temp"任何刚接触 PowerShell 的人(以及一些老手,当他们不考虑它时)都会感到困惑,因为他们知道他们我刚刚发明了一个命名范围“驱动器”。

所以反对那个......有人可能会说'创建一种新的语法,进一步使用“:”和“?” 你疯了?'
IIRC,在 twitter 上@BrucePay将三元运算符描述为“不是很

大多数人会写$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谁是 "?" 的主要用户在名称中认为这是可以处理的 _break _。 在我们获得空成员之前,这听起来像是“找到了一种方法来做到这一点”

我认为有一个很短的时间窗口可以放入需要 ? 以名称括起来,如果该功能开启 - 这应该是默认设置 - 所有新的 ? 运营商应该相应地解析。 只有当该功能关闭时,解析器才会需要这些运算符的空格或大括号。 这是一个重大更改,除非设置了选项,否则某些脚本(一个小的无法量化的数字)将被破坏(并且禁止变量名应该防止 $IsIt ?.tostring() 是脚本中的第一个错误,因此脚本应该失败,而不是危险地运行)。 理想情况下,点发布不应发生这种情况(您对 PowerShell 没有真正使用 sem-ver 是正确的,但这仍然是一个很好的原则)

要求 {...} 的问题不仅仅是打字的不便:它首先不期望需要它,后来忘记了它的需要(潜在的微妙故障) - 因为它太违反直觉了。

这是一个很好的观点,并且在很大程度上会让我在实​​践中对${...?}感到恼火。 即使我知道使用{} ,我也很有可能最终会忘记,这可能会导致一开始并不明显的细微错误,因为它可能在语法上仍然有效。 $var??='bar'就是一个很好的例子,尽管我通常对间距非常小心。

谢谢, @jhoneill ,这很好地说明了有多少符号在 PowerShell 中执行了很多双重(三重,...)任务。
在大多数情况下,一旦您了解了所有用途,这不是问题,原因如下:

  • 不同的上下文(例如, *作为运算符与*作为通配符。)

    • ?的不同上下文实际上有一些共同点,在一定的抽象层次上:您可以在Get-ChildItem | ? Target$var?.Target$?设想? $? , $var ?? 'none'都涉及一种_问题_,因此是_测试_或_条件行为_。

  • 合理的多态行为(例如, +执行与字符串的连接)。

一个有问题的例子是& (调用操作符 vs. 后台操作符),它被用在具有不同语义的相同上下文中,仅取决于其_position_。

如果变量名包含“?”,它将被修复。 需要像包含“.”的名称一样用大括号括起来。 或“+”或“:”

确实,并且需要重复:在变量名称中使用?的能力不会消失 - 但您必须继续使用{...}${var?} = @{ foo=1 }; ${var?}?.foo

正如您所展示的,从某人明智地_不_期望?是有效字符的角度来看。 .用于点采购是合理的例外; ?作为Where-Object别名后面需要一个空格可能会令人惊讶(例如, gci|?target不起作用),但是命令名称 _do_ 需要用空格与其参数分开)。

能够在.和成员名 ( $obj. foo ) 之间放置空格主要在多行语句中有意义,尽管可以说更具可读性的形式 - 从其他语言中熟悉 - 是(也)允许空格 _before_ .

# 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可以是_command_的名称)。

@KirkMunro的多行延续 RFC 虽然专注于允许 _commands_ 跨越多行,但将为我们提供下一个最佳解决方案 - 请参阅https://github.com/PowerShell/PowerShell-RFC/pull/179#issuecomment -498734875。

至于默认开启的实验性功能:幸运的是,_preview_ 发布 - 但不是发布候选 - 现在默认开启 _all_ 实验性功能; 但是,请注意,如果您曾经运行过Enable-ExperimentalFeature _without_ -Scope-Scope CurrentUser以及您在那里启用的任何功能,那么这将被持续覆盖。

@mklement0虽然我基本上同意所有这些,但我担心我们可能会因为你那里的额外零碎而使这个特定的讨论脱轨; 如果您想解决点属性运算符周围的行延续问题,如果还没有的话,您可能需要为此打开一个新问题。 🙂

我同意继续允许?作为一个普通的变量名字符对用户来说充其量是极其混乱的,因为该字符的使用突然使空白敏感,至少从历史上看,我认为变量名没有_永远_过去对空格敏感。

我认为,引入这种歧义比进行重大更改和禁止在变量名称中使用?更成问题。 通过重大更改,我们可以轻松构建 PSSA 规则以提醒作者该语法不再被允许。 然而,反过来就不是那么容易了,而且与破坏性变化不同,它不一定会出错; 它很容易导致用户不小心悄悄地引用了错误的变量,而不知道那是他们在做什么。

@PowerShell/powershell-committee 讨论了这个,我们分析了 PowerShell 脚本语料库,并且有相当多的以问号结尾的变量名的用法。 因此,我们不能打破那些围绕 PowerShell 变量名的大括号,这是一项现有的语言功能,可将变量名与其他字符(例如在字符串中)区分开来。

我们已经分析了 PowerShell 脚本语料库,并且有很多以问号结尾的变量名的用法

谢谢,@SteveL-MSFT - 你能分享一下这个分析的结果吗?

是的,我很想看到其中的一些内容,因为我知道一些社区成员做了他们自己的分析,但没有得到任何接近这种结果的结果。

我也想看看使用的结果和方法。 昨晚我使用 AST 对PowerShell Corpus进行了自己的分析,这在我的机器上花了将近 6 个小时才能完成。 由于 Windows Defender 隔离文件,有少量文件无法解析。

我在 8 个以问号结尾的文件中找到了 11 个唯一变量名(不包括 $?,它出现了 8846 次)。 在 408,423 个文件中共有 1,895,983 个唯一变量。 这意味着语料库中所有唯一的 PowerShell 变量中有 0.0006% 的变量名称以问号结尾。

这是 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

正如他们在数学课上所说的,“请展示你的作品”😄

出色的侦探, @ThomasNieto - 谢谢。

我认为您可能会消除更多情况,因为${...}表单将继续适用于提议的更改 - 例如,我们只需要担心$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% ......(开玩笑)。

所以委员会必须使用不同的 - 私人? - 语料库。

所以,除了决定手头的问题之外,还有一个问题:公开可用的语料库需要更新吗? 是否正式维护(当前日期为 2017 年 7 月 26 日)?

@SteveL-MSFT,你能澄清一下吗?

@mklement0我四舍五入所以如果你想感觉好一点,它实际上是 0.00058% 😄

https://github.com/PowerShell/PowerShell-RFC/pull/223#discussion_r318340339 中, @adityapatwardhan使用我使用的相同公共语料库进行了分析,得出 62% 的变量以问号结尾。 我没有看到关于他如何得出这个百分比的回应。 PS 委员会是否使用该分析来确定此请求?

查看链接的评论,我认为@adityapatwardhan在说_在名称中包含?所有变量中_62% 的名称的 _last_ 字符为?

相比之下,您对(无花括号)结尾?变量(占变量使用总量的百分比)的实际流行率的分析似乎更相关。

在这一点上,听起来委员会只是在说“我们不想”,如果是这样的话,如果结果是笨重和未使用的,为什么还要有这个功能呢?

@ThomasNieto同样,我围绕这些问题在某处进行了分析,并得出了与您相似的结果。

@mburszley感谢您的分析。 我想用另一种方法(ast vs regex)来确认你去年得到的结果。

我听到你了, @mburszley ,但是:

委员会只是说“我们不想”

如果这真的是一个 _fiat_ 决定 - 一个不是基于对现实世界使用情况的分析 - 这对整个社区来说不是好兆头,特别是考虑到_陈述的_原因是这种分析的基础。

因此,我们应该让委员会有机会展示该决定所基于的现实世界的使用分析——或者认识到分析不够充分并重新考虑该决定。

我在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+ 时。

我希望它可以解决大多数向后兼容性问题。

@托马斯尼托

@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_ 的一部分,还是 _"?" 是名字-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 逻辑以提供相同的诊断。 但是随后您在各处都增加了复杂性(或者减少了您的测试面)以服务于某种装饰性场景。

更糟糕的是,即使 PSScriptAnalyzer 支持,所有其他依赖 AST 的工具也无法承受这种复杂性。 大多数构建 AST 目标工具的人不会针对 PowerShell 的各个次要版本进行编译以获得完美的正确性。 他们可能甚至不会知道这么小的突破性更改,尤其是因为它在任何版本中都不是错误; 这是破坏改变的最阴险的形式,一种行为悄悄地变成另一种行为。

这基本上就是让解析器/标记器级别的更改变得更加危险的原因。 甚至 PSScriptAnalyzer 也必须向后弯腰才能正确地为它们服务,这意味着它们必须权衡这些繁重的变化。

@rjmholt虽然我认为我们都可以理解这样的改变_原则上_有多棘手,但在这种特殊情况下_实践_并不重要,我认为甚至不需要 PSSA 规则:

  • @ThomasNieto的分析表明——如果我们相信语料库代表所有 PowerShell 代码——几乎没有人使用名称以? _ 结尾的变量。

  • 如果我猜的话,大多数人甚至不会_认为_使用这样的变量名,因为他们不希望它们起作用(包括我在内),甚至可能没有注意到自动$?变量构成(其名称在 POSIX 兼容的 shell 中同样是一个例外,例如bash )。

    • 事实上,仔细观察@ThomasNieto的分析会发现,在列出的8文件中:

      • 5只包含_误报_,即在_strings_ 内部使用的变量,例如"Are you sure you want to kill $vParam?" - 事实上,这些使用是_broken_,并表明脚本作者确实_不_期望?变量名的一部分。

      • 这些文件中的2不再公开可用。

      • 其中只有 _1_ 个真正使用了?结尾的$key1? ),作为旁白,因此 _not_ 与成员访问运算符结合使用, . /cc @stevenayers。

基于上述,这让我觉得这是一本教科书桶 3:不太可能的灰色区域变化,因此是_可以接受的_ - 我认为这种变化的_好处_已经有说服力的争论。

_记录_行为的改变(有理由)就足够了。

当然:

  1. 这是基于 (a) 分析正确和 (b) 公开可用的语料库具有代表性。

  2. 这与委员会声称“有相当多的以问号结尾的变量名的用法”的说法完全不一致(当然,如果这些变量名没有包含在{...} ,这只会是一个问题

继续假设 1. 是真的(我们没有听到任何支持 2. 的消息),我们有两个选择:

  • 撕掉创可贴,然后简单地禁止?在变量名中,除非包含在{...} (明显的例外$? ) - 这将打破消失的小目前依赖于此的脚本比例。

    • 也就是说,像$key1?这样的东西既不是.也不是另一个? ( $key1??'else' ) 也不是三元表达式 ( $key1?1:0 )会导致_解析器错误_。

      • 正如空合并和三元运算符示例所示,它们也将从这一变化中受益 - 目前,您需要一个 _space_ - $key1 ?1:0 / $key1 ??'else' - 或{...} - ${key1}?1:0 / ${key1}??'else'

    • 在 _expandable strings_ 中, ?将不再被视为(非{...}封闭)变量名的一部分,因此我们实际上会_修复_错误地使用诸如"Are you sure you want to kill $vParam?"字符串的现有脚本
  • 如果我们真的觉得我们需要避免打破现有脚本的极小部分,我们_可以_考虑@ExE-Boss 提出的逻辑,但我认为我们不想引入这种概念复杂性——更不用说实现复杂性(我真的不能说话)。

@SteveL-MSFT

感谢您在 9 月社区电话会议 (https://aka.ms/PSCommunityCall;截至撰写本文时,9 月电话会议的录音尚未发布在此处,但可通过 https://aka.ms/JoinPSCall 访问,直到下一次通话),从 46:00 开始,基于@ThomasNietohttps://github.com/PowerShell/PowerShell-RFC/issues/260 上先前征求讨论主题中的建议:

(我希望提供指向录音相关部分的直接链接,但这必须等到它发布到 YouTube 并链接到 https://aka.ms/PSCommunityCall。)

根据您在录音中的评论:

  • 鉴于您表示愿意重新访问此问题,请重新打开此问题并将其重新标记为Review - Committee

  • 至于你的评论,将变量名括在花括号中是一个预先存在的特征,人们反对在这种情况下基于_美学_理由必须使用它们:

    • 我认为这里没有人_不_意识到{...} _can_ 有时_必须_ 用于消除变量名称的歧义。
    • 这也不是关于_美学_,主要是关于_没有得到预期的行为_以_安静地_失败的方式_,因为_不希望在这种情况下必须使用{...} _; 次要问题是打字不便。
GitHub
RFC(征求意见)文档,用于社区对 PowerShell 生态系统的设计更改和改进的反馈 - PowerShell/PowerShell-RFC
微软团队

我担心这个问题——现在似乎无限期地处于封闭状态——会被破解,所以我打开了#14025 作为提醒重新审视它,我邀请所有有兴趣的人在那里表示支持。

此页面是否有帮助?
0 / 5 - 0 等级