Powershell: 致电本地运营商

创建于 2020-06-30  ·  189评论  ·  资料来源: PowerShell/PowerShell

问题陈述

当前,在某些情况下,剪切和粘贴本机命令行无法在PowerShell中按预期运行。 这可能是由于要传递给本机命令的引号解析不正确,或者是由于使用了PowerShell语法,而不是将其解释为PowerShell。 当与本机命令一起使用时,PowerShell具有--%特殊参数,将其余参数视为传递给本机命令的文字,但是存在几个问题:

  1. 这是无法发现的,用户需要提前了解此特殊参数
  2. |&&||具有优先权,因此: wsl --% ls | less将执行wsl ls并将结果通过管道传递给less在PowerShell中运行,而不是在wsl中运行less
  3. 如果剪切并粘贴命令行,则需要编辑该行以在开始处插入--%
  4. 在Unix系统上,-%后的args逐字传递而没有globing,而Unix上的本机命令希望shell执行globbing

拟议的技术实施细节

建议引入一个新的--% (Call Native)运算符。

此运算符之后的所有文本内容都将调用操作系统的“默认外壳”以执行。 在Windows上,它将是cmd.exe;在基于Unix的系统上,它将是/ bin / sh。 这解决了基于Unix的系统上的问题,并且还允许在Windows上扩展%variable% 。 与--%开关不同,这也意味着|&&||被视为本机命令行的一部分。

这意味着这两个在功能上是相同的:

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

其中$foo$PWD由WSL中的shell评估。 请注意,在第一个示例中,您将必须知道转义&&使其在WSL中而不是PowerShell中执行。

要将执行结果通过管道传递回PowerShell,要求用户首先将结果存储到变量中:

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

请注意,与当前的&调用运算符不同,您不能使用任何PowerShell语法,因此:

--% $commandline

不会首先通过PowerShell将$commandline解析为变量,而是将$commandline传递给默认外壳程序以处理未解决的问题。

剪切和粘贴问题可以通过在键入--%之后简单地粘贴来解决。

上面的wsl示例如下所示:

--% wsl ls | less

目的是使整个代码行在WSL Linux实例中执行。

可发现性

已经熟悉--%作为切换方式的用户可能会很容易地过渡到使用这个有意义的新运算符。 对于新用户, --%是唯一的,因此搜索引擎可以轻松找到与PowerShell相关的信息。

替代注意事项

&!&n作为标记,但由于&!在某些语言中是有效的运算符而被推迟了,这使得在网络上搜索文档更加困难。 同样令人担忧的是,类似于&呼叫运算符的视觉效果是否会使用户感到困惑。

使用此新运算符时,存在关于支持行继续的问题。 我建议我们一开始不支持它。

提出了cmdlet解决方案而不是运算符解决方案。 我们认为这不能解决“剪切和粘贴”问题,因为您现在需要知道将管道放在单引号中和/或转义特殊字符。 我们确实相信Invoke-NativeCommand的cmdlet(名词尚待确定)作为附加选项很有用,而不是代替对运算符的需要。

相关问题

这也应该解决以下问题:

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

Committee-Reviewed Issue-Discussion Issue-Enhancement

最有用的评论

我刚刚发布了一个模块NativeInstall-Module Native -Scope CurrentUser ),我邀请大家尝试一下,下面将使用其命令; 引用的数字用例来自上面@rkeithhill的评论。

如果我们想清除概念上的迷雾,我认为我们需要以不同的方式来构造用例。
_它们都不需要解析_中的任何更改。

用例[本地shell调用]:

您要执行为平台本机shell_编写的_individual command_(用例1)或整个_multi-command命令行_(用例2):(此问题):

  • 由于您使用的是不同的Shell语法,因此您要将命令(行)作为单个字符串_传递给目标Shell,以便_it_进行解析; PowerShell的字符串插值提供了将PowerShell值嵌入到传递的字符串中的能力(用例2a)。
  • insInvoke-NativeShell涵盖了这些用例。
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

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

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

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

这里的字符串语法不是最方便的方法(因此建议实现内联变体-参见#13204),但是您不必_have_就可以使用它。 对于逐字命令,您可以使用'...' ,它仅需要_doubling_嵌入式' (如果存在)。

另外,这是“ StackOverflow运算符”

如果将以下PSReadLine密钥处理程序放置在$PROFILE文件中,则可以使用Alt-v使用此处的逐字字符串为对ins的调用提供支持当前剪贴板文本粘贴到其中。Enter提交呼叫。

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

用例[直接可执行调用]:

您想使用_PowerShell_的语法和_individual arguments_(用例1a和3)来调用_single external execute_。

  • 这是任何shell的核心任务,因此,鲁棒地工作至关重要。

    • 您需要能够依靠PowerShell来传递_its_解析_as-is_到目标可执行文件的所有参数。 目前情况并非如此-请参阅#1995
  • 它要求您了解_PowerShell的_语法和_its_参数模式元字符。

    • 您需要注意, `用作转义字符并用于行继续。
    • 您需要注意,PowerShell还有其他元字符,需要逐字引用/转义; 这些是(请注意, @仅作为参数的第一个字符有问题。):

      • 对于类似POSIX的外壳(例如Bash): @ { } ` (和$ ,如果您想阻止PowerShell进行前期扩展)
      • cmd.exe( ) @ { } # `
      • 个别`转义此类字符。 就足够了(例如printf %s `@list.txt )。

虽然我们在等待1995年#是固定的,功能ie (对于nvoke(外部)电子xecutable)填补了这个空白,只是在前面加上直接调用; 例如,而不是以下调用:

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

您将使用以下内容:

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

Native模块带有类似于echoArgs.exedbeaDebug-ExecutableArguments )命令; Windows上的示例输出:

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

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

Command line (helper executable omitted):

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

通过使用-UseIe (( -ie )开关,您可以告诉dbea通过ie传递参数,这表明它可以解决问题:

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

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

Command line (helper executable omitted):

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

注意:固定#1995后,不再需要ie ; 为了向前兼容, ie旨在检测并自动推迟进行修复; 也就是说,修复程序到位后, ie将停止应用其解决方法,并且实际上将像直接/ &调用一样起作用。

用例[直接恶意Windows可执行调用]:

有两个主要问题,仅在_Windows上出现:

  • 您正在调用的“流氓”可执行文件的引用要求与最广泛使用的约定不同; 例如, msiexec.exemsdeploy.exe要求精确地用prop="<value with spaces>"报价-_value_部分的报价-即使"prop=<value with spaces>" -整个报价参数-应该_等效(后者是PowerShell合理地在后台执行的操作)。

  • 您正在使用_an参数调用_a批处理文件_,该参数不包含空格,但包含cmd.exe元字符_,例如&^| ; 例如:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell-合理地-传递了http://example.org/a&b _unquoted_(因为没有嵌入的空格),但这会中断批处理文件的调用,因为cmd.exe -不合理地-使传递给批处理文件的参数服从于同样的解析规则,适用cmd.exe而不是接受它们作为文字。

    • 注意:虽然使用批处理文件来_direct_实施功能可能正在减弱,但是将它们用作_CLI入口点_仍然很常见,例如Azure的az CLI,它已作为批处理文件az.cmd

注意: ie自动为批处理文件和msiexec.exemsdeploy.exe处理这些情况,因此对于_most_调用,不需要额外的工作; 但是,不可能预料到所有“恶意”可执行文件。

有两种方法可以解决此问题:

  • 给定cmd.exe ,将自己的解释应用于命令行中的参数后,_does_保留指定的引号,您可以在Windows上委派给ins调用:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • 如果使用_expandable_字符串,这又使您能够将PowerShell值嵌入命令字符串中。

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

  • 或者,使用当前的--%实现,但要注意其局限性:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • 给定--%的实现方式,嵌入PowerShell值的唯一方法是-笨拙地-定义_aux。 环境变量_并使用%...%语法引用它:

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


错误处理

待处理的https://github.com/PowerShell/PowerShell-RFC/pull/88将带来与外部(本机)可执行文件更好的错误处理集成。

同时, Native模块附带了两个功能,可以临时加入,将非零退出代码视为脚本终止错误

  • ins支持-e / -ErrorOnFailure开关,如果在调用本机外壳程序后$LASTEXITCODE不为零,则会引发错误。

  • ieeie的包装函数,如果在调用外部可执行文件后$LASTEXITCODE非零,则类似地引发错误。

模块附带的命令有很多细微之处。
相当广泛的基于注释的帮助有望涵盖它们。

所有189条评论

我喜欢这个想法,并发现它很有用,但认为如果可以进行单个字符串扩展,它会更加有用(我在考虑DSL的情况下尤其如此)。 因此,如果我在PS中构建了cmd.exe兼容字符串,则可以采用逐字方式将字符串转换为cmd.exe。 也许

&n -command $string

那也可能让我指定我的解释器:

&n -shell /bin/sh -command $string
要么
&n -shell cmd.exe -command $string

在名称/运算符方面,是“&!” 正在使用? 类似于shbang(“#!”)。

$!是一个很好的建议!

可以通过简单地将外壳指定为命令来指定外壳:

&! /bin/sh -c blah

$var扩展产生了一个问题,其中$可能需要原义才能传递给本机命令/ shell。 试图避免的问题之一是人们需要弄清楚如何正确地避开一切。 如果需要变量扩展,则可以始终执行以下操作:

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

但是,在要将PowerShell与本机命令混合使用的情况下,最好使用当前语法,并且只知道需要适当转义的内容,因为我认为这是高级用法。

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

我简直不敢相信您只是建议iex。 :-)

@essentialexch有几次是合适的:)

请注意,在该示例中,期望用户在执行之前正确验证了$mycommand的内容!

绝对需要出色的论文写作和针对您所描述问题的解决方案。
我本人不了解--%并且不记得曾经不幸看到过它。 因此,建议的&n运算符可能会遇到类似的可发现性问题,并且我认为将符号与字符组合起来并不自然(这让我想起了C printf语句中的%f)。 既然这是一项重大更改,是否有理由不能成为例如&&
也许使用括号(或双括号?)之类的东西标记要执行的区域会更直观。 示例: & [ wsl ls ] | less

我同意&!是处理来自其他shell的人的更直观的方法,但是在超级双线性服务器中,我通常通过构建参数数组然后将其放在命令中来运行本机命令。

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

所以这是在有条件的情况下最好的方法

@bergmeister我最初认为&&在视觉上类似于今天很多人都知道的& 。 但是, &&作为管道链运营商可能会引起混淆。 我目前喜欢&!建议。

@JustinGrote ,没有理由不继续使用它。 &!语法确实适用于您只想剪切和粘贴或有一些与PowerShell语法冲突的args,而又不想或不知道如何正确转义的情况

噢,老兄,我记得十年前曾与布鲁斯和杰森进行过讨论。 我的直觉是在这里发明另一个操作员似乎是不必要的。 我知道有些人还没有听说过--%但我敢打赌,比您想的还要多。 有什么问题:

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

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

为什么没有人建议这个? 从逻辑上讲,它与在其他地方使用--%表示“此后的所有内容无需经过特殊的powershell处理即可通过”。

@很好地执行了该命令,因为该命令必须以要运行的命令
image

这有点像预期的那样:
function Invoke-LiteralCommand ($command) {Invoke-Expression "& $command --% $args"}

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

@JustinGrote是的,我知道它现在不起作用:)我建议是&n&!或其他价格。 这对我来说更有意义: &是调用, --%是禁止powershell解析的; 它们在一起,比添加新内容更连贯,更容易被发现。 我不喜欢拥有“呼叫”和“呼叫本地”操作员的想法。

我扩展了示例以显示差异。

@oising对于新的运算符,我会很酷,尽管对于“知道bash的新Powershell用户”来说,这是很多

@oising对于新的运算符,我会很酷,尽管对于“知道bash的新Powershell用户”来说,这是很多

一点也不。 适用于不了解PowerShell / cmd.exe / bash之间复杂的报价规则的PowerShell用户。 正如问题标题所说的“调用本地”,用于调用本地可执行文件。

@oising对于新的运算符,我会很酷,尽管对于“知道bash的新Powershell用户”来说,这是很多

一点也不。 适用于不了解PowerShell / cmd.exe / bash之间复杂的报价规则的PowerShell用户。 正如问题标题所说的“调用本地”,用于调用本地可执行文件。

是的,对于那些根本不想考虑它们的人。

是的,我在@oising上。 这个概念存在,但还远远不够。 如果我们要实现全新的东西,最好不要使用/删除旧的语法。

我觉得这些想法经常被表达出来,而他们的实际目标人群却没有得到足够的重视。 如果用户已经在努力寻找现有方法,找到找到它们时所缺少的现有方法,则要求a)改进文档,b)改善现有操作员的实际功能。

取而代之的是,我们得到了一个奇怪的选项c),该选项应该以某种方式解决过去的问题,同时引入_another_运算符,该运算符依赖于文档,而该文档已经无法被用户自然地找到。

我同意应该使用错误消息和/或建议系统来帮助介绍这些概念。 我不同意我们需要第三份吗? 第四? 第五? (实际上我已经数不清了,有人在这里帮助我)调用命令的方式。 现有的方法是不够的-这意味着我们应该改进它们,而不是像蜘蛛网那样将它们抛在后面,以使用户在开始研究它们时进一步感到困惑。

当人们意识到有5种做某事的方式时,我往往会想到的第一个问题是“为什么有2-3种从字面上做同样的事情但又不能很好地工作的方式”,我的回答是: -PS团队过分专注于确保在尝试添加/改进功能时,过去十年++的所有内容仍然有效。 在这里,我们再次看到它,现有的但尚不足的实现需要修订和改进,建议的解决方案是通过添加另一个可能不完整的实现来进一步混淆资源池。

我们应该完成我们开始的工作,而不是从头再开始另一个实施,IMO。

我相信,如果要使用&来使用多个唯一用例,我们应该开始考虑将其视为动词,或着眼于整个语言的标准化。

我想要的最后一件事是对&打算做什么感到困惑。 整体而言。

我会考虑3种情况:

  1. 全面扩展-当前的PowerShell行为。
  2. 完整文字-在OP中提出。
  3. 部分扩展- wsl ls $path仍然有效

我想知道@ mklement0在哪里评论? :-)

在开始阅读此主题时,我想; 好点子! 但是看完评论后,我开始思考。 我总是发现&命令“不符合PS标准”和使我想起“ PERL天”(ugh)的东西。引入另一个非标准PS命令(不是名词-动词)将无济于事。 PS。

我也不知道-%参数。 我使用与@JustinGrote相同的原理作为解决方案。

将命令剪切/粘贴到PS shell中从来不是我的主要任务。
我们用PowerShell取代那些本地命令会更好吗?
制作完全替换cmd.exe的路径...

我投票赞成探索改进现有命令。 并且-实际上-改善了有关-%和&用法的一些文档

@iSazonov用例@ SteveL-MSFT提出的是“剪切粘贴”方案。 我认为部分扩展会使事情变得更加困难(在AST方面)

立即出现“#!” 接着 ”!#”。 喜欢“&!”。

我们可能会考虑将令牌加速器添加到令牌生成器中的函数调用。 这将允许使用以下内容。

Iex -l
或Invoke-Expression -literal在传递之前停止解析。

我觉得在解析级别添加更多唯一令牌会增加进入障碍,而此功能的可发现性也会下降。

例如,“获取帮助”不会显示任何内容。 我们必须在about_Operators中寻找它。

但是,有很多没有记录的call_operator特殊情况。
&modulename {命令}
允许您将作用域划分为一个模块。 而且我敢肯定还有更多。 但是它的可发现性很低,以致于在本机文档中很难找到它。 而且社区仅通过JSnover以及他给我们展示的一些有趣的话题才知道。

我相信,无论决定什么,都需要从新用户的角度充分考虑这一“功能”的可发现性,并牢记新用户将尝试在Powershell 5上使用它,而不是不了解pwsh核心的新功能。发现性太低。

我希望我们更改现有的行为,但是在PowerShell中更改这样的基本API只会破坏很多事情。 我们可能会考虑迁移配置。

在PS 6正式发布之前,曾经有可能更直接地破坏事物,但是老实说,即使那样,它也将对向后兼容性构成严重威胁(与Python的print()函数不同)。

就将参数传递给子流程而言,我认为同步调用和Start-Process调用都已经存储了讨论大修的问题,而我的感觉是,两者都需要立即进行投资以保持一致性。 特别是Start-Process需要更新其对传递参数数组的支持。

对于通过cmdline arg传递进行的同步调用,我认为可以传递四种参数:

  • 当前行为,这将成为旧行为,在Windows上受CommandLineToArgvW约束,并具有意外的结果
  • 默认行为,应按其表达式值传递参数,但应用通常的裸字令牌规则,以便像>|命令分开。 在这种模式下,子进程从表达式中获取的值应该是Write-Host $val
  • 逐字行为,其中所有标记都被解释为裸字字符串,直到看到转义标记为止。 这就是--%今天应该做的,但理想情况下,只有一个结束令牌可以嵌入在同一行中,例如--% ... %--
  • Bash readline行为,其中应用了替代的,面向sh的转义逻辑,从而实现了更简单的兼容性

我认为应该通过将第二种行为作为实验性功能并进行互换来逐步淘汰第一种行为。

第二和第三种行为可能有自己的信号,例如&!&#+% ... -%东西。 但是对于逐字模式,我认为重要的方面是简化转义令牌,以便逐字采用更多令牌。 类似于heredoc。

如果请求仅用于解决复制粘贴的情况,例如“此操作符之后的任何文本内容将调用操作系统的“默认外壳”以执行”。 为什么不通过shell明确地说呢?
```地狱
PS> shell wsl ls | 减
PS>(shell wsl ls * .txt)| 选择字符串你好

它比Forth / APL风格的隐式运算符更容易发现和可读。

我绝对不知道这将如何解决#1995:请参阅https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653174261

另外,WSL的问题是wsl.exe的行为问题(请参阅https://github.com/PowerShell/PowerShell/issues/12975#issuecomment-650353021),而不是powershell的问题。
(好吧,powershell存在问题,但这是1995年)

哦,这几乎不是https://github.com/PowerShell/PowerShell/issues/12975的副本吗? 因为基本上可以重复,所以我在这里提到的是:

@bitcrazed
问题在于,缺乏将要传递给varbatim的命令行的一部分划定为接收命令/脚本的能力,这总是使用户不知所措。
“通过varbatim传递的命令行部分”是什么意思? 您的意思是1.将一些字符序列逐字传递给被调用的可执行文件,以便被调用的可执行文件在其参数数组的一个元素中具有此字符序列(例如,这些字符可在[main]的`argv [1]`中使用](https://docs.microsoft.com/zh-cn/cpp/cpp/main-function-command-line-args?view=vs-2019#command-line-arguments)或您的意思是2.插入将某些字符逐字逐字放入“ CreateProcess”的[`lpCommandLine`参数中](https://docs.microsoft.com/zh-cn/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw#parameters) -如果您的意思是(1.),那么它实际上已经可以使用:
PS /home/User> /bin/echo 'cd / && ls . | cowsay'
cd / && ls . | cowsay
PS /home/User>
(除了在https://github.com/PowerShell/PowerShell/issues/1995中讨论的嵌入引号问题之外),有人可能会争辩说,在此处添加单行字符串会改善某些用例,但我认为,这不是这个问题的重点。 因为这已经或多或少地起作用了,所以我认为您的意思是(2.)---如果您的意思是(2.),那么让我以一种戏剧性的方式发表我的看法:请不要添加特殊语法。 基本上,这是`-%`试图做的,这也永远不会实现。 我为什么如此强烈反对呢? 1.这是仅Windows的问题,因此添加语法将意味着Windows上的powershell与Linux上的语法不同。 (或者该语法将受支持,但完全没有意义,因为目前在-%情况下是这样)。2.如果Microsoft在Windows上发布的Windows的主命令行外壳程序添加了一流的功能(通过特殊语法而不是via)一个cmdlet)来调用不遵循典型的[命令行解析规则]的可执行文件(https://docs.microsoft.com/zh-cn/cpp/cpp/main-function-command-line-args?view=vs -2019#parsing-c-command-line-arguments)(如果该工具遵循典型规则,则您不需要(2.),通常可以更好地使用(1.)),这会鼓励命令行作者工具,不遵循这些规则,只会加剧[“ Windows命令行混乱”](https://stackoverflow.com/a/4094897/2770331)。 人们遵循典型规则的次数越少,以编程方式调用外部可执行文件或编写跨平台代码的难度就越大,因此,我绝对认为,应该鼓励程序作者遵循这些典型规则。 3.我强烈怀疑这是一个常见用例>这不仅仅是影响WSL的问题:它还影响Windows Terminal的wt命令行调用和许多其他工具。 您能否添加一些出现此类问题的示例? 因为在使用WSL的情况下,我想说WSL的命令行解析很简单[broken](https://github.com/Microsoft/WSL/issues/1746)(问题是关于`bash.exe`的,但情况是在默认情况下,默认情况下使用`wsl.exe`效果更好-我会考虑不遵循典型的[命令行解析规则](https://docs.microsoft.com/zh-cn/ cpp / cpp / main-function-command-line-args?view = vs-2019#parsing-c-command-line-arguments)损坏,但WSL的默认行为是恕我直言,甚至没有正确记录...我说“ “ wsl.exe”的行为已损坏-在编写此响应时,我注意到,在使用`-e`时,`wsl.exe`实际上似乎表现出预期的效果:
PS C:\> wsl -e bash -c 'cd / && ls . | cowsay'
 _______________________________________
/ acct bin boot cache cygdrive data dev \
| etc home init lib lib64 lost+found    |
| media mnt opt proc root run sbin snap |
\ srv sys tmp usr var                   /
 ---------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
PS C:\>
因此,此显式用例唯一缺少的是IMO作为wsl.exe的参数来调用默认外壳程序,该外壳程序具有与其他任何普通exe一样经过解析的命令行参数。

唯一与“使用varbatim传递给接收命令/脚本的命令行部分”有关的,用此运算符可以改进的是上面的含义(2.),如上所述,我认为朝这个方向发展是坏。

如果您要做的只是快捷方式,请复制并粘贴现有命令行,为什么不添加一个cmdlet(例如Invoke-Shell ,在Linux上调用bash -c在Linux上调用cmd /c视窗?
那你就可以做

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

如果必须为一行,则添加“单行在这里”字符串语法,但是请不要实现一个特殊的运算符,该运算符无法正确找到,只会使一切复杂化。

而且请不要为此放弃1995年!

使用“印记”一词时获得+10分。

&!shell ,“ shell”是一个常见的英语单词,因此人们可能已有一个名为shell的命令。 (实际上,我同时拥有一个名为“ shell”的函数和一个名为“ n”的函数(因此,我不喜欢&n )。)

@oising& --%很好,除了所提议功能的明确目的之一是意味着|变成了传递给本地shell的东西。 而不会使您重新进行PowerShell解析。 如果我们做出了管道的优先级不同的& --%foo $blah --% more args ,我会发现,令人眼花缭乱......如果你没有& $foo --% more args | blah ? 因此,在我看来,建议的&!足够不同,因此可以保证拥有不同的运算符。 (换句话说:建议的功能是相关的,但不同于&--%组合在一起。)

此功能不仅仅适用于“知道bash的新PowerShell用户”和“不知道所有复杂的引用/转义规则的用户”。 它也适用于最专业的用户,他们只想从StackOverflow复制/粘贴命令,而不必进行任何操作。 这在我身上相当普遍-我正在研究的产品上有很多有关操作方法的文档,而francua franca是cmd.exe命令-所以我必须去解决%VARIABLE_REFERENCES%等问题。 。; 键入&!然后粘贴会好得多。 实际上,如果实现了这一点,我可能会将其称为“ StackOverflow运算符”。 :D

@ vexx32 :这不是“一环来统治所有人”的呼叫运营商; 只是工具箱中用于解决常见问题的另一个工具。 我认为我们不应摆脱现有的工具来为此腾出空间。 他们做不同的事情。 我了解,当工具箱配有锤子,螺丝刀,钻头,冲击钻和射钉枪时,新用户可能会感到不知所措……尽管他们都是“通过一个或多个其他东西戳戳东西”的事实事物”,对于专业人士来说是无价的; 全部用于不同(尽管相关)的情况。

@TSlivede :我同意#1995是分开的。 但是我不同意“这是仅Windows的问题”:从StackOverflow复制和粘贴特定于平台的命令同样适用于任何OS。 (实际上,这对于像我这样的很多人都非常有用,他们在一个平台上比另一个平台更强大-我精通Windows技术,因此在Linux上,我倾向于从SO进行大量复制和粘贴。)

无论是通过新的运算符&!还是某些Invoke-Shell命令,我都不像其他人那样强烈...我同情人们抱怨运算符难于搜索等,但是我真的很喜欢&!的简洁性。

@jazzdelightsme并不是“工具箱中的另一个工具”,而是“另一个令人难以置信的螺丝刀专用螺丝刀附件,目前已经有3-5个,现在已经可以了,并且可以改进”。

@jazzdelightsme “仅Windows”部分可能与我最初发布该声明的问题有关-是的,这与该问题并不完全相同。

如果没有放弃#1995并且不能通过特殊的语法而是通过命令行开关(或者对于单行的新语法,这里的字符串可以在所有情况下不仅适用于此特殊情况,那么在这里运行)也无法解决此处要求的功能,那么我实际上是赞成添加此功能。 (或者不是“赞成”,而是“这是我完全可以接受的”)

另一个想法:如果这实际上是关于复制和粘贴本机外壳的完整命令的问题,则PsReadLine可以添加一项功能,以将命令从bash或cmd样式大致转换为powershell。 转换可以由特殊的组合键触发,也可以在尝试执行命令时发生解析错误时自动触发。

这将具有教用户使用powershell语法的额外优势。

关于可发现性:每当粘贴语法错误的内容(在翻译后才有效)时,PsReadLine总是可以在短时间内显示一条消息,指出可以使用特定的组合键。

但是我可以理解这是否有点复杂。

@jazzdelightsme您是否与@joeyaiello交谈? 我以为只有他称呼它为StackOverflow operator ,这让我畏缩了,尽管那是真的

@ SteveL-MSFT不,我没有和任何人谈论这个; 我独立到达“ StackOverflow运算符”。 xD

@jazzdelightsme

...除了所提议功能的明确目的之一是指| 变成传递给本机外壳的东西; 而不会使您重新进行PowerShell解析。

我不确定你是否理解我的建议; 你说的是_precisely_我的意思。 让我更详细地讲一些具有战略意义的提示:D

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

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

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

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

# or

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

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

我在这里想念什么? 如果管道出现在--%那只是另一个哑字符:一个参数。 运算符优先级没有任何问题。 如果包含在嵌套管道或子表达式中,请再次开始对powershell进行解析。

如果您这样做了&$ foo-args增加了%| 等等?

好吧,这将尝试在$foo执行参数并传递"more""args""|""blah"视为在$foo想要。 这些规则对我来说似乎很简单,但是诚然,我使用Powershell / monad已有很长时间了。 这不是对&的重大突破,因为& $foo --% ... --%像其他任何参数一样对待

有什么想法吗?

我对上面的内容进行了多次编辑,以涵盖部分行评估,多行评估和--%变量位置。 所有这些都不会引入新的信号,运算符或奇怪的语法。 我认为涵盖了这一点。 唯一的事情是:解析器可以处理吗? 您如何看待@lzybkr @JamesWTruher @BrucePay

听到,听到,@ TSlivede。

原谅我直率,但是我们一直在谈论这些问题很久了:

该问题提出的内容构成了神奇的思维:
它与命运不佳,本质上存在问题的“停止解析符号” --% ,在当前形式下,它在Windows_上使用受限(请参见https://github.com/MicrosoftDocs/PowerShell- Docs / issues / 6149),并且在类似Unix的平台上几乎没用(请参见https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4963)。
--%永远都不应实施,并且此提案也不应按提出的那样实施。

魔术思维是两个概念在概念上的混淆:

  • (a)“我想将以空格分隔的令牌传递给某个外部程序,允许任何裸字,而不必担心诸如&| shell元字符。” -参见https://github.com/PowerShell/PowerShell/issues/12975#issuecomment -646276628

    • 当然,使用这样的命令作为管道的一部分_或使用管道链运算符_( &&|| ),并且在同一行上放置其他命令,这种功能是不对的。在; -并且同样重要的是,能够在命令行中使用任何_PowerShell变量和expressions_。
  • (b)“我希望我的命令行以我习惯的方式从通常使用的shell(或最初为其编写命令行的shell)中进行处理”,当然,_is_会受到不加引号的字符使用例如&|被解释为元字符,因此具有_syntactic_的含义。

    • cmd/bin/sh (或任何类似POSIX的外壳,如bash )都不会以传递ls的方式接受wsl ls | less ls|less _verbatim_到wsl -它们都将_unquoted_ |为_their_管道符号。

这些想法彼此矛盾,需要不同的解决方案-两者都不需要特殊的语法:

  • (a)的解决方案是_abandon_这个想法,而是使用_PowerShell自己的现有语法_来确保使用元字符_verbatim_-总是需要_quoting_或_escaping_ ,:

    • 要么:引用包含作为整体_的元字符的裸词(未括在引号中的令牌): wsl ls '|' less
    • 或: ` -分别在裸字中的转义元字符: wsl ls `| less
    • 或者:使用_array [variable] _传递参数_verbatim_: $wslArgs = 'ls', '|', 'less'; wsl $wslArgs
    • 当然,#1995表明,令人遗憾的是,当涉及到在调用外部程序时嵌入" _的参数时,这种方法一直且总是被打破的-_这需要解决_:请参见下文。
  • (b)的解决方案是实际_call我通常使用的shell_,并将命令行_作为单个string_传递。

    • 正如@TSlivede所建议的那样,一个Invoke-Shell cmdlet(您将命令行_作为单个字符串_传递给该cmdlet并调用_platform-native shell_是解决方案),并且我们已经有了逐字字符串的语法( '...'或此处的字符串变体@'<newline>...<newline>'@
    • 我的建议是在类似Unix的平台上将其称为Invoke-NativeShell ,并调用/bin/sh -而不是bash

      • 另外,我建议将ins定义为简洁的别名。

    • 这种方法可以解决--%和现有建议中的所有概念性问题:

      • 它使传递到本机shell的命令结束处非常明显(由于是_a单个字符串_),并且允许在常规PowerShell管道中使用Invoke-NativeShell / ins调用/ PowerShell重定向没有歧义。

      • 您可以选择使用_expandable_字符串( "...."或此处的字符串变体),以便在命令行中嵌入_PowerShell_变量值( --%无法做到)。


由于已经得出结论认为无法固定#1995的权力,以免破坏向后兼容性-毕竟,所有现有的解决方法都将失效,请参阅https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606-我们能做的最好的事情就是使用尽可能简洁的语法来支持选择加入正确的行为,以最大程度地减少因烦恼而无法获得应有的行为的痛苦。默认。

因此:基于@TSlivede的RFC建议的行为,仅应提供特殊语法,例如&!作为选择,以正确地将参数传递给外部程序以处理#1995https://github.com/PowerShell/PowerShell-RFC/pull/90。 如果以后的PowerShell版本被允许打破向后兼容性,则直接调用/带有&调用应该表现出这种行为。

从用户的角度来看,这意味着:我要做的就是满足_PowerShell的_语法要求-参见https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -640711192-我可以依靠PowerShell来牢固地将所得的逐字标记传递给目标程序-_这是任何Shell的核心任务,遗憾的是,PowerShell对于外部程序从未满意过_。

例如,下面的命令(现在可以按预期运行_not_可以正常工作(当直接调用或使用& -参见此文档问题)-应该可以运行:

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

那些在当前/早期PowerShell版本中寻求解决方法的人可以在https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -303345059中找到一个涵盖大多数用例的简单函数(在https://上讨论的限制) github.com/PowerShell/PowerShell/issues/1995#issuecomment-649560892),以及更详尽的版本,网址

PS :(不仅基于符号的)运算符的发现问题是一个单独的问题,很值得单独解决:解决方案是“增强Get-Help以允许运算符lookup_-参见#11339,这也链接到临时解决方案。

既然已经得出结论,不能固定#1995的权力,以免破坏向后兼容性-毕竟,所有现有的解决方法都将被破坏,请参阅#1995(评论)-我们最好的办法是支持选择加入使用尽可能简洁的语法来进行适当的行为,以最大程度地减少因烦恼而无法获得应始终为默认行为的痛苦。

@ mklement0我认为选择加入是https://github.com/PowerShell/PowerShell/issues/1995的一部分,但被拒绝了。 所以对我来说,这听起来像是一厢情愿的想法。 我错了吗?

@AndrewSav ,我们最初只讨论了通过配置或首选项变量进行的选择。

但是, @ joeyaiello在关闭问题时(在其中加了强调)在https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -653164987中说了以下内容:

我们在这里碰到的任何东西都不过是一个重大突破,我们应该由新的操作员来解决这个问题。

我希望有一个新的操作员可以选择加入,但希望没有问题(尽管很不幸,但这是向后兼容的代价)。

虽然该提议据说还解决了#1995,但鉴于它(主要)旨在适应_other_ shell的语法,因此它没有解决。 鉴于两个问题都值得独立解决,我的建议是:

  • 让我们介绍一个新的运算符,但仅使用它来修复#1995(通过https://github.com/PowerShell/PowerShell-RFC/pull/90),即修复PowerShell的_own_(非其他shell模仿)处理传递给外部程序的参数。

    • 然后,在调用外部程序时,新代码应始终使用&! (或我们决定采用的任何形式),而不是& (您可以将其视为对外部程序的弃用& ) 。
    • 调用_PowerShell_命令时, &!行为应像现在的&一样(从来没有问题),从而可以始终使用&!
  • 为了解决其他shell的命令行重用问题,让我们实现一个Invoke-NativeShell / ins cmdlet,该cmdlet调用适合平台的本机shell(在Windows上cmd /c ,在类似Unix的平台上/bin/sh -c ),并且命令行以_single string_形式传递。

    • 在最简单的情况下,在命令行中没有' ,将使用常规的单引号字符串; 例如:

      ins'ls | 猫-n'

    • '存在的情况下,它们可以是_doubled_; 例如:

      ins'echo”嗨| 猫-n'

    • 或者,如果根本不需要触摸命令行,则可以使用here字符串(如@TSlivede所示)-尽管此语法有点麻烦,但它应该始终有效:

      ins @'
      回声“嗨” | 猫-n
      '@

    • 此外,通过使用_expandable(内插)字符串( "..."@"<newline>...<newline>"@ ),您可以选择将_PowerShell_变量合并到传递给本机shell的字符串中( --%不支持):

      $ foo ='嗨'
      ins @“
      回声'$ foo'| 猫-n
      “ @

    • 注意:

      • Invoke-NativeShell cmdlet将是cmd /c / /bin/sh -c调用的相当薄的包装,您显然可以直接自己制作,但是这样做的好处是您不需要了解特定的可执行文件名称和命令行传递语法。
      • 作为潜在的有益副作用,您将能够使用-OutVariable类的常用参数,并可以考虑是否让-Error*参数作用于_stderr_(尽管出于对称目的, Invoke-Command然后也应该是这样,这将是一个巨大的变化)。

        • 由于类似POSIX的外壳程序支持-c _multiple_参数-第一个构成要执行的代码,随后的参数传递给该代码,以$0 (!)开头-例如bash -c 'echo $@' _ 1 2 3 -也必须支持可选的其他参数。

        • 由于调用了平台本机外壳程序,因此由于cmdsh之间的根本差异,此类调用是_not_ Portable的。这就是为什么修复PowerShell的_own_参数传递(#1995)如此重要的原因,因为它随后提供了一种可靠的,真正跨平台的方法来调用外部程序。


难题的最后一部分是_documentation_。

https://github.com/MicrosoftDocs/PowerShell-Docs/issues/5152是一项长期的建议,旨在编写有关调用外部程序(例如about_Native_Calls )的概念性帮助主题,该主题应放在一个位置,涵盖与调用外部程序有关的所有方面; 除了那里已经介绍的内容(由于其他元字符而导致的语法陷阱,缺少原始字节管道,不与PowerShell错误处理集成……)之外,还应该讨论当前的问题:

  • 嵌入的" (和_empty_参数)的传递参数如何(希望很快成为)在早期版本中被破解(目前在单独的提案中进行了讨论,https://github.com/MicrosoftDocs/PowerShell-Docs / issues / 2361),以及解决方法(手动\转义或使用#1995中的一个辅助函数,其中之一非常简洁,可以直接包含在主题中)。

  • 今后仅应使用&!来调用本机(外部)可执行文件,以确保正确传递参数。

  • 如何使用Invoke-NativeShell / ins照原样执行为平台本地shell_写的命令行。

有趣的是,今天的用户可以:

  1. 直接调用
  2. &运算子
  3. 开始过程

尚不清楚用户应该使用。

修复损坏的行为后,用户将具有以下功能:

  1. 直接调用
  2. &运算子
  3. 开始过程
  4. &! 算子
  5. 启动流程V2

Shell运行ping 127.0.0.1是否过多?

是的,这是我还要添加另一个运算符以及@iSazonov的主要问题。 已经很混乱了,这无济于事。 文档只是难题的一部分。 即使我们有不错的文档,也有五种不同的方式来做,而每种方式的行为却有所不同,这总会引起混乱。

如果我们完全在乎UX,我们应该选择增加更多案例来改进现有案例。

如果我们完全在乎UX,我们应该选择增加更多案例来改进现有案例。

您不能做到这一点,因为您必须向后兼容。 我觉得我们正在转圈。 https://github.com/PowerShell/PowerShell/issues/1995就是这个原因,它因此被关闭了。

@iSazonov

即使我们没有引入新的运算符,也需要解决现有的指南不足:

简要总结一下:

  • 不要使用Start-Process来调用_console_(终端)程序(除非-仅在Windows中可用-您要在新窗口中运行它们)。

  • 直接调用和通过&调用是_equivalent_; (不考虑脚本块的情况),仅由于_syntactic_原因才需要& ,只要命令名称或路径为_quoted_和/或包含_variable references_。


修复损坏的行为后,用户将具有以下功能:
直接调用

  • &运算子
  • 开始过程
  • &! 算子
  • 启动流程V2
  • 不,不会有Start-ProcessV2 ,只有_new参数_, -IndividualArguments -参见https://github.com/PowerShell/PowerShell/issues/5576#issuecomment -552124719

  • 是的,会有&! (或我们决定使用的任何符号),但实际上有效_取代&和直接调用_(至少对于_external program_而言,但您可以使用它) _all_命令形式)-应该易于沟通。

Shell运行ping 127.0.0.1是否过多?

ping 127.0.0.1将继续工作,但是如果您希望bash -c 'echo "one two"'工作,则必须使用
&! bash -c 'echo "one two"'

不幸的是,但是-考虑到直接调用/ &行为_无法修复-它使我感到是最佳解决方案。

幸运的是,这是一个容易记住的规则:_如果要将可预测的参数传递给外部程序,请使用&! _

选择加入的其他形式-配置(通过powershell.config.json )或首选项变量-问题更多:

  • 动态设置首选项变量的范围,使其很容易无意间将选择加入不适用于并非为此设计的遗留代码-参见https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -650262164

  • 配置将应用于整个会话,因此完全不可能退出。

  • 在这两种情况下,都需要将动作与特定的调用分开,并且查看给定的调用不会告诉您其行为-相比之下, &!毫无疑问会导致哪种行为。

对于最终用户而言,它看起来就像我们启动计算器并在其中找到5个用于进行加法操作的按钮一样糟糕。 😄

如果我们要对其进行很大的改进以准备好应对此类复杂性,那么这也许足以接受重大更改(用于直接调用和& ),从而简化行为并发布V8.0。

如果我们的愿望不是很强烈,则可以充分改善诊断( prog.exe param1 param2 -Whatif和Start-Process -WhatIf相同),以便显示testargs程序显示的内容以及有关如何操作的建议正确执行转义。

脚本开发人员有能力花时间测试和使用调试器。 但是在交互式会话中,任何用户都希望简单,简洁和清晰,这比一切都重要。

@iSazonov

那么也许这足以接受重大更改(用于直接调用和&),从而简化行为并发布V8.0

您当然有我的投票权-但是支持这项计划的权力吗? [_update_-参见#13129]
如果我们确实进行了如此大的更改,我们最终可以解决所有历史包--参见#6745

如果我们要这么多改进

有两个很好的理由:

  • 为了结束因参数传递失败而造成的数年痛苦,正如#1995所证明的那样,许多相关问题以及无数堆栈溢出问题。

  • 因为-如前所述,多次调用-用参数调用外部程序是任何shell_的_core委托。

从历史上看,在仅Windows的日子里,缺乏功能强大的外部CLI使得无法正确调用它们变得不那么麻烦,并且留在PowerShell围墙花园中的用户(仅调用PowerShell本机命令)不会感到痛苦。 但是时代肯定已经改变了:

我们生活在许多跨平台CLI的时代,这些CLI对开发,CI和部署任务至关重要。
如果PowerShell希望被认真对待作为外壳,则必须解决此问题。


即使发生了允许向后突破的兼容性v8.0(手指交叉),它的发布也可能需要很长时间,因此我们也需要一个临时解决方案。

尽管缺乏建议的对文档的增强功能,但我认为缺少足够的可用运行时解决方案不足以缓解这些麻烦。

我知道该运算符很丑陋,但是无论其形式如何,一个不间断的解决方案_necessitates_其他语法(需要更多输入)。

&!的另一种选择是不太丑陋,更易于键入并且不会“污染”该语言,是提供带有简洁名称的内置_function_,例如iep函数(nvoke-êxternal保持不变)掩盖所有问题,出在这里
(请注意,我们已经发布了包装其他功能(例如pause )或容纳cmd.exe用户(例如cd.. [sic])的功能。)

因此,要使bash -c 'echo "one two"'正常工作,您可以致电iep bash -c 'echo "one two"'
或者,使用更现实的情况:

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

再一次,我们最终谈论的是在服务中保留_向后兼容_以保持向后兼容性-这不可避免地对最终用户造成了麻烦。


改善诊断(prog.exe param1 param2 -Whatif和启动过程-WhatIf相同),以便显示testargs程序显示的内容以及有关如何正确执行转义的建议。

由于基于直接/ &的调用不是cmdlet调用,并且所有参数都假定为传递参数,因此将-WhatIf视为通用参数将构成重大更改(但是在实践中)。

如果您愿意接受这样的更改,我们也可以(也)支持-DoItRight开关(或类似于--%的基于符号的开关)作为选择加入固定的行为-这让我比iep -prefix / &!方法更糟。

(我想,不间断的诊断方法是增强Invoke-Command ,该功能目前不支持-WhatIf -但是,如果我们提供正确记录的正确选择加入行为,我认为这不值得做。)

我同意@iSazonov的观点,这种情况并不理想,无法再引入另一种方法来完成它应该起作用的工作,但是正如@ mklement0所提到的
https://github.com/PowerShell/PowerShell/issues/1995#issuecomment -562334606对吗?

@ mklement0我不确定iep / invoke-external程序如何工作,因为cmdlet受到我们试图修复的相同参数的限制。 这是解析器问题,对不对? 如果我们试图将所有内容都隐藏在cmdlet /函数中,我想它会变得非常复杂,因为它必须取消烦人的/重构意图。 还是我错过了什么?

@oising

需要明确的是:链接的iep函数修复了#1995,而不是_this_提案的主要含义(如上所述,我认为这是_not_值得追求的),尽管请注意,OP中的意图是要也解决#1995。

1995年是关于能够可靠地将_PowerShell_的语法(而不是其他Shell的语法)传递给外部程序的参数,如PowerShell在其自己的parsing_之后逐字逐句地看到的那样。

PowerShell自身的解析很好,只要将参数传递给_PowerShell_命令就没有问题。

相比之下,将参数传递给_externalprograms则很不容易,这就是iep修复的问题。

当构建最终使用的命令行时,由于存在错误的_re-quoting和escaping_幕后隐藏参数而被破坏。
https://github.com/PowerShell/PowerShell-RFC/pull/90提出了一个正确的解决方案(没有关于如何处理向后兼容性的协议,还有一些尚待解决的小问题),其核心依赖于最近引入了System.Diagnostics.ProcessStartInfo.ArgumentList来为我们执行正确的重新引用和转义(我们只传递了PowerShell自身解析产生的参数集合)–顺便说一下,仅在Windows_上是必需的; 在Unix上,明智的是,在进程启动时传递参数时,_没有命令行(在外壳程序之外):您只逐字传递参数(作为数组)。

简而言之:

  • 提供iep作为内置函数将是一种低礼仪性的选择,可以将适当的参数传递给外部程序,直到可以实现适当的修复(在重大更改的情况下)为止

  • Invoke-NativeShellins )cmdlet是正确的解决方案,而不是权宜之计,它提供了一种使用_its_语法调用_a不同的shell's_(本机shell的)命令行的方法-参见上文

当前,在某些情况下,剪切和粘贴PERL命令无法按预期在Python中运行。 去搞清楚。 对于那里的所有裁切人员而言,这必定是一个很大的劣势。 我认为Python应该为此使用特殊的运算符。

我不确定你是否理解我的建议; 你说的是_precisely_我的意思。 让我更详细地讲一些具有战略意义的提示:D

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

我在这里想念什么? 如果管道出现在--%那只是另一个哑字符:一个参数。 运算符优先级没有任何问题。 如果包含在嵌套管道或子表达式中,请再次开始对powershell进行解析。

有什么想法吗?

您缺少的是需要将右括号作为参数传递给外部程序。 这是典型的DWIM方法,但PowerShell不是HAL。

谢谢大家的详细反馈。 在这里自己说话。 在深入了解具体内容之前,请注意以下几点:

  • 我仍然不打算使用vNext / 8.0来决定打破一堆全新的东西。 向后兼容性很重要。 我知道这很痛苦,我宁愿生活在一个我们可以回过头来纠正PowerShell曾经犯过的每一个错误的世界中,很多都是无意的。 但是绝大多数PS用户并没有在SO边缘案例中陷入困境。 他们已经将相同的解决方法复制/粘贴到某项内容达4至10年,他们不一定知道它为什么起作用,并且他们在乎脚本升级失败时,我们也不会在意“正在修复错误”。 他们只知道“更新PS破坏了我,我无法信任更新”,然后我们最终导致更多用户放慢了更新和采用的速度。
  • 我认为有些人对此问题可能会投射出许多不同的希望和愿望,或者将其视为“有/有其他修正”。 我意识到我在关闭#1995时加剧了这一点,对此我感到抱歉(尽管我会说,我仍然不确定具体是否可以修复,但是我们会更加认真地研究一下),但是确实是一个“陷阱门”解决方案,可以使新老用户迅速摆脱困境,而不会沮丧。 我们仍然可以就房屋PS解析端的修复问题进行长期讨论,以便为所有问题提供真正的跨平台解决方案。

现在,我读到的评论是:

  • 我对建议与#! (与&!$!对齐)的提议保持警惕,因为在默认情况下,用户可能不愿指定解释器如果我想做类似$! net <stuff>事情,则net.exe不是我的解释器,并且在非Windows上,“本机”要做的还是传递给sh / bash /之前的任何东西传递给正在运行的二进制文件,例如,我不希望Python解析*.py $! /usr/bin/python *.py甚至$! python *.py字符串中的$! /usr/bin/python *.py我希望Bash进行遍历然后移交给与python的匹配项列表。
  • 就像@oising所建议的那样,我赞成--%作为运算符,重载在前面使用。 可发现性是一个问题,考虑到我们已经致力于该对象的可发现性已有10年了(它现在可以在Google上使用! ,我说我们就可以做到这一点。(我是使用@ SteveL-MSFT的人将其放在他的提案中作为替代考虑),但是,问题:我们这里是否还需要&调用运算符?为什么不只是以--% native.exe do /some $stuff开始行呢?
  • @rjmholt :我在这里并不完全理解您的评论,但是我认为我们不应该对可执行文件之后的现有--%行为进行任何更改,但是我认为我们可以为您“行为”与可执行文件之前的位置。
  • @jazzdelightsme阅读此

    它也适用于最专业的用户,他们只想从StackOverflow复制/粘贴命令,而不必进行任何操作。

    在我看来,这是方案的一个很大方面,可能是最大的方面。 也不只是SO,它是来自网络的文档,这些文档预先假设您处于类似Bash的外壳中。

  • 不管#1995之类的问题是否得到解决,要指出的是,a)像这样的奇怪边缘情况有很长的尾巴,b)真的很难解决而又不伤人。 所有这些不一定都是正确的,但是在过去的几年中我们已经达到了足够的效果,对我来说很明显,我们需要这样的“陷阱门”,使人们摆脱他们不想潜水的毛茸茸的境地嵌套转义的兔子洞(只是发现有#1995之类的错误,即使他们确实知道自己的知识,也无法阻止他们工作)。

在审查评论时,我认为我们仍然需要回答两个悬而未决的问题:

  1. 我们是否继续在PowerShell中解析命令分隔符及其后的所有内容,还是命令分隔符也逐字传递到本机shell?
    我说我们应该将所有内容逐字传递给本机shell,因为这是您目前无法做的事情, @ oising的领先括号解决方案可能是这样的解决方案,因为我们仍然可以支持管道,管道链等如果您想混合/匹配本机和PS解析(尽管我想从更接近解析器的人那里得到一些意见)。
  2. 什么是接线员?
    如上所述,我喜欢--%因为它已经以“只做过去的事情”运算符而享有盛誉,并且我们将不得不使用任何新内容重新设置可发现性。 我了解类似@ mklement0这样的参数,如果这样做,我们将更改--%的技术含义(@ SteveL-MSFT也向我提出了这种说法),但我不认为大多数野外人士都在考虑--% ,以“它在cmd中的作用”。 我认为将这种假设编入运算符的定义中并不是一件容易的事,例如“在此执行本机shell要做的事情”。

正交地说,只是想到了我们可能想要或可能不想支持的另一种情况。

在Windows上,cmd(有效地)将当前目录包含在其PATH查找中。 但是,PowerShell要求用户在当前目录不在PATH之前添加./以便执行任何EXE(或PATHEXT任何内容)。

因此,我的公开问题是:我们是否真的想将整个命令字符串交给cmd? 我建议,如果我们真的想使用此操作符来简化复制/粘贴场景,答案是肯定的,因为某些第三方文档/教程不一定详细说明如何放置他们希望您使用的工具进入PATH 。 它可能并不重要,许多无论哪种方式,但我确实想在这里一一列举了。

  • 我仍然不打算使用vNext / 8.0来决定打破一堆全新的东西。

是的,我不知道您如何解决这些问题,而又要解决难以置信的难以解决的问题。 另外,我不了解所有内容,但即使我不想在编写多个版本时也必须兼顾所有这些复杂的活页夹/解析器差异。

通常,如果我反对打破变化,那是针对其他人的。 但是,即使对于ITT中存在的人们,这似乎也可能是一个PITA。

  • 就像@oising所建议的一样,我赞成--%作为运算符,重载在前面使用。 (...)为什么不以--% native.exe do /some $stuff开始行?

我真的很喜欢理想情况下,尽可能接近调用CreateProcess / exec (加上nix env var处理以及输入/输出重定向或控制台附件)。 或者也许完全回到cmd.exe / bash

我也不会坚持将&放在--%前面。 我可以证明至少在我的头上将原始--%当作指令/运算符来对待。 那多行呢? 允许--%@""@进行var替换和子表达式,单引号@''@传递文字吗? 没有here字符串,仅是单行吗?

就像@oising建议的一样,我赞成-%作为运算符

为我工作。

但是,问题是:我们在这里甚至需要&调用运算符吗?

只要我仍然可以执行--% & $computedPathToNativeExe foo;@workspace然后由我罚款。 我们确实需要能够与此一起使用调用运算符。

我说我们应该将所有内容逐字传递给本机shell,因为这是您目前无法做的事情

同意

我认为将这个假设编入运算符(-%)的定义并不费力,例如“在这里执行本机shell所做的事情”。

也同意!

另外,也许这是一种获取功能的方法,该功能可以在仅针对可执行文件的会话定义的可执行文件之前定义环境变量(#3316):

--% JEKYLL_ENV=production jekyll build

只是一个想法。

@rkeithhill ,使用call native operator的想法是,该行未由PowerShell处理,而是逐字发送到操作系统的“默认外壳”。 因此,在这种情况下,第一个示例--% & $computerPathToNativeExe foo;@workspace不起作用。 您将不得不使用当前方法并进行适当的转义。 第二种情况--% JEKYLL_ENV=productoin jekyll build应该可以工作。

该行未由PowerShell处理,并逐字发送到操作系统的“默认外壳”

好的,那似乎很合理。 但是,此--% JEKYLL_ENV=productoin jekyll build只能在Linux / macOS上运行,而不能在Windows上运行-至少没有PowerShell的帮助,对吧?

当前建议的@rkeithhill不能在Windows上运行,因为它依赖于操作系统的“默认”外壳。 对于PowerShell声明进程的env var,我们有一个不同的问题可以讨论。

@ PowerShell / powershell-committee在今天进行了讨论,我已经更新了原始建议以反映该讨论的结果。 我们将继续进行实验性功能实现,并从使用工作代码的用户那里获得更多反馈。 我们仍然对运营商开放,但是现在使用--%

其中$foo是文字文件名, $PWD是WSL中的当前目录。 请注意,在第一个示例中,您将必须知道转义&&使其在WSL中而不是PowerShell中执行。

$foo$PWD都是变量引用,将由sh进行评估。

要将执行结果通过管道传递回PowerShell,要求用户首先将结果存储到变量中:

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

请注意,与当前的&调用运算符不同,您不能使用任何PowerShell语法,因此:

--% $commandline

将尝试按字面意思(在cmd之下)或在shell扩展之后(在sh )执行$commandline 。 PowerShell不会尝试解析该变量。

剪切和粘贴问题可以通过在键入&n之后简单地粘贴来解决。

通过简单地在--%之后粘贴

上面wsl示例如下所示:

哪一个?

由于--%之后的所有参数都传递给“默认”外壳程序,因此,为了支持管道化到PowerShell,您将需要使用一个变量:

这是重复。

如果我们要添加一些有用的东西,我建议使用Pivot。

为什么我们不能像herestring这样的文字

@!“”!@

因此,我们可以采用一种真正的方法来代替-%也有其自身问题的-%。

至少可以通过“更好的”设计策略来掩盖问题。
这些边缘情况的可发现性位于About_Strings中,应该在其中解决它。

只是一个想法。

我看不到@!"…"!@Invoke-NativeShell @"…"@ 。 我们是否正在尝试使PowerShell更像Brainfuck?

@ yecril71pl

我看不到@!"…"!@Invoke-NativeShell @"…"@ 。 我们是否正在尝试使PowerShell更像Brainfuck?

我相信, @ romero126的意思是建议一个单行herestring文字,以便人们可以写

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

代替

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

如果要考虑这样的单行herestring,我建议使用@"""…"""@@'''…'''@类的语法,这将比@!"…"!@更加容易输入IMO。


@ romero126您能否阐明您的意思?

@ yecril71pl感谢您捕获这些错误,并通过修复更新了顶部消息。

未指定--%将作为命令行执行还是作为批处理文件执行。

我相信, @ romero126的意思是建议一个单行herestring文字,以便人们可以写

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

代替

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

如果要考虑这样的单行herestring,我建议使用@"""…"""@@'''…'''@类的语法,这比@!"…"!@更加容易输入IMO。

@ romero126您能否阐明您的意思?

我相信,如果我们为字符串增加灵活性以处理“几乎原始的”未解析的数据,那么我们不会在弹出窗口中遇到问题。
@TSlivede,我几乎完全同意你所说的。 仅稍有变化。 它应该能够处理单行和多行,而无需在引号内插入任何内容。 无论我们采用哪种语法来实现这一目标,我都愿意接受。 我更专注于这个概念。

通过这些示例,它增加了很多灵活性,而我不需要“ Invoke-NativeShell”,因为它只返回一个字符串。 这意味着我们可以像字符串一样自由地操作它。 这样就有可能成为伟大作品的主食。

像这样的Pivot也将意味着我们不必处理上面的问题,只要我们能够处理边缘情况,就可以在几乎解决方案上旋转轮子,而不是整体解决问题。

快速示例:

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

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

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

但这并未解决此评论第二部分中的@ mklement0封面问题。 事实是,我们不能只将一个字符串传递给所有命令。 在许多情况下,必须将它们拆分。

我相信,如果我们为字符串增加灵活性以处理“几乎原始的”未解析的数据,那么我们不会在弹出窗口中遇到问题。

这就是'@'的用途。

通过这些示例,它增加了很多灵活性,而我不需要“ Invoke-NativeShell”,因为它只返回一个字符串。 这意味着我们可以像字符串一样自由地操作它。 这样就有可能成为伟大作品的主食。

本机命令返回字符串序列。 @!"有潜力成为难以理解的主食。

我相信,如果我们为字符串增加灵活性以处理“几乎原始的”未解析的数据,那么我们不会在弹出窗口中遇到问题。

这就是'@'的用途。

通过这些示例,它增加了很多灵活性,而我不需要“ Invoke-NativeShell”,因为它只返回一个字符串。 这意味着我们可以像字符串一样自由地操作它。 这样就有可能成为伟大作品的主食。

本机命令返回字符串序列。 @!"有潜力成为难以理解的主食。

我觉得你很困惑。 我的意思是。
单引号和双引号仅会扩展$ variable。 它不会自动转义字符。 这也限制了我,因此我不再可以使用其定义字符。 因此,要使它像现在一样正常工作。 我仍然必须转义字符,这就是为什么我们需要包含&!的全部内容。 命令。

如果您有更好的解决方案,我会全力以赴。 我确实相信这比添加&!

单引号和双引号仅会扩展$ variable。 它不会自动转义字符。 这也限制了我,因此我不再可以使用其定义字符。 因此,要使它像现在一样正常工作。 我仍然必须转义字符,这就是为什么我们需要包含&!的全部内容。 命令。

您必须转义哪些字符,为什么?

@ romero126现在我很困惑您在说什么。 您是否知道,powershell有一个here字符串

我以前以为,您可能会在这里考虑powershell的语法,字符串令人讨厌(至少我有时会感到恼火,在开头和结尾必须要有换行符...),但是现在我不确定是什么你是说,你能澄清吗?

@TSlivede同意这里的字符串在语法上有点麻烦。

有一个现有的建议#2337,它提出了一些改进,并且虽然着重于允许缩进定界符,但@lzybkr还在https:// github中提出了一个类似Rust的_single-line _(-also)变体

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

通过根据需要将_multiple_ '@结合起来(例如@'' can use @' here ''@ ,可以避免转义的需要。

换句话说:这将是对PowerShell字符串文字的普遍增强,可在任何地方使用, Invoke-NativeShell也将从中受益。

鉴于#2337主要集中在其他方面,我在#13204中创建了一个

避免逃跑的需要

我已经在这个线程中见过几次了。

令牌生成器的任何指示符以开始新模式都需要一种转义机制,因为您需要将令牌生成器的指示符与该字符的文字形式区分开来,以区别于令牌生成器。

今天最自然的形式是单引号字符串:

  • ' :开始单引号令牌化
  • ' :结束单引号令牌化
  • '' :在单引号字符串中写一个文字单引号

我认为尽管如此,我们谈论逃避的原因是:

  • --%运算符定义了太多退出其模式的方式( |&&||; ,换行符)并允许字符串在其中(剥离其'字符)
  • Herestrings和共同解析为一个字符串参数,而不是它们的数组

想要将参数传递给可执行调用意味着我们实际上是在谈论数组语法:

  • 如何启动阵列(启动逐字模式)
  • 如何分隔数组(将单个参数传递给新的可执行文件)
  • 如何结束数组(逐字结束模式)

(这是* nix上的本机概念,因为exec需要一个数组。在Windows上,我认为我们被迫采用一个数组,将其序列化为特定的字符串语法,并通过CommandLineToArgvW 。但是,.NET为这两种方法都提供了基于数组的抽象-因此数组仍然最有意义。

这意味着我们需要两个转义符:

  • 数组分隔符文字
  • 数组结尾文字

例如,假设:

  • --%在命令成为新的逐字语法之前
  • 我们如何结束语法? 换行符和;也许?
  • 我们如何分隔参数? 也许?

那么问题是:

  • 我们如何传递文字;或换行符?
  • 我们如何传递文字

本机命令行不是数组。 它可以是字符串或AST。 由于PowerShell在本机外壳中对AST一无所知,因此仅保留一个字符串。

本机命令行不是数组。 它可以是字符串或AST

您尚未为该断言提供任何依据。

这是PowerShell为命令构建AST的位置(任何形式,因为直到运行时命令才知道它是哪种类型,它才知道):

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

请注意,命令元素(包括参数和参数)已添加到数组中。

您可以在PowerShell中看到如下所示:

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

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

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

该数组在此处编译为表达式:

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

然后将其传递到此处的管道调用(注意Expression.NewArrayInit(typeof(CommandParameterInternal[]), pipelineExprs)是传递参数的位置):

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

在运行时,运行已编译的表达式时,将成为此方法调用

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

最终,当调用管道时,该参数数组将使用NativeCommandParameterBinder绑定到本机命令:

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

这需要参数数组,其中包括AST元数据(例如,每个参数使用哪种字符串),并在NativeCommandProcessor此处构建用于参数调用的新字符串:

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

这样做是因为我们当前用于流程调用的.NET API是旧的“单个字符串中的所有参数”版本,而不是.NET Core中的新参数,后者允许您传递数组并让.NET重建调用根据特定平台的需要。

但是我们使用该API只是实现细节。 例如,我们可以直接在* nix上调用fork / exec ,就像在这里这样:

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

注意这里的exec需要一个数组-我们这样做是因为这是将参数传递给syscall的最简单,最直接的方法,这最终是逐字参数语法的目标。

@rjmholt

_在功能上,现有的字符串文字形式已经满足了所有需求,如果您将本机命令行作为单个字符串传递,我强烈建议您通过新的Invoke-NativeShell / ins cmdlet来传递。

此处的起点是_single string_,而不是数组:它是_entire命令行_-由目标shell_解析-是最有意义的将其反映在PowerShell中,而不是试图笨拙地使用像--%一样将不同的Shell语法转换为PowerShell语法

Invoke-NativeShell所要做的就是通过其CLI将包含整个native-shell命令行的单个字符串传递给native shell:

  • Unix上,这意味着:将字符串as_is,_as whole_作为参数_array_的第二个元素传递给exec (其中-c是第一个参数数组元素,而/bin/sh可执行文件); 以.NET术语表示,并带有一个简单示例,其中printf "'%s'" foo | cat -n是包含要传递的命令行的单个字符串的逐字内容(请注意,使用.ArgumentList而不是.Arguments ; '的加倍仅是此示例的人工产物):

    • $psi = [Diagnostics.ProcessStartInfo]::new('/bin/sh'); $psi.ArgumentList.Add('-c'); $psi.ArgumentList.Add('printf "''%s''" foo | cat -n'); [Diagnostics.Process]::start($psi).WaitForExit()
  • Windows上,这意味着:为了容纳cmd的怪癖,按如下所示填充CreateProcesslpCommandLine参数(假定lpApplicationName为环境变量ComSpec ,它指向cmd.exe ): "/c " + cmdLine ,其中cmdLine是整个命令行字符串。 以.NET术语表示,并带有一个简单示例,其中echo a^&b & ver是包含要通过的命令行的单个字符串的逐字内容:

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

      • @ yecril71pl ,这暗示了您将使用命令提示符还是批处理文件语义的问题的答案:它是前者,其他脚本语言(例如带有os.system() Python)也使用过它; (可能不幸的)含义是,您将无法转义%字符,并且for循环变量必须使用单个% (例如, %i而不是%%i ); 并且必须在循环主体命令前加上@前缀,以防止回显它们(例如for %i in (*.txt) do <strong i="56">@echo</strong> %i )。

该过程对最终用户是“透明的”,因此他们需要关注的只是传递本机外壳命令行_verbatim_,而只需要满足_PowerShell的字符串字面语法要求:

使用形式ins @'<newline>...<newline>'@ ,您已经可以_n_照常使用本机外壳命令行:请参见上文,了解如何使用各种现有的字符串-文字形式,由于-interpolating_在这里的可用性-字符串形式( @"<newline>...<newline>"@ )-包括在命令行中使用_PowerShell_变量和表达式的能力(无法这样做是--%的主要缺点之一)。

我之前的评论提出的是_new原始字符串literal_的通用形式,它__与此提议_分开(并且(希望)其解析逻辑定义明确); 但是,如前所述, Invoke-NativeShell调用将更加方便。

而不是已经可以执行的操作(假设使用Invoke-NativeShell / ins命令):

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

使用建议的新单行原始文字,您可以简化为:

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

如果这种新的原始字符串文字语法的细节需要辩论,请让我们在#13204中进行,而不是在这里。

因此,重新阅读该提案以了解原始意图,我发现它实际上只是在为/bin/sh -c '<command>' + cmd版本要求更好的语法,以及针对每个目标shell的特殊字符串转义逻辑。 我最初的构想是基于特殊的语法,需要与解析器和本机命令处理器一起使用,并且基于.NET的ProcessStartInfo.Arguments属性很难在引用传递方面正确使用

但实际上Arguments字符串只是通过逐字传递。 因此,我们要做的就是直接传递这样的字符串。

在那种情况下,我认为完全不需要任何新语法(它将使令牌化逻辑变得复杂,并且我怀疑会使人困惑),而是这只是一个新命令,就像您说的那样, ins 。 .NET在这里有一些逻辑,我们将不得不重新实现它,但这没关系,因为无论如何我们都需要变得更聪明。

@rjmholt的评论的一些评论https://github.com/PowerShell/PowerShell/issues/13068#issuecomment -660231641

令牌生成器的任何指示符以开始新模式都需要一种转义机制,因为您需要将令牌生成器的指示符与该字符的文字形式区分开来,以区别于令牌生成器。

我同意,我不认为在此插入行内字符串语法可以完全避免转义。

-%运算符定义了太多退出模式的方式(|,&&,||,;,换行符),并允许其中包含字符串(将其'字符剥离)

说到--% ,您是指现有的--%开关吗? 如果是这样,最好在您的注释中明确说明,因为现在建议将--%用作呼叫运营商。

想要将参数传递给可执行调用意味着我们实际上是在谈论数组语法:

该提案是调用本地shell(sh或CMD)和呼叫操作后,从字面上通过一切--% ,如sh -c ... 。 因此,不是将参数传递给目标可执行文件的powershell。 在那种情况下,我认为我们不需要_an数组语法_,对吗?

我们如何结束语法? 换行符和; 也许?
我们如何分隔参数? 也许是“空间”?

我认为逐字分析在换行符处结束。 要将新行作为参数的一部分传递,我想使用\n ,就像您直接在bash中所做的一样。

本机命令行不是数组。 它可以是字符串或AST

您尚未为该断言提供任何依据。

这是PowerShell为命令构建AST的位置(任何形式,因为直到运行时命令才知道它是哪种类型,它才知道):

如果该建议得以实施,它将引入一种特殊的结构,其中PowerShell根本不会构建AST。

很高兴听到它,@ rjmholt。

.NET在这里有一些逻辑,我们将不得不重新实现

我不认为我们实际上需要重新实现任何功能,如我先前的注释中的命令所示:在Windows上,将.Arguments用于整个命令行字符串传递到WinAPI; 在Unix上,使用.ArgumentList构造参数_array_,该参数直接与exec

如果该建议得以实施,它将引入一种特殊的结构,其中PowerShell根本不会构建AST。

好吧,它将建立另一种AST类型,为此目的而设计。 如果它不是AST的一部分,那么它基本上与注释相同,什么也不会发生。

_在功能上,现有的字符串文字形式已经满足了所有需求,如果您将本机命令行作为单个字符串传递,我强烈建议您通过新的Invoke-NativeShell / ins cmdlet来传递。

我个人喜欢Invoke-NativeShell cmdlet的想法,在某些方面要比调用运算符--% 。 我听到的关于此想法的主要担心是,它比--% + Ctrl + v更具键入性,区分文字here-string和插入here-string是用户的另一负担(_我个人认为ins--% _更容易键入。

我所听到的关于此想法的主要担忧是,它比--% + Ctrl + v更打字,区分文字here-string和插入here-string是用户的另一负担(_我个人认为ins--% _更容易键入。

也许PSRL可以提供帮助。 类似于有关围绕粘贴路径的键处理程序的问题,PSRL可以读取CommandAst ,看到它是ins并正确地转义粘贴的字符串。

好吧,它将建立另一种AST类型,为此目的而设计。 如果它不是AST的一部分,那么它基本上与注释相同,什么也不会发生。

当然可以,但是那棵树是微不足道的。 它不会反映要执行的命令的含义。

启动进程cmd.exe重定向stdin / stdio,我们可以从字面上使用字符串来调用-NativeCommand与bash相同

很高兴听到它,@ daxian-dbw。
喜欢这个想法, @SeeminglyScience

将文字here-string与插值here-string区别开是用户的另一负担

我认为这是_plus_,因为-与--% -使用@"<newline>...<newline>"@可以让您直接将_PowerShell_变量和表达式的值合并到本机命令行中。

当然,这假定用户知道单引号(verbatim)和双引号(插值)字符串之间的区别,以及有关选择性`转义$ ,但是我认为这很重要(高级)选项。

为了帮助剪切粘贴用户,建议的PSReadLine功能应默认为@'<newline>...<newline>'@ (普通)。


最终,我们不想提倡用户不需要了解和理解PowerShell的独特语法要求的思维方式-相反,我们希望根据他们带来的_附加功能来促进对它们的理解。

但是为了鼓励用户使用PowerShell的_own_语法(定义为_cross-platform_),它必须可预测地工作
为此,必须固定#1995 ,最好直接

总结一下:

  • --% (这里提出的新运算符形式)之类的东西,定义上是_platform-specific_功能,决不能替代#1995

  • Invoke-NativeShellins )避免了建议的--%运算符的所有局限性,并提供了@joeyaiello提到的“陷阱门”解决方案:一种快速执行编写的命令行的方法用于本机外壳,而无需使其适应PowerShell的语法。

  • --%应该仅保留其原始_argument_形式,并且应保留_Windows-only_功能(它有效_,尽管不是_technically_的特征)-换句话说:无需更改。

    • 固定#1995之后, --%的唯一目的就是容纳Windows_上的_edge情况:允许您控制传递给恶意程序(例如msiexec的命令行的显式引用参数内引用的非常具体的形式。

我在PS v7.x中真正想要的其他功能之一是能够使本机命令退出并显示错误代码以停止我的脚本,例如本RFC中讨论的set -e如果解决方案类似于Invoke-NativeCommand ,那会与Invoke-NativeShell混淆吗?

只是尝试向前播放电影,因为就我想调用原生功能而言,我真的希望我的构建和测试脚本在调用msbuild,cl,cmake,Conan,npm等时出错,并且这些原生命令以一个非零的退出代码,而不必记住始终检查$ LASTEXITCODE。 如果实现仅通过另一个首选项变量(例如$PSNativeCommandInErrorActionPreference那么我想这会影响对本机shell的调用-它是本机“命令”之类的吗?

我所听到的关于此想法的主要担忧是,它比--% + Ctrl + v更打字,区分文字here-string和插入here-string是用户的另一负担(_我个人认为ins--% _更容易键入。

也许PSRL可以提供帮助。 类似于有关围绕粘贴路径的键处理程序的问题,PSRL可以读取CommandAst ,看到它是ins并正确地转义粘贴的字符串。

但是关于:

$c = gcm ins
& $c ...

PSRL将很难识别那里发生的事情。 它必须是有状态的,将别名ins解析为invoke-nativeshell,然后确定您要调用commandinfo,然后对arg进行减值处理? 我真的非常不喜欢使用特殊框伪造命令的想法。

将文字here-string与插值here-string区别开是用户的另一负担

我认为这是_plus_,因为-与--% -使用@"<newline>...<newline>"@可以让您直接将_PowerShell_变量和表达式的值合并到本机命令行中。

我没有关注您@ mklement0。 为什么不能教--%做这件事? 我已经将此作为用例。

我真的非常不喜欢使用特殊框伪造命令的想法。

这里没有伪指令,只有真实的cmdlet Invoke-NativeShell别名为ins

gcm insGet-Command ins )将以[System.Management.Automation.AliasInfo]实例的形式提供有关ins别名的信息,您可以稍后将其传递给&用于调用,与其他任何命令或别名一样。

因此,您的示例将完全按预期运行-只是如果您使用gcmGet-Command )绕行,则必须拼写适当的字符串文字语法_yourself_,而无需如果实现了@SeeminglyScience的建议,则

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

或者,如果实现了#13204中提出的新的原始字符串文字语法:

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

当然,如果您熟悉PowerShell字符串文字的语法,也可以使用常规的单引号字符串:只需将整个'...'字符串中的嵌入式'加倍:

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

为什么不能教--%做这件事?

  • --%缺少结束定界符,这阻止了它在Powershell管道中的使用,这是不可接受的限制(请注意, (...)封装是_not_一个选项,因为这意味着所有输出的前期集合而不是_streaming_处理)。

  • 在Unix上, --%不能同时支持(未转义) $作为PowerShell元字符_和_作为POSIX-shell元字符。

再说一遍:没有理由,将_不同的shell的语法_拼凑到PowerShell中也没有好处-将本机shell命令行_作为字符串传递给ins ,所有概念上的问题都消失了。

Invoke-NativeShell表示PowerShell不是本机外壳。 这个假设是否总是正确的,并且永远是正确的?

@用例是有人从自述文件或其他内容复制并粘贴本机命令。 如果他们做的事情比输入ins SPACE CTRL + v更复杂,那么他们将像通常一样构建并转义自己的字符串。

@ mklement0

但是为了鼓励用户使用PowerShell的_own_语法(定义为_cross-platform_),它必须可预测地工作
为此,必须固定#1995 ,最好直接

这与关于ins / --%的讨论分开吗? 如果您不同意,可以澄清为什么该问题会影响新的命令/语法吗?

@SeeminglyScience ,是的,它是独立的,但是在该线程中尚不清楚。
出现#1995的唯一原因是它最初是为解决此问题而关闭的(尽管此后幸运地重新打开了),并且此处的OP仍指出此问题将解决此问题(“这也应解决以下问题:”) 。
因此,我想强调指出, ins / --%会急于地址#1995,该地址需要自己修复。
(只有PowerShell自己的参数传递可以正常工作,我们才可以出于良心建议用户完全学习并接受PowerShell自己的语法,我们应该这样做。)

@ SP3269 ,我们可以跨过

不过实话说:

如果您将“ native”视为“专门为主机平台[家族]编写”,则即使PowerShell成为给定平台上的默认系统外壳,该名称仍然适用(希望如此,但我不认为这是现实的,至少在可预见的将来如此。

至于替代品:

  • Invoke-[System]DefaultShell是最准确的名称(尽管如果PowerShell成为系统的默认外壳,它将毫无疑问地不再适用),但“本地”似乎是PowerShell世界中最常用的术语。

  • Invoke-LegacyShell在Windows上有一些道理,但我认为Unix用户对这个术语不会太客气。

我想我开始消除我在试图跟踪每个人的动机,想法和问题时所遇到的混乱。 看来我们(至少@ mklement0和我)将其视为针对同一概念问题的两种不同观点的两种不同解决方案。 我的建议是使用--%作为_parser_的提示,以更改与调用&运算符结合使用的解析方式(不一定使用--%独立)。另一方面,Michael似乎正在将ins作为嵌入式cmdlet来替换powershell自己的内部本机命令代理和参数解析器,并且不希望更改powershell的解析器,而是使用字符串/此处-strings捕获预期的参数(我说这也可以与--% 。)

-%缺少结束定界符,这阻止了它在Powershell管道中使用

这一点我一点都不明白。 与ins / invoke-native命令相比,它不再缺少结束定界符。 如果您需要定界或从LHS提要,请使用此处字符串,否则将其视为单个语句。

无论选择哪种方式,似乎都需要选择一个本机shell来分派命令行。 我建议我们在Windows上使用环境变量COMSPEC (默认为%systemroot%\system32\cmd.exe ),在Linux上使用SHELL (默认为/bin/bash )-如果SHELL不如果不存在,我们应该以/bin/sh (无论如何,这在大多数情况下都是bash的符号链接。)

-%缺少结束定界符,这阻止了它在Powershell管道中使用

这一点我一点都不明白。 与ins / invoke-native命令相比,它不再缺少结束定界符。 如果您需要定界或从LHS提要,请使用此处字符串,否则将其视为单个语句。

基本上,您不能调用cmd --% /c someapp | someOtherApp而让cmd处理管道。 PowerShell仍将管道(以及其他一些东西,例如&&||为PowerShell令牌)作为管道令牌,而不是将管道作为一部分传递给本机命令参数字符串。

和Linux上的SHELL(默认为/bin/bash

否:类似Unix的平台上的_system_ shell是_invariably_ /bin/sh
这不同于给定的_user's_交互式外壳(反映在$env:SHELL -但是,不幸的是,当前不是_PowerShell_是用户的外壳,请参见#12150),它是(a)可配置的,并且(b)通常_defaults to_ /bin/bash ,但即使不是给定的,macOS最近也证明了这一点,即过渡到/bin/zsh作为新用户的默认交互式外壳。

我建议我们在Windows上使用环境变量COMSPEC

好的一点,那是在Windows上引用系统外壳程序(命令解释器)的更好方法-我已经相应地更新了上面的示例命令。

我在PS v7.x中真正想要的其他功能之一是能够使本机命令退出并显示错误代码以停止我的脚本,例如本RFC中讨论的set -e如果解决方案类似于Invoke-NativeCommand ,那会与Invoke-NativeShell混淆吗?

只是尝试向前播放电影,因为就我想调用原生功能而言,我真的希望我的构建和测试脚本在调用msbuild,cl,cmake,Conan,npm等时出错,并且这些原生命令以一个非零的退出代码,而不必记住始终检查$ LASTEXITCODE。 如果实现仅通过另一个首选项变量(例如$PSNativeCommandInErrorActionPreference那么我想这会影响对本机shell的调用-它是本机“命令”之类的吗?

为此,我们在build模块中有Start-NativeExecution

我看到了如果您希望以后的用户有效地抛出非零的退出代码,那么如何将Start-NativeExecutionInvoke-NativeShell结合起来? 我真的希望像PR#3523之类的东西能够与此调用原生功能一起使用,因为我希望两者一起工作。 我想如果将调用原生方法实现为cmdlet(Invoke-NativeShell vs-%),则可以实现-ErrorAction Stop使其在非零退出代码上抛出(终止错误)。

-%缺少结束定界符,这阻止了它在Powershell管道中使用

这一点我一点都不明白。 与ins / invoke-native命令相比,它不再缺少结束定界符。 如果您需要定界或从LHS提要,请使用此处字符串,否则将其视为单个语句。

基本上,您不能调用cmd --% /c someapp | someOtherApp而让cmd处理管道。 PowerShell仍将管道(以及其他一些东西,例如&&||为PowerShell令牌)作为管道令牌,而不是将管道作为一部分传递给本机命令参数字符串。

是的,我了解当前的行为@ vexx32 。 但是我们(至少我不是)不是在谈论--%当前行为,而是在谈论增强它,不是吗? 为什么我感觉到我们在这里互相交谈? :)

正如我所说的那样,在上方,我们可以增强&以与--%因此这是可能的。 我还认为我们需要弄清楚隐式本机exec与带参数的shell的显式调用。 这一直是混乱的根源,即使对于经验丰富的Windows用户而言,也是如此,以某种方式需要cmd.exe(shell)来“运行”其他可执行文件。 linux / osx也是如此。

是的,我了解_current_行为@ vexx32 。 但是我们(至少我不是)不是在谈论--%的_current_行为,而是在谈论增强它,不是吗? 为什么我感觉到我们在这里互相交谈? :)

因此,当前调整--%是,如果它通常是命令名所在的位置,则它右边的所有内容都将按原样进行解析(直到新行)并直接发送到bash / cmd。 没有任何PowerShell语法,例如here-strings,没有什么空间,因为那样它就具有--%和本机命令处理器当前存在的所有相同问题。

我看到了如果您希望以后的用户有效地抛出非零的退出代码,那么如何将Start-NativeExecutionInvoke-NativeShell结合起来? 我真的希望像PR#3523之类的东西能够与此调用原生功能一起使用,因为我希望两者一起工作。 我想如果将调用原生方法实现为cmdlet(Invoke-NativeShell vs-%),则可以实现-ErrorAction Stop使其在非零退出代码上抛出(终止错误)。

当前将是Start-NativeExecution { Invoke-NativeShell <strong i="10">@whatever</strong> }

这一点我一点都不明白。 与ins / invoke-native命令相比,它不再缺少结束定界符。 如果您需要定界或从LHS提要,请使用此处字符串,否则将其视为单个语句。

Invoke-NativeShell不需要任何结束定界符,因为它是常规命令,而--%的恐怖则不需要。

和Linux上的SHELL(默认为/bin/bash

否:类似Unix的平台上的_system_ shell是_invariably_ /bin/sh

我认为这应该等同于制作一个可执行的脚本文件并运行它,这将选择正确的外壳的任务委托给了操作系统,因此我们不应该为之烦恼。 包括,如果它以#!开头,那就这样吧。

Invoke-NativeShell背后的想法是为本机shell的_CLI_提供方便的包装。

  • 在Unix上,这已经足够了,创建一个辅助脚本文件不仅会带来额外的开销,而且还会在$0 (脚本文件路径)中报告一个随机值,而通过CLI调用可预测地包含/bin/sh ,甚至可以通过在代码参数后面加上另一个参数(例如sh -c 'echo $0' foo )进行显式设置

    • (顺便说一句:使用shebang行#!/bin/sh创建shell脚本意味着通过完整路径定位系统shell,就像直接调用/bin/sh 。)
  • 在Windows上,通过批处理文件_would_执行会带来好处(但不是委派$env:ComSpec可以完成的定位系统外壳的任务),因为它将避免%转义的问题和for行为中提到以上

为了礼貌地处理后者(以便用户不必编写自己的辅助批处理文件),为概念清晰起见,我们可以提供
-AsBatchFile切换为_opt-in_。

就是说,如果大家一致认为,用于剪切和粘贴的大多数公共cmd命令行都考虑了_batch-file_语义( %%i而不是%i作为循环变量,可以逐字转义%作为%% ),也许总是使用辅助。 Windows_上的批处理文件(虽然在Unix上坚持使用CLI)是更好的解决方案-我对此不太满意,但是必须对其进行明确记录,特别是考虑到它表现出与其他脚本语言不同的行为,则尤其如此。

@rkeithhill

对我来说,您链接到的本地错误集成RFC与#1995一样值得紧急解决:

两者都表示在PowerShell中使外部程序(本机实用程序)成为一流公民的必要步骤(在概念上尽可能多),它们应该一直如此。

成为头等公民意味着:_直接调用(仅出于&语法原因而需要
换句话说:永远不应有Invoke-NativeCommand cmdlet或Start-NativeExecution cmdlet。

(顺便说一句: Start-NativeExecution被错误命名,因为build模块中的许多函数都是错误的; Start表示_asynchronous_操作,而大多数这些函数是_synchronous_-请参阅有关的讨论Start-Sleep ,网址为https://github.com/MicrosoftDocs/PowerShell-Docs/issues/4474)。

因此, Invoke-NativeShell不需要特殊考虑:PowerShell(已经这样做)将根据本机外壳程序可执行进程报告的退出代码设置$LASTEXITCODEcmd / sh )被调用。

然后,RFC中建议的机制将对其起作用,就像对直接调用的可执行文件起作用一样(例如,如果设置了$PSNativeCommandErrorAction = 'Stop' ,则如果$LASTEXITCODE非零,则会发生脚本终止错误。 )

(简单来说-此讨论不属于这里:至于_per-call_机制:类似
/bin/ls nosuch || $(throw 'ls failed)/bin/ls nosuch || $(exit $LASTEXITCODE) (相当于POSIX shell的/bin/ls nosuch || exit )一样有效; #10967讨论了为什么我们不幸地不能避免$(...) (语法没有重大变化); 同样,可能无法避免$LASTEXITCODE显式引用

(顺便说一句:使用shebang行#!/bin/sh创建shell脚本意味着通过完整路径来定向系统shell,就像直接调用/bin/sh 。)

那不是我想说的。 我想说,PowerShell不需要知道默认外壳程序是什么,因为操作系统内置了此功能。Linux知道如何执行可执行脚本,以防它不是以#!开头或以。其他东西,例如#!/usr/bin/env python ,Windows知道如何调用.CMD脚本,因此恕不偷看%COMSPEC% 'n'stuff恕我直言。 Windows也可以执行其他脚本,但是它基于脚本文件的扩展名,因此显然不适用于这种情况。

同样, Invoke-NativeShell的任务应该是调用本机外壳程序的_CLI_,而不是在幕后创建辅助脚本文件(具有副作用)(除非出于不同的原因,如上所述)。

关于可靠地确定系统外壳,这里没有要解决的问题:在Unix上对/bin/sh进行硬编码是完全合适的,在Windows上咨询$env:ComSpec也是完全合适的。

不,在系统调用级别的Unix平台上,不知道如何执行可执行的纯文本文件而没有任何影响。 您仍然可以_can_调用此类文件是类似于POSIX的shell的便利功能:它们尝试exec ,然后_fall_解释为此类文件。 也就是说,无论运行的是哪种类似于POSIX的外壳,最终都会执行该文件-而PowerShell只是_fails_,因为它没有实现这种礼貌的后备(您将获得Program 'foo' failed to run: Exec format error )。

不用说,因此创建这样的脚本是不明智的。

不,在系统调用级别的Unix平台上,不知道如何执行可执行的纯文本文件而没有任何影响。 您仍然可以_can_调用此类文件是类似于POSIX的shell的一个便利功能:它们尝试exec ,然后_fall_解释为此类文件。 也就是说,无论运行的是哪种类似于POSIX的外壳,最终都会执行该文件-而PowerShell只是_fails_,因为它没有实现这种礼貌的回退(您将获得Program 'foo' failed to run: Exec format error )。

csh不是POSIX样的,它不会在exec裸脚本上失败。 它调用shperl

我希望这很明显地表明了我的意思:是由每个shell_来决定要做什么,并且不同的shell /脚本语言可以做不同的事情。 PowerShell当前仅会失败-如果必须做出选择,并且您希望该选择为/bin/sh ,那么我们又回到了平方:必须假定/bin/sh

我希望这很明显地表明了我的意思:是由每个shell_来决定要做什么,并且不同的shell /脚本语言可以做不同的事情。 PowerShell当前仅会失败-如果必须做出选择,并且您希望该选择为/bin/sh ,那么我们又回到了平方:必须假定/bin/sh

exec中的perl是直接系统调用, perl不会以任何方式对其进行修改。 特别是,它不会选择解释器。

当然,这假定用户知道单引号(verbatim)和双引号(插值)字符串之间的区别以及关于选择性`转义$ ,但是我认为这很重要(高级)选项。

也许只是我一个人,但我会用一千"来交换一千-f

@ yecril71pl

perl中的exec是直接系统调用,perl不会以任何方式对其进行修改。 特别是,它不会选择解释器。

如果它可以与没有#!行的脚本一起使用,那将是不正确的。 没有#! ,Linux上的exec syscall不能工作,因为可以轻松地测试:

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

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

  execl(binaryPath, binaryPath, NULL);

  perror("Error");

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

编辑:我刚刚注意到,如果您使用库函数execlp则根据手册页:

如果无法识别文件的标头(尝试的execve(2)失败,并显示错误ENOEXEC),这些函数将使用文件的路径作为第一个参数执行shell(/ bin / sh)。 (如果此尝试失败,则不会进行进一步的搜索。)

但是,这不是gnu c库的“ linux”功能-再次根据手册页,它明确使用/ bin / sh而不是一些用户可定义的shell。 (在posix/execvpe.cnew_argv[0] = (char *) _PATH_BSHELL; )和sysdeps / unix / sysv / linux / paths.h (或sysdeps / generic / paths.h的源中进行posix/execvpe.c硬编码-我不知道哪个标头是使用...)( #define _PATH_BSHELL "/bin/sh" ))

但是,这不是gnu c库的“ linux”功能-再次根据手册页,它明确使用/ bin / sh而不是一些用户可定义的shell。 (在posix/execvpe.cnew_argv[0] = (char *) _PATH_BSHELL; )和sysdeps / unix / sysv / linux / paths.h (或sysdeps / generic / paths.h的源中进行posix/execvpe.c硬编码-我不知道哪个标头是使用...)( #define _PATH_BSHELL "/bin/sh" ))

由于它来自sysdeps/**/linux ,因此它是Linux。 它也可能是通用的,但它是可重写的-显然不是本地的,而是每个OS的。

是的,我了解_current_行为@ vexx32 。 但是我们(至少我不是)不是在谈论--%的_current_行为,而是在谈论增强它,不是吗? 为什么我感觉到我们在这里互相交谈? :)

因此,当前调整--%是,如果它通常是命令名所在的位置,则它右边的所有内容都会被_as is_解析(直到新行)并直接发送到bash / cmd。 没有任何PowerShell语法,例如here-strings,没有什么空间,因为那样它就具有--%和本机命令处理器当前存在的所有相同问题。

哦,洛迪...每个人都只是想欺骗我吗? 😄不,帕特里克-如果要使用Powershell表达式,请在此处使用--%字符串。 如果要多行,请使用here-字符串。 如果您希望多行没有变量替换,则可以使用单引号here-字符串。

无论如何,我在这个游戏中没有皮肤,我想我已经说够了。 我仍然认为使用cmdlet从shell内部推迟到另一个shell确实存在严重错误。 只是笨拙。 当您使用cmdlet执行另一个Shell来执行本来不需要该辅助Shell的本机命令时,这尤其笨拙。 整个事情都闻起来很难闻。

编辑:最后一句话

此项的标题是“呼叫本地操作员”。 当我们已经有一个调用运算符& ,并且已经有了一个令牌,该令牌旨在处理将参数传递给本地命令--%时,我看到了一个浪费的机会。 现在,无法识别将&--% ,因此如果我们决定启用此方案并给予其特定的行为来解决此处讨论的问题,则不会带来重大变化。

如果要使用Powershell表达式,则将here-string与--% 。 如果要多行,请使用here-字符串。

运算符的要点是它实际上不会解析任何PowerShell语法。 没有这里的字符串。 没有单引号字符串常量之类的东西。 它是“按原样向右发送文本”,而不是“向右评估表达式,转换为字符串并发送其值”

因此,例如,如果您有:

--% @'
echo my native command
'@

它将转换为:

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

除非您会收到解析器错误,因为'@只是包含@的单引号字符串常量的开头。

我认为您可能会以不同的方式描述实现,因此它与ins的音调大致相同,但是以上是--%的当前音调。

还值得注意的是,这也是现有功能的工作方式。 此示例的工作方式大致相同:

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

我重复:

现在,不能识别/未定义&与-%的配合使用,因此,如果我们决定启用此方案并赋予其特定的行为以解决此处讨论的问题,则不会带来重大变化。

让我更清楚地说明:与&一起使用时,允许这里的字符串跟随--% &

嗯,这真是个大问题-是的,我错过了,对不起@oising。

关于这个想法,我认为让--%意味着在某些情况下停止解析而在其他情况下“在原生参数绑定方面做得更好”会有点让人困惑(同样,使它像Invoke-NativeShell如果这是您的意思)。

感谢您深入研究@TSlivede exec 。 让我尝试总结一下,因此希望能切合这个切线。 适用于Linux和macOS:

在Unix上,没有名为exec系统(或库)函数,只有_family_相关函数的名称中有exec (其中大多数作为前缀)。

  • _system_函数( man2 ), execve的核心,_fail_,当您尝试执行无shebang的脚本时。

  • 在系统函数_上构建的_library_函数中( man3 - man 3 exec ),只有那些具有p函数(用于“路径”,我假设)具有传递到系统外壳的备用地址(例如execlp )。

    • 这些功能的GNU库实现将/bin/sh硬编码为@TSlivede所示,而基于BSD的仅使用sh ,并依赖sh $env:PATH (尽管,奇怪的是, man页面显示/bin/sh )。

如前所述,对于直接执行而言,类似POSIX的主要shell( bashdashkshzsh )可以回退到执行无shebang脚本_themselves_,这意味着它们不会在exec库函数中使用回退变体; 相比之下, perl选择自己的exec函数是依靠后备; 类似地,除了bash之外,类似POSIX的主要shell的exec _builtins_依赖于后备。

由于在Unix上不需要在后台使用脚本文件,因此我们可以对系统外壳路径做出与后备库函数相同的假设,我建议/bin/sh胜过sh ,安全性和可预测性:

尽管POSIX规范_not_强制要求sh (也就是说,严格来说BSD库函数是兼容的):

  • 可以肯定地假定/bin/sh ,因为它是_de facto_的标准位置,尤其是因为编写便携式Unix shell _necessitates_在其完整路径上引用了sh ,因为shebang行仅支持_full,路径( #!/bin/sh )。

  • 相反,如果可以操纵$env:PATH调用另一个sh ,则依靠$env:PATH来定位sh可能会带来安全风险。

顺便说一句,通过$env:ComSpec cmd.exe通过$env:ComSpec定位cmd.exe也有风险,所以也许更好的方法是调用GetSystemDirectory WinAPI函数并将\cmd.exe附加到其结果(如果未定义$env:ComSpec ,则无论如何都需要回退)。

可以假定/bin/sh是安全的,因为它是_de facto_的标准位置,尤其是因为shebang行仅支持_full,literal_,因此编写便携式Unix shell _necessitates_时以完整路径引用sh 。路径( #!/bin/sh )。

通常,您说#!/usr/bin/env sh (系统脚本中除外)。

定位名为sh的可执行文件的唯一原因是可移植地定位最低公共符号-假设-POSIX功能-仅_system_ shell,即/bin/sh

通常,您不要:谷歌搜索“#!/ bin / sh”产生大约3,900,000个匹配项, “#!/ usr / bin / env sh”产生大约34,100个匹配项。

没有办法将Web搜索限制为用户脚本,尤其是因为许多用户脚本根本没有标记为可执行文件,即使它们是可执行文件,它们也可能依赖execlp ;。 但是即使大多数用户脚本都这么说,我们也不应将_customary_用作_normal_。 PowerShell所运行的脚本是用户脚本。 当用户需要外壳程序时,他们会叫sh ,而不是/bin/sh ,除非他们偏执。

@oising

将本机外壳程序命令行_作为单个字符串_(以最简单的字符串(文字)形式)传递,并将_only_作为单个字符串(保存Unix上支持的其他命令行传递参数(仅)上面讨论的内容)-假设您同意-听起来像是共同点。

作为_single string_传递命令行是以下条件的前提:

  • 能够将这样的调用合并到_PowerShell_管道中。
  • 能够将_PowerShell变量和expression_合并到本机Shell命令行中。

传递单个字符串是调用将进行的直接概念表达。 相比之下,尝试通过_individual_(可选地,裸字)参数直接将不同的Shell语法直接合并到PowerShell中会带来无法解决的问题,导致存在基本限制的混乱折衷,现有的--%已经体现出来。


尽管您可能会争辩说,然后选择是将_operator_还是_cmdlet_传递给单字符串命令行(对于_discoverability_的问题除外)并不重要,但我对&使用存在概念上的担忧--% (在& ):

  • 关心的是& :如果我们需要传递一个包含命令行的带引号的字符串,那么我们实际上是在_expression_模式下运行,而&当前意味着_argument_模式(这意味着: _individual_参数,仅在必要时引用)。 因此, &的参与令人困惑。

  • --%的关注点在于仅Windows的起源和--%作为停止解析符号的混乱语义; 如果将--%作为停止解析符号以_argument_模式进行操作(例如,
    & /bin/ls --% dir1 dir2 ),而& --%以_expression_模式运行(例如& --% '/bin/ls dir1 dir2 >out.txt'


让我退后一步,检查--% (停止分析符号),当前已实现:

这是试图解决两个最终不相关的问题(当时仅Windows):

  • (a)一个也适用于其他平台的用例:让我按原样重复使用以cmd.exe /本机shell编写的_commandlines_,所以我不必使它们适应PowerShell的语法。

  • (b)始终是仅Windows的问题:让我调用_rogue CLI_(即对_single_外部控制台应用程序的调用),它需要一种非常特殊的引用样式,而PowerShell的通用幕后重新引用无法提供,通过允许我为最终的CLI _verbatim_设计最终传递给WinAPI的命令行。 (将问题陈述限制为此假设是,PowerShell的重新报价通常可以正常运行,而从来没有过,这是#1995的主题)。

--%被实现为(a)和(b)的解决方案尝试的混乱_conflation_,最终导致无法正确解决这两个问题:

  • 它被宣传为(a)的解决方案,但实际上有严重的局限性,因为_唯一可模拟的cmd exe功能是%...%样式环境变量引用的扩展。

    • 考虑到| ,它只支持_single_ cmd命令
      &&|| (即使当时尚未实现)也隐式终止了传递给cmd部分。

    • 一个_ & -这是cmd的多命令分隔符-传递了_is_,但是_not_也导致了多命令支持,因为-因为cmd是' t实际上涉及到-将& _verbatim传递给目标program_; 例如
      echoArgs.exe --% a & b不回显a然后调用b ,它传递逐字参数a&bechoArgs

    • cmd不带引号的转义字符( ^ )不被接受。

    • 必然地,引用现有环境变量的%...%令牌被_不变地扩展; 试图通过^阻止该操作不正常(例如
      echoArgs.exe Don't expand %^USERNAME% ,在cmd可防止扩展_并去除^ _,在从PowerShell调用时保留^作为echoArgs.exe --% Don't expand %^USERNAME%

  • 作为(b)的解决方案,它也没有达到:

    • 就像(a)中的用法一样,无法将_PowerShell_变量和表达式合并到命令中-除非您笨拙地首先定义_aux。 您随后在--%之后通过%...%引用的environment_ variable_; 例如
      $env:FOO='foo'; echoArgs.exe --% %FOO%!

如(a)所述,正确的解决方案应该是Invoke-NativeShell / ins

(b)的正确解决方案是:

  • 支持--%仅作为_first_参数特殊
  • 就像ins ,要求它后面跟着一个单个string_,它构成了整个传递命令行_整体_,并且还具有通过字符串插值的方式合并_PowerShell_变量和表达式的能力(或构造字符串的其他方式)
  • 例如msiexec --% "/i installer.msi INSTALLLOCATION=`"$loc`"" ,其中$loc包含'c:\foo\bar none' ,将导致逐字逐行命令行/i installer.msi INSTALLLOCATION="c:\foo\bar none"通过,满足msiexec的非标准要求在<prop>=<value>参数中,仅将<value>部分加双引号。

尽管这不是_convenient_,但要为无政府状态提供可靠的解决方案是要付出的代价,该解决方案是在Windows上传递的基于命令行的参数。

@ mklement0感谢您耐心而周到的答复(与往常一样)。

因此,我完全同意;

(b)的正确解决方案是:

  • 支持-%仅作为第一个参数是特殊的
  • 就像ins一样,要求它后面跟随一个字符串,该字符串构成整个传递命令行,>-再次具有通过字符串插值(或其他方式)合并PowerShell变量和表达式的功能构造字符串)

    • 例如msiexec-%“ / i installer.msi INSTALLLOCATION = "$loc ”“,而$ loc包含'c:foobar none',将导致逐字逐行命令行/ i installer.msi INSTALLLOCATION =” c:foobar无”,满足了msiexec的非标准要求=仅争论部分用双引号引起来。

这就是我一直试图作为对(a)和(b)的广义解决方案进行阐述的方式,除了我仍然没有真正意识到(a)应该作为一个独立的问题解决吗? 我真正挣扎的是如果(b)是针对& _specifically_实现的,那为什么还不能涵盖(a)? 例如, ins ...& cmd --% ...什么? 除了不允许&传递--%作为参数(这似乎不太可能,这不是什么大改变)的好处是,您不必担心comspec,shell管他呢。 让呼叫者决定。

@ PowerShell / powershell-committee对此进行了审查:

  • 这将是获得实际使用情况反馈的实验功能; 我们仍然对实际的消息持开放态度; 我们将/bin/sh/bin/env sh推迟到PR审查
  • 我们将继续使用--%作为新的运算符,专门用于“ stackoverflow”场景,在该场景中,用户将命令行剪切并粘贴到PowerShell中,并且该命令行应该可以正常工作(当然,在它前面有新的运算符)
  • Invoke-NativeShell cmdlet与该建议分开,可以由社区在PowerShellGallery中发布; 同样,要求here-string不能解决用户体验问题,而无需提前知道将其包装为here-string
  • 我们不希望对--%开关进行重大更改,而现有用户可能会依赖该行为
  • 需要像--%开关这样的流行为的用户应继续使用该开关并在必要时转义特殊字符
  • 用户仍然需要了解--% (或任何解决方案)的发现问题
  • 我们目前不接受对PSReadLine的更改,以将粘贴的内容修改为此处字符串以试图预测用户意图
  • 我们没有在考虑允许在此处字符串和非此处字符串(扩展)方案的多种解决方案; 例如& --%--%

我还想补充一点,尽管@ SteveL-MSFT和我在打开此代码的同时关闭了#1995,但实际上,我们的意图并不是要传达此信息作为解决该问题的最终方法。

我认为,此特定功能的好处比#1995的影响要重要得多。 我认为对于非PS开明的工具,有很多StackOverflow示例和docs示例,人们希望剪切/粘贴(包括我自己)。

另外,还希望在PowerShell自己的语言中提供适当的转义,但是我没有看到人们在狂野人群中一跃成为1995年(但我将在那儿详细说明)。

  • 我们将继续以--%作为新运算符

抱歉,将其拖出,我不需要其他讨论,但是什么样的操作员呢? (我主要对语法感兴趣。)

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

谁接管? $怎样? 与&&&||类似。

在什么条件下(如果有), --%可以在前进的PS管道中使用吗?

谢谢。

抱歉,将其拖出,我不需要其他讨论,但是什么样的操作员呢? (我主要对语法感兴趣。)

AST明智的做法是,从技术上讲它可能不是运算符,但它是AST类型的。 如果确实将其实现为运算符,则它将是一元运算符。

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

谁接管? $怎样? 与&&&||类似。

字符串find . -iname *$pdf -print0 | xargs -0 ls -ltr将直接发送到本机shell。 虽然$a仍将被填充,所以您可以在下一行中将其填充到某些内容中。

在什么条件下(如果有), --%可以在前进的PS管道中使用吗?

如果〜在第一个命令元素槽中〜在语句的开头,可能不是。 那是新语法的吸引力之一。 如果不是在开始的第一个广告位中,它将像今天一样继续工作。

(@ daxian-dbw @ SteveL-MSFT如果有任何错误请更正❤️)

@SeeminglyScience完全正确

如果在第一个命令元素插槽中,可能不会。 那是新语法的吸引力之一。 如果不在第一个插槽中,它将像今天一样继续工作。

将上游命令的输出传递给本机命令可能会比较棘手,本机命令将由--%调用。 --%基本上只是将字符串传递给本地shell,例如bash ,但是上游命令的输出不能仅馈送到bash ,而应该馈送到本机命令本身,例如find

我认为流行为不是对此功能的考虑,因为它主要用于“ StackOverflow”场景。 因此,我认为我们最好只对--%一个特殊的声明ast,这样很显然它不适用于_PowerShell_管道。

啊,我知道“命令元素插槽”是错误的原因。 我的意思是在声明开始时。 我会解决的,谢谢!

上游命令的输出不能仅馈送到bash

_upstream_是_us_吗? 我认为可以做到: 'ls' | bash 。 这不是一件特别聪明的事情,但有可能。

@jpsnover讨论此事时,他提出了shebang #!作为标记。 在这种情况下,您只需输入:

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

(在第二种情况下,假设sh在路径中)。 在这种情况下,我们必须决定是否需要指定外壳程序,或者是否总是在默认外壳程序下运行该外壳程序,因此在此示例中, sh被启动两次。 但是,由于shebang文件今天的工作方式,因此存在问题。 它们在PowerShell中工作,因为shebang被忽略为注释。 如果我们决定忽略脚本中的第一行(如果有注释的话),那么在交互式外壳程序中我们仍然会遇到问题,其中每行新的行都是一个新脚本,因此今天无法确定该脚本是否作为一个脚本运行。脚本或交互式。

另一种选择是借用markdown语法:

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

在此示例中,我们将使用降价代码防护语法。 这也将解决多行问题,并使用不完全是新概念。 鉴于我们仍然对新用户有最初的发现问题,因此我认为这并没有那么糟糕,因为您需要拥有跟踪的三重反引号。

在这种情况下,这可能会起作用:

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

@ SteveL-MSFT Markdown语法很有意义。 如果我们能够指定。 其他类型,例如python还是扩展名?

社bang#! 会干扰现有评论。

这对我来说似乎过于复杂,既要处理解析又要让用户实际使用。

是的,我们不需要将人们与不应成为评论的评论混淆。 #Requires已经有点怪异了,shebang上的全部内容充其量会使Windows人员充其量也可能使Linux人员困惑。

我个人更希望不要将此功能与shebang混合使用,在我看来这很令人困惑。
另外,如果我们开始使用markdown代码块语法,那么人们要求直接在PowerShell中运行任意语言代码只是时间问题。 我不认为我们想那样走...

另外,如果我们开始使用markdown代码块语法,那么人们要求直接在PowerShell中运行任意语言代码只是时间问题。

尽管我知道这是一个坏主意,但我从不停止要求内联C#。

markdown语法不会引入任意语言代码,因为嵌入式脚本将在子进程中运行。 您现在可以将任意语言代码放在@'并且不会发生任何不良情况。

在团队讨论中,以及在与Jeffrey的讨论中,我们都不应反对想要在PowerShell脚本中运行其他语言的人们。 正如@ yecril71pl指出的那样,我们不直接支持其他语言,而是依靠不同的过程来解释和执行该代码。 这里的场景是用户看到一个python或bash脚本块,而只想在PowerShell脚本中按原样使用它。 没有什么要求人们这样做。 如果愿意,他们可以决定将其他脚本移植到PowerShell。 这只是为用户提供了一个选项,可以将其剪切并粘贴到PowerShell中以完成工作。

我们不需要混合单行和多行建议,因为我们可以同时支持这两种建议,尽管不利的是现在我们有两种方法可以做非常相似的事情,但是语法不同。

我希望删除单行内容。 外来代码孤岛应该突出显示,否则没人会明白发生了什么。

@ yecril71pl

在团队讨论中,以及在与Jeffrey的讨论中,我们都不应反对想要在PowerShell脚本中运行其他语言的人们。

绝对是100%,但这并不意味着它需要直接内置到语言中。

正如@ yecril71pl指出的那样,我们不直接支持其他语言,而是依靠不同的过程来解释和执行该代码。 这里的场景是用户看到一个python或bash脚本块,而只想在PowerShell脚本中按原样使用它。

使用这里字符串已经可以做到这一点,对吧? 您能否详细说明与现有方法相比有什么好处?

还有为什么要停在python和bash上呢? 为什么不使用C#,CIL,C ++?

没有什么要求人们这样做。 如果愿意,他们可以决定将其他脚本移植到PowerShell。 这只是为用户提供了一个选项,可以将其剪切并粘贴到PowerShell中以完成工作。

我并不反对,因为我认为我将被迫使用它。 我不喜欢它,因为从语言的角度来看,这将很难维护,这对于编辑人员来说是一场噩梦,最终会鼓励除非您会几种语言,否则这些脚本是不可读的。


确实,尽管这是与本主题的原始主题截然不同的概念,但含义要广泛得多。 我建议为此创建一个新问题。

使用这里字符串已经可以做到这一点,对吧? 您能否详细说明与现有方法相比有什么好处?

通用代码编辑器将能够检测到它们并适当地修饰它们,而@'则无济于事,因为该编辑器不知道里面有什么。

还有为什么要停在python和bash上呢? 为什么不使用C#,CIL,C ++?

因为它们不是脚本语言?

通用代码编辑器将能够检测到它们并适当地修饰它们,而@'则无济于事,因为该编辑器不知道里面有什么。

编辑者可以说bash -c @'包含bash就像代码围栏一样容易。 同样具有挑战性的部分不是确定代码段是什么语言,而是在混杂语言服务器和语法突出显示工具。 无需花费大量时间和精力就可以运行的唯一方法是从本质上使其像这里字符串那样进行渲染。

因为它们不是脚本语言?

C#和C ++都有面向脚本的子集。 即使没有,“脚本语言”也是主观的,没有真正的定义。 作为确定将包含哪些内容和不包含哪些内容的明确界限,这没有帮助。

C#和C ++都有面向脚本的子集。 即使没有,“脚本语言”也是主观的,没有真正的定义。

当您通常调用interpet options… script arguments… ,该语言是一种(独立的)脚本语言,该调用只保留原定要保留的内容,不包括临时内容和解释器的私有内容。 这也意味着通常需要运行解释器的副本。 有些嵌入式脚本语言无法以这种方式运行。

@SeeminglyScience

使用这里字符串已经可以做到这一点,对吧? 您能否详细说明与现有方法相比有什么好处?

人们今天可以使用此处字符串,或者如果他们能弄清楚如何正确使用它们,则可以转义传递给本机命令的所有参数。 这里的目的是使新用户在剪切和粘贴(Stackoverflow)场景中更简单。

还有为什么要停在python和bash上呢? 为什么不使用C#,CIL,C ++?

这里支持的方案是传递给本机命令的文本块。 如果支持该调用约定,则可以支持任何语言。 没有建议创建一个临时源文件并对其进行编译。

我并不反对,因为我认为我将被迫使用它。 我不喜欢它,因为从语言的角度来看,这将很难维护,这对于编辑人员来说是一场噩梦,最终会鼓励除非您会几种语言,否则这些脚本是不可读的。

不会出现混合语言语法着色的情况。 来自另一种语言的任何嵌套脚本都将被单调。

确实,尽管这是与本主题的原始主题截然不同的概念,但含义要广泛得多。 我建议为此创建一个新问题。

拟议的实现与原始问题不同,但是问题是相同的,即剪切和粘贴以及“正常工作”方案。

人们今天可以使用此处字符串,或者如果他们能弄清楚如何正确使用它们,则可以转义传递给本机命令的所有参数。 这里的目的是使新用户在剪切和粘贴(Stackoverflow)场景中更简单。

是的,但是实际上更容易吗? 我知道这是主观的,但是这些对我来说似乎并没有什么不同:

~~~ powershell
$ a =```bash
找 。 -iname * $ pdf -print0 | xargs -0 ls -ltr

~~~

vs

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

它们似乎几乎相同,除了后者在即将发生的事情上更加清楚。

这里支持的方案是传递给本机命令的文本块。 如果支持该调用约定,则可以支持任何语言。 没有建议创建一个临时源文件并对其进行编译。

好吧,C#不需要临时源文件。 也许也不是CIL(对C ++不了解)。 不管哪种方式,严格来说,代码围墙都是用于调用可执行文件(例如bash / python / cmd)的语法糖,这对我来说都很困惑。 代码围栏暗示语言支持imo。

不会出现混合语言语法着色的情况。 来自另一种语言的任何嵌套脚本都将被单调。

对于基于tmLanguage的解析器,这仍然是一场噩梦。 他们几乎无法处理他们期望atm的语法。 不仅如此,如果是这种情况,我不明白为什么它与这里的字符串不同。

拟议的实现与原始问题不同,但是问题是相同的,即剪切和粘贴以及“正常工作”方案。

公平,但是我们已经有145条评论讨论先前提出的解决方案。 特别是由于该线程已经结束,您可能会发现更多地参与了新的专用线程。

好吧,C#不需要临时源文件。 也许也不是CIL(对C ++不了解)。 不管哪种方式,严格来说,代码围墙都是用于调用可执行文件(例如bash / python / cmd)的语法糖,这对我来说都很困惑。 代码围栏暗示语言支持imo。

我只想回应@SeeminglyScience对此的评论。 我们可以运行C#脚本,而无需使用Microsoft.CodeAnalysis.CSharp.Scripting库进行编译。 这就是C#Jupyter内核中使用dotnet-interactive的方式。 因此,用户要求使用这种语法的C#脚本甚至F#脚本支持可能并非不合理。

您可以说PowerShell中的代码防护语法是用于调用本机命令的,理论上这允许这样做:

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

但是这与用户对markdown的理解有所不同,正如@SeeminglyScience所说,这意味着语言支持。 在我看来,使用具有不同语义的相似语法会造成混淆。

理论上允许

您将脚本而不是参数放在其中,并且脚本等效于输入流。 同样,如果这是为了提供SO支持,则不应允许完整路径。

您将脚本而不是参数放在其中,并且脚本等效于输入流

该脚本是bash / cmd / python的参数。

同样,如果这是为了提供SO支持,则不应允许完整路径。

然后切换此示例:

〜powershell$ a = ipconfig /all

或先将任意exe添加到路径。

您将脚本而不是参数放在其中,并且脚本等效于输入流

该脚本是bash / cmd / python的参数。

它们都不允许将脚本作为位置参数传递。 CMD甚至不允许批处理文件。

同样,如果这是为了提供SO支持,则不应允许完整路径。

然后切换此示例:

$ a =```ipconfig
/所有

那行不通。

似乎我们会在非常任意的基础上做出很多特殊情况,以使这个降价想法能够起作用。

同样,Markdown甚至支持此功能的主要原因是语法突出显示。 如果我们在语言中添加类似的内容,那将是一个不断的要求。

这不是有效的解决方案。

在与@jpsnover讨论此事时,他提出了shebang #!作为标记

仅加2美分,我认为在宣布解决方案后重新开始讨论是错误的。 我个人不喜欢shebang和markdown语法(而且都在改变中)。

如果有人提出了明显更好的建议,我不介意重新开始讨论。 确实,现在比在发布该功能之后更好。 我只是认为#!或markdown方法不是向本机exe提供“ un-PowerShell-adulterated”命令行字符串的更好的解决方案。 也许我只需要尝试一下就可以适应它,但是我的最初反应是...嗯,嗯。

_如果我们(和PowerShell)知道我们运行的是本机可执行文件,为什么我们完全需要“调用本机运算符”?

如果PowerShell解析输入字符串并获取CommandAst,则PowerShell会发现该命令。
如果命令是本机命令,PowerShell会将ast转换为NativeCommandProcessor。
对于这种情况,我们可以实现新的(实验性NativeCommandProcessor),它将重新解析Ast范围(即输入字符串),以便获得本机可执行文件的名称/路径和其余字符串作为OS规则的自变量。

如果用户要从shell复制粘贴,则应输入“ cmd”或“ bash”-无需编辑即可使用。
如果用户要复制粘贴本机命令行也会像g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11

如果用户想要从shell复制粘贴

我们有Get-/Set-Clipboard

对于将重新解析Ast范围(即输入字符串)的情况,以便获得本机可执行文件的名称/路径和其余字符串作为OS规则的自变量。

我的想法是,您可以这样做:

  • 无效地分析命令AST,只是稍后将范围重新解析为字符串并将原始AST丢弃,或者
  • 您将AST尝试将其重新构造为字符串(当前NativeCommandProcessor的方式),但是这样做会丢失一切

因此,我们需要从一开始就保留字符串。 现在,我认为可以说PowerShell已经拥有三种字符串令牌,但是本次讨论中的主要要求似乎是不想转义。 任何字符串都必须能够转义其终止符,但是那里讨论的解决方案似乎是使用换行符作为终止符,而不能转义换行符(总是存在分号)。

我并没有完全嫁接到仅换行符的终止符,但是它似乎确实是不希望逃脱任何东西的唯一解决方案。 基本上,这将使“本地调用运算符”标记化,类似于今天的行注释-从运算符到行尾是传递给“本地shell”的字符串。

所以:

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

将标记为:

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

(请注意,即使字符串在--%之后也直接发送到本机shell,因为字符串在--%之后立即开始)

然后,将此令牌包装在非常简单的AST中,例如:

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

然后,在将其编译并发送到管道之后,它使用新的本机命令处理器将字符串作为参数直接发送到本机外壳。

尽管这一切似乎都是可以实现的,但我想到的一些问题是:

  • 本机外壳是否可配置? 如果没有,我们如何证明这一点? 如果是这样,应将其作为语法的一部分(使语法复杂化)是显式的,还是作为偏好变量(使其难以发现和管理)实现?
  • 这样的实现是否会被发现并且不会使人们感到困惑? 用户是否会了解PowerShell在这里所做的工作很少,或者使用|&甚至字符串时会发生什么?
  • 这里的单行逐字字符串将为多少个场景提供服务? 我们是否打算执行此操作只是为了发现许多人想要多行调用? 是否需要避免像转义单引号这样的操作足以增加这些约束?
  • 对于全新的语言语法,这似乎是非常特定的场景。 如果要创建新的逐字字符串语法,为什么我们将其直接与本机调用的概念耦合? 如果我们要创建一个新的调用机制,为什么我们将其直接耦合到其他shell的概念上? 良好的语法和良好的语言构造可以在语法和功能上进行组合,并尽量避免过多地融入语言本身(而是为用户提供了一个平台,供用户在其程序中组装)–我们建议的本机调用语法是否符合标准将新的基本语法组成程序? 我们是否可以采用更好的替代方法,以一种简单的方式解决此问题,但仍然允许我们使构建基块保持不耦合状态,从而使它们能够组合起来以解决其他问题?
  • 其他shell或其他语言是否会实现此功能? 在语法级别还是其他级别? 如果是这样,怎么办?

我的想法是这样做

@rjmholt CommandAst具有带输入字符串的Extent属性。 重新解析就像在Windows上按空格分隔或在Unix上按空格分隔一样简单。 因此,我希望我们不会在字符串中丢失任何内容并获得良好的性能。

但这已实现,我认为我们需要一组用例进行测试。 让我“提出”以下建议:

  • 用例1:我希望在不知道引用/转义规则的情况下,执行单个exe文字(无插值)调用,例如: g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11 。 当前的解决方法是找出要引用的内容,例如: g++ timer.cpp '@conanbuildinfo.args' -o timer --std=c++11一个更复杂的示例可能包括跨多行的命令行,例如:
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 

当前解决方法(以\交换反引号),例如:

tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz `
    --exclude=/proc `
    --exclude=/lost+found
  • 用例1a:我想使用PS插值执行单个exe调用,例如: g++ $CppFile @conanbuildinfo.args -o timer --std=c++11 。 当前的解决方法是弄清楚要引用的内容(单引号或双引号)和要转义的内容,例如: g++ $CppFile '@conanbuildinfo.args' -o timer --std=c++11一个更复杂的示例可能包括跨多行的命令行,例如:
tar -cvpzf "/share/Recovery/Snapshots/${ComputerName}_`$(date +%Y%m%d).tar.gz" \
    --exclude=/proc \
    --exclude=/lost+found 

当前的解决方法,将第一个参数用双引号引起来,将反引号替换为\并在$(date + ...)开始时转义$

  • 用例2:我想在另一个shell中执行文字流水线命令例如: ps -o pid,args -C bash | awk '/running_script/ { print $1 }'wsl ls $foo && echo $PWD 。 _我不确定这是否需要与前一个用例分开。_当前的解决方法是引用bash命令,例如: bash -c 'ps -o pid,args -C bash | awk ''/-bash/ { print $1 }''' 。 该用例可能还包括多行字符串,例如:
ps -o pid,args -C bash | \
awk '/running_script/ { print $1 }'
  • 用例2a:我想用PS插值在另一个shell中执行流水线命令-这是用例吗?

  • 用例3:我需要npm run $scriptName才能继续工作,即对变量进行插值。 (这可能是隐式的,不需要说明)。

有一个问题是,是否应该为interpolating字符串使用用例,即g++ $CppFile @conanbuildinfo.args -o timer --std=c++11还是我们只是在上面打一点点,说当前的转义规则涵盖了这一点?

这些用例可能不是正确的(或全部)用例,但我认为记录使用例并在讨论此功能时可以参考它们会很有帮助,否则很容易迷失该功能的实际使用方式(或不使用) )客户用例。

另外,在我看来,第二个用例需要“ shell调用”。 不过我不确定第一个。 好像exe可以直接调用。

@rkeithhill您能否在上

@oising ,我也非常感谢您的周到答复。

ins ...& cmd --% ...什么?

--%的当前_parameter_(符号)形式-我想我们要保留其行为-无法用于cmd /c --% ,因为它不支持_multi-command_ cmd命令行(也不将^作为转义字符。),因为第一个未引用的|将被解释为_PowerShell's_管道运算符,并且&逐字传递。
这同样适用于Unix,其中--%通常近无用,用sh -c失败完全,因为sh期望命令线作为_single_参数(例如, sh -c --% echo hi产生一个空行,因为仅echo作为命令执行)。

ins ,它要求通过作为单个string_传递来定界传递命令行来解决这些问题,并且如上所述,还允许使用字符串插值来合并_PowerShell_变量和表达式值。

后者使我们了解了为什么建议的--% _statement_是_dead-end street_

  • 没有过渡路径,从完全按照is_执行预先存在的命令行到将_PowerShell_变量和表达式值合并到其中。

    • 实现此目的的唯一方法(晦涩且繁琐)是定义_auxiliary环境变量_,该变量存储感兴趣的PowerShell值,然后您可以在其中引用(适当地使用%...%$... shell引用)本机命令行。
  • 您无法将--%语句合并到更大的PowerShell管道中,这很尴尬。

    • 注意,有合法的_non_-cut-and-paste调用本机shell的理由,您可能希望在此进行集成。 具体而言,通过(中间)管道传递_raw字节数据_并在不带尾随换行符_的情况下发送_string要求使用本机管道-有关实际示例,请参见此SO答案
  • --% _ _parameter_在Unix上几乎没有用也很尴尬,这引入了一个奇怪的不对称性:如果--% _statement_也能在Unix上使用,为什么--% _parameter_ ? (例如,
    /bin/echo --% 'hi "noon"'逐字产生'hi noon' (而不是预期的hi "noon" ),这是由于echo接收了_two_参数,逐字'hinoon' ,其中单引号保留为数据,双引号被去除)。


ins作为--%语句的建议替代方案,理想情况下只是cmd /c _ sh -c左右的_方便包装器,但实际上是_required_:

  • 至少在Unix上是暂时的,只要#1995不固定,因为当前直接调用sh -c '...'不会正确地传递带有嵌入式"参数。

  • _Invariably_在Windows,因为你需要通过命令行_As,确保对方能到cmd通过lpCommandLine的参数CreateProcess (其中--%参数不能做由于限于_one_命令); 或者,由于#1995,尝试再次将命令行作为整体包含在"..."的_single string_传递而无法正常运行。


可以通过提供命令行来执行_via stdin_( ins可以并且应该支持)来避免_argument_传递的麻烦,即将_pipe_命令行/脚本文本提交给shell可执行文件:

  • 在Unix上,这工作得很好,因为通过_stdin_传递代码到sh基本上与传递_script file_来执行代码相同(就像sh -c '<code>' is ):
PSonUnix> @'
echo '{ "foo": "bar" }' | cat -n
'@ | sh

     1  { "foo": "bar" }
  • Windows,在Windows上cmd不能很好地处理这样的输入:它会打印其徽标并在执行前回显每个命令以及提示字符串:
PSonWin> 'date /t & ver' | cmd

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

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

Microsoft Windows [Version 10.0.18363.836]

C:\Users\jdoe>

可悲的是,PowerShell本身也表现出类似的无益行为:请参阅#3223


就像sh和所有主要的POSIX类外壳程序( dashbashkshzsh )一样,大多数脚本语言都可以正确地_do_支持stdin-as-script输入,例如pythonnoderubyperl

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

hi

为了合并_PowerShell_变量和表达式值,您还可以选择使用字符串插值/串联:

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

bar

或使用解释器的参数传递功能:

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

bar

我希望上面的说明很清楚,根本不需要特殊语法- --%语句, #!语句或Markdown代码块-所有这些都将再次缺少能够合并来自调用PowerShell脚本的值。

至于要使用的本机shell的可配置性:为了可预测性,我认为我们不应该提供这种配置。 想要定位到其他Shell的用户可以通过命令行将命令行传递到该Shell的特定可执行文件,或者在固定#1995之后,将其作为参数传递。

  • 一个有争议的变化是针对/bin/bash ((不太可能)回退到/bin/sh )-而人们希望已发布的命令行仅依赖POSIX要求的功能,带有Bash-isms的命令行(鉴于bash的普遍性,(并非所有/bin/sh实现都可以预期支持的非POSIX功能)很可能在那里。

@joeyaiello

我认为,此特定功能的好处比#1995的影响要重要得多。

我发现令人难以置信的是,我们在制作此剪切和粘贴功能上付出了最大的努力-主要是为了_interactive_便利-一个(尴尬的,功能有限的)一流公民,而PowerShell根本无法传递空白参数和嵌入了"字符的参数。

但我没有看到人们在狂野群众中达到#1995

我愿意,并且出于我先前讨论

再次,我认为像PowerShell当前所做的那样,执行以下操作的Shell存在严重问题

# On Unix
PS> /bin/echo '{ "foo": "bar" }'

{ foo: bar }

@rjmholt CommandAst具有带输入字符串的Extent属性。 重新解析就像在Windows上按空格分隔或在Unix上按空格分隔一样简单。 因此,我希望我们不会在字符串中丢失任何内容并获得良好的性能。

是的,我了解那里的内容,也许在那种情况下AST的分配并不可怕(尽管我认为该语言不应为简单的语法生成不必要的分配)。 但是您所要做的就是想象一下bash / cmd会将输入视为字符串并且PowerShell不会开始遇到问题:

无法使用当前的PowerShell进行解析:

--% my_command "\" "

解析,但按空格分割会产生错误的结果:

--% my_command "\"    "\"

(我们必须在字符串中发送4个空格,但是PowerShell看到两个字符串分隔在一个数组中)

@rjmholt上面

我已经在上一个注释中解决了有关使用哪个本机shell可执行文件的问题,但至于:

其他shell或其他语言是否会实现此功能? 在语法级别还是其他级别? 如果是这样,怎么办?

我不知道任何其他语言在不使用_explicit delimiters_并允许_interpolation_的情况下在语法级别完成了此操作。
例如, perl具有`...`用于运行shell命令,因此您可以在Unix上的脚本中运行以下命令,例如:
my $foo='/'; print `ls $foo | cat -n`

关键方面是使用显式定界符以及合并来自调用方的值的能力-两者在--%语句的当前建议中均缺失。

如前所述,我们_可以_将此方法用于新的_operator_(或某种基于定界符的,可选的插值语法),但鉴于此,我认为这不值得
ins "ls $foo | cat -n" (或带有here-string变体)将不会造成语言复杂性增加的麻烦。

perl提供了一个(适当的)语法级别的解决方案_,因为它本身并不是shell_,但是希望尽可能方便地进行shell调用。

相比之下,我们也是一个shell,并且直接提供shell样式的调用,而没有任何其他语法(尽管目前存在缺陷-请参阅#1995)。
提供特殊的语法以使_a不同的shell's_调用尽可能简单似乎是不必要的。

@iSazonov

_如果我们(和PowerShell)知道我们运行的是本机可执行文件,为什么我们完全需要“调用本机运算符”?

如果PowerShell解析输入字符串并获取CommandAst,则PowerShell会发现该命令。
如果命令是本机命令,PowerShell会将ast转换为NativeCommandProcessor。
对于这种情况,我们可以实现新的(实验性NativeCommandProcessor),它将重新解析Ast范围(即输入字符串),以便获得本机可执行文件的名称/路径和其余字符串作为OS规则的自变量。

如果用户要从shell复制粘贴,则应键入“ cmd”或“ bash”-无需编辑即可使用。
如果用户想要复制粘贴,则本机命令行也将像g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11

但是,等等,如果我们做到了所有这些,您将永远无法在本机命令中使用任何PowerShell语法吗? 因此,像sc.exe query $serviceName会传递$serviceName作为文字字符串吗?

但是,等等,如果我们做到了所有这些,您将永远无法在本机命令中使用任何PowerShell语法吗? 因此,像sc.exe query $serviceName会传递$serviceName作为文字字符串吗?

--%选项并非旨在解决该需求。

sc.exe query $serviceName&"<cmd>"

--%选项并非旨在解决这一需求。

sc.exe query $serviceName&"<cmd>"

是的,可以肯定的是,我不认为@iSazonov指的是什么。 我正在阅读,好像他们在建议对本机命令的参数绑定工作方式进行一般性更改。

是的,可以肯定的是,我不认为@iSazonov指的是什么。 我正在阅读,好像他们在建议对本机命令的参数绑定工作方式进行一般性更改。

主啊,我希望不会。

主啊,我希望不会。

有时候,当您集思广益时,很容易忘记显而易见的内容。 公平地说,我们所有人都读过它,并立即开始思考实现,而没有意识到它会破坏多少。

或者我遗漏了一些明显的东西,而这根本没有被提议😁

@essentialexch ,要清楚,重新:

sc.exe query $serviceName&"<cmd>"

& "<cmd>"仅在<cmd>计算为仅一个命令_name或path _,_不包括arguments_时才有效-参数必须单独传递-并且它们本身不需要"...."引用要扩展的变量引用(例如,
$exe = 'findstr'; & "where.exe $exe"设计失败; 必须为& "where.exe" $exe (此处双引号为可选;如果省略,则&也为可选))

是的,可以肯定的是,我不认为@iSazonov指的是什么。 我正在阅读,好像他们在建议对本机命令的参数绑定工作方式进行一般性更改。

我的建议是尽可能简单地修复#1995(有重大更改)。 原因插值不起作用。 原因不是最终提议-它演示了如何在几分钟内实现修复。 而且它主要演示了我们完全不需要@ mklement0指出的调用本机运算符。

解析,但按空格分割会产生错误的结果:

@rjmholt是的,但我认为您可以理解。
CommandAst的第一个孩子是StringConstantExpressionAst,其本机命令为BareWord。 我们可以从CommandAst Extent中删除裸词,并获取参数列表,而无需进行ast-s更改。 但是我们也可以添加新的ast来保留参数列表。
我花了一些时间进行解释,因为在下面我想总结一下讨论,我觉得我们将需要实现一些看起来很复杂的增强功能,但是我认为可以简单地实现它们,并且________________易于退出以实现向后兼容性__。


我相信@ SteveL-MSFT最初提案的意图是避免重大更改。
我同意@TSlivede@ mklement0的观点,该讨论表明不需要新的语法,因为它不能解决所有#fundamental_问题,例如#1995和#12975。 另一方面,它增加了语言的复杂性,相反,我们希望简化本机应用程序的工作。

__继续前进的唯一方法是进行重大更改或更多更改。__(之后,在某些情况下,新的运算符或cmdlet也可能会有用。)


我们应该从用例和用户期望的概述开始。
参见上面的@rkeithhill精彩文章

  1. 用户想要启用/禁用插值。
    PowerShell已经具有带单引号和双引号的字符串/此处字符串。
    我们的目标是了解如何应用/完善/增强它。
  2. 用户想要运行任何可执行文件或外壳程序

    • 任何可执行文件-命令以应用名称开头,并跟随参数

      • 有/无插值

      • 在单行/多行

    • shell可能是一种特殊情况,因为参数是_script_

      • 在复制粘贴方案中,用户不需要插值,而需要单行和多行

      • 在脚本方案中,用户希望启用/禁用插值,并且需要单行和多行

    想法:
    这使我们认为复制粘贴方案的行为应该是默认的。
    行终止符取决于可执行文件(在bash中,在PowerShell中为`)。 这使我们认为:
    (1)应明确将shell声明为任何可执行文件,
    (2)也许任何具有多行的可执行文件都应该通过shell调用,或者我们必须进行特殊的解析(以删除终止符并构建参数列表以回避转义问题)
    (3)我们可以解决PSReadline中的复制粘贴方案。 它可以将缓冲区转换为正确的PowerShell命令。

  3. 用户需要单行和多行。

    • 也许我们需要增强单行的此处字符串以解决前面讨论的某些情况。
    • 参见2-(2)-始终将shell用于多行或实现特殊的解析

我不想写更多的内容,因为@TSlivede@ mklement0可以从较早的时候更新他们的想法和结论,但是现在可以基于对突破性更改的接受。 我要求打开一个新的问题(并关闭所有旧问题),列举所有用例(以@ @rkeithhill开头并添加更多用例),并可能创建Pester测试-这是修复问题的重要一步。

我只想补充一点,我们可以考虑使用新的cmdlet来简化用户采用,例如Invoke-Shell(Invoke-NativeShell),Test-Arguments(例如echoargs.exe,以显示本机应用程序并为用户提供帮助),Convert-Arguments(将用户输入转换为右转义的参数)。

PS:正如我上面展示的,在运行时可以很容易地退出单行行为以实现向后兼容性。

行终止符取决于可执行文件(在bash中,在PowerShell中为`)。

\不是bash的行终止符。

原因插值不起作用。 原因不是最终提议-它演示了如何在几分钟内实现修复。 而且它主要演示了我们完全不需要@ mklement0指出的调用本机运算符。

如果我们删除存储库,就可以在几分钟内修复所有错误。 您建议的那是一个很大的突破。 即使只是假设,我也看不出它有什么帮助。

即使只是假设,我也看不出它有什么帮助。

@SeeminglyScience似乎我们处在不同的环境中。 我说我们可以将单行本机调用实现为一项重大更改,但又不破坏Parser。 换句话说,无论我们要实现什么新行为,我们都可以即时切换行为。

换句话说,无论我们要实现什么新行为,我们都可以即时切换行为。

我想我所缺少的是如何在不明确要求的情况下即时切换该行为(例如使用调用本机运算符)。

@iSazonov

_如果我们(和PowerShell)知道我们运行的是本机可执行文件,为什么我们完全需要“调用本机运算符”?

如果PowerShell解析输入字符串并获取CommandAst,则PowerShell会发现该命令。
如果命令是本机命令,PowerShell会将ast转换为NativeCommandProcessor。
对于这种情况,我们可以实现新的(实验性NativeCommandProcessor),它将重新解析Ast范围(即输入字符串),以便获得本机可执行文件的名称/路径和其余字符串作为OS规则的自变量。

如果用户要从shell复制粘贴,则应键入“ cmd”或“ bash”-无需编辑即可使用。
如果用户想要复制粘贴,则本机命令行也将像g++ timer.cpp @conanbuildinfo.args -o timer --std=c++11

我想确认一下这是什么意思:您是否建议在调用外部可执行文件时实质上忽略所有Powershell语法?

具体来说:您是否建议,例如运行

/bin/echo (1..5)

在powershell中将不再输出1 2 3 4 5而应输出文字(1..5)

如果这实际上是您的建议,那么我不得不说:我认为这是一个糟糕的主意。

但这并没有简化事情(外部可执行文件现在的语法与内部cmdlet在

@rjmholt
上方

  • 其他shell或其他语言是否会实现此功能? 在语法级别还是其他级别? 如果是这样,怎么办?

我唯一想到的就是ipythons !运算符

在ipython输出中运行!ps -'fax' || true

  261 pts/0    Sl     0:00          \_ /usr/bin/python /usr/bin/ipython
  311 pts/0    S      0:00              \_ /bin/bash -c ps -'fax' || true
  312 pts/0    R      0:00                  \_ ps -fax

ps不会在参数周围显示引号,但是ps -'fax' || true实际上是作为bash的单个参数给出的。)

但是,此功能仍允许将python变量插值到该命令( {pyvar} )中。

@ mklement0示例类似,ipython仅实现了此功能

因为它本身不是外壳

如果ipython只允许使用python语法,那么它作为shell并不是很有用,因此它们允许该语法将命令转发给bash以用作shell。 另一方面,Powershell声称是外壳程序(直到1995年解决,我不会说它外壳程序),因此添加特殊的语法来调用其他外壳程序会很奇怪。

如果出于某种实际原因实现了这样的请求(希望不是),我建议使用!而不是--%作为“调用sh​​ell运算符”。

人们可能会猜! (因为他们为此目的从ipython(或从vim命令模式或从less或从ftp知道! ftp ) )

另一方面, --%较难键入,不太可能被猜到,最重要的是:我认为--%的当前用法与“将内容传递给另一个shell”几乎没有共同点。行为。 当前的--%恰好模拟cmd语法的一个元素:变量替换(甚至不完全替换)。 它不支持转义字符^ ,不允许重定向到文件,依此类推。 当前--%唯一有用的东西是将逐字内容传递到lpCommandLine -但这是提议的“调用sh​​ell运算符”所不擅长的。

提议的“调用sh​​ell运算符”和当前的--%之间还有另一个区别(已经提到过):一个终止于管道,另一个终止于管道-对于新用户来说非常混乱。 另一方面, !会将|转发到我到目前为止测试过的所有应用程序中的shell(ipython,vim,less,ed等)。

HELP about_Logical_Operators -S

是的, !本身是有问题的,因为它是该语言中的逻辑否定运算符。

顺便说一句,备选方案之一是&! -一种“调用本地/ shell”运算符。

我希望@TSlivede在说“如果出于某种原因而实现了这样的目的(希望没有)”时,他又强调了“希望不是”部分。

  • 假设$用作_both_中的变量sigil,没有操作员/语句(带有单个参数)可以完全解决我想在本地命令行中使用PowerShell值的问题PowerShell和类似POSIX的外壳。

  • 在整个本机命令行周围没有显式定界符的情况下,没有任何运算符/语句可以解决本地命令行末尾的问题(您可能会或可能不会在意)。

@ PowerShell / powershell-committee今天对此进行了讨论。 在这一点上,关于最初的问题陈述或如何解决的设计似乎还没有达成共识,因此我们不相信我们可以将其纳入7.1 w /稳定的设计中。

@ PowerShell / powershell-committee今天对此进行了讨论。 在这一点上,关于最初的问题陈述或如何解决的设计似乎还没有达成共识,因此我们不相信我们可以将其纳入7.1 w /稳定的设计中。

182条留言!

对此进行了一些精彩的讨论! 我将继续监视有关此问题的任何新评论。 我们仍然鼓励社区独立于此问题而实施发布到PSGallery的Invoke-NativeCommand类型cmdlet。

当您在https://github.com/PowerShell/PowerShell/PowerShell/issues/13068#issuecomment -662732627上有一个折衷但非常有用的解决方案时,我认为很遗憾,要放弃此解决方案。 我知道您想要达成共识,但有时妥协是最好的选择。 不,这不是完美的,但是单行SO解决方案将非常有用。 至少在此人看来。

@essentialexch要清楚,我们在PowerShell团队内部甚至没有达成共识

由于推迟了执行助手

我们可以考虑使用新的cmdlet来简化用户采用,例如Invoke-Shell(Invoke-NativeShell),Test-Arguments(例如echoargs.exe,以显示本机应用程序并为用户提供帮助),Convert-Arguments(将用户输入转换为正确的转义参数)。

我认为测试参数可能非常有用。

我刚刚发布了一个模块NativeInstall-Module Native -Scope CurrentUser ),我邀请大家尝试一下,下面将使用其命令; 引用的数字用例来自上面@rkeithhill的评论。

如果我们想清除概念上的迷雾,我认为我们需要以不同的方式来构造用例。
_它们都不需要解析_中的任何更改。

用例[本地shell调用]:

您要执行为平台本机shell_编写的_individual command_(用例1)或整个_multi-command命令行_(用例2):(此问题):

  • 由于您使用的是不同的Shell语法,因此您要将命令(行)作为单个字符串_传递给目标Shell,以便_it_进行解析; PowerShell的字符串插值提供了将PowerShell值嵌入到传递的字符串中的能力(用例2a)。
  • insInvoke-NativeShell涵盖了这些用例。
# Use case 1: Single executable call with line continuation, on Unix.
@'
tar -cvpzf /share/Recovery/Snapshots/$(hostname)_$(date +%Y%m%d).tar.gz \
    --exclude=/proc \
    --exclude=/lost+found 
'@ | ins

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

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

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

这里的字符串语法不是最方便的方法(因此建议实现内联变体-参见#13204),但是您不必_have_就可以使用它。 对于逐字命令,您可以使用'...' ,它仅需要_doubling_嵌入式' (如果存在)。

另外,这是“ StackOverflow运算符”

如果将以下PSReadLine密钥处理程序放置在$PROFILE文件中,则可以使用Alt-v使用此处的逐字字符串为对ins的调用提供支持当前剪贴板文本粘贴到其中。Enter提交呼叫。

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

用例[直接可执行调用]:

您想使用_PowerShell_的语法和_individual arguments_(用例1a和3)来调用_single external execute_。

  • 这是任何shell的核心任务,因此,鲁棒地工作至关重要。

    • 您需要能够依靠PowerShell来传递_its_解析_as-is_到目标可执行文件的所有参数。 目前情况并非如此-请参阅#1995
  • 它要求您了解_PowerShell的_语法和_its_参数模式元字符。

    • 您需要注意, `用作转义字符并用于行继续。
    • 您需要注意,PowerShell还有其他元字符,需要逐字引用/转义; 这些是(请注意, @仅作为参数的第一个字符有问题。):

      • 对于类似POSIX的外壳(例如Bash): @ { } ` (和$ ,如果您想阻止PowerShell进行前期扩展)
      • cmd.exe( ) @ { } # `
      • 个别`转义此类字符。 就足够了(例如printf %s `@list.txt )。

虽然我们在等待1995年#是固定的,功能ie (对于nvoke(外部)电子xecutable)填补了这个空白,只是在前面加上直接调用; 例如,而不是以下调用:

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

您将使用以下内容:

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

Native模块带有类似于echoArgs.exedbeaDebug-ExecutableArguments )命令; Windows上的示例输出:

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

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

Command line (helper executable omitted):

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

通过使用-UseIe (( -ie )开关,您可以告诉dbea通过ie传递参数,这表明它可以解决问题:

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

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

Command line (helper executable omitted):

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

注意:固定#1995后,不再需要ie ; 为了向前兼容, ie旨在检测并自动推迟进行修复; 也就是说,修复程序到位后, ie将停止应用其解决方法,并且实际上将像直接/ &调用一样起作用。

用例[直接恶意Windows可执行调用]:

有两个主要问题,仅在_Windows上出现:

  • 您正在调用的“流氓”可执行文件的引用要求与最广泛使用的约定不同; 例如, msiexec.exemsdeploy.exe要求精确地用prop="<value with spaces>"报价-_value_部分的报价-即使"prop=<value with spaces>" -整个报价参数-应该_等效(后者是PowerShell合理地在后台执行的操作)。

  • 您正在使用_an参数调用_a批处理文件_,该参数不包含空格,但包含cmd.exe元字符_,例如&^| ; 例如:

    • .\someBatchFile.cmd 'http://example.org/a&b'
    • PowerShell-合理地-传递了http://example.org/a&b _unquoted_(因为没有嵌入的空格),但这会中断批处理文件的调用,因为cmd.exe -不合理地-使传递给批处理文件的参数服从于同样的解析规则,适用cmd.exe而不是接受它们作为文字。

    • 注意:虽然使用批处理文件来_direct_实施功能可能正在减弱,但是将它们用作_CLI入口点_仍然很常见,例如Azure的az CLI,它已作为批处理文件az.cmd

注意: ie自动为批处理文件和msiexec.exemsdeploy.exe处理这些情况,因此对于_most_调用,不需要额外的工作; 但是,不可能预料到所有“恶意”可执行文件。

有两种方法可以解决此问题:

  • 给定cmd.exe ,将自己的解释应用于命令行中的参数后,_does_保留指定的引号,您可以在Windows上委派给ins调用:

    • ins '.\someBatchFile.cmd "http://example.org/a&b"'
    • 如果使用_expandable_字符串,这又使您能够将PowerShell值嵌入命令字符串中。

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

  • 或者,使用当前的--%实现,但要注意其局限性:

    • .\someBatchFile.cmd --% "http://example.org/a&b"'
    • 给定--%的实现方式,嵌入PowerShell值的唯一方法是-笨拙地-定义_aux。 环境变量_并使用%...%语法引用它:

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


错误处理

待处理的https://github.com/PowerShell/PowerShell-RFC/pull/88将带来与外部(本机)可执行文件更好的错误处理集成。

同时, Native模块附带了两个功能,可以临时加入,将非零退出代码视为脚本终止错误

  • ins支持-e / -ErrorOnFailure开关,如果在调用本机外壳程序后$LASTEXITCODE不为零,则会引发错误。

  • ieeie的包装函数,如果在调用外部可执行文件后$LASTEXITCODE非零,则类似地引发错误。

模块附带的命令有很多细微之处。
相当广泛的基于注释的帮助有望涵盖它们。

如果我们想清除概念上的迷雾,我认为我们需要以不同的方式来构造用例。

大! 我之前发布的内容只是为了让人们考虑创建更多更好的用例。

我应该澄清insInvoke-NativeShell )的一些重要方面,这些方面与我先前提出的有所不同; 目的是说明bash的普遍性,并在各个平台之间提供一致的CLI。

  • Unix上,鉴于Bash的普遍性,我决定调用/bin/bash而不是/bin/sh ,但是您可以选择将/bin/sh-UseSh-sh )( /bin/sh也可以作为备用事件,因为不太可能出现/bin/bash不存在的情况)。

    • 为了在调用表单和平台之间获得一致的体验,我隐藏了设置调用名称$0的功能; 也就是说,任何传递参数都以$1开头,这与从管道中传递脚本时传递参数的方式以及用户期望的传递方式一致:
# Script as argument
PSonUnix> ins 'echo $# arguments; echo "[$1] [$2]"' one two
2 arguments
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonUnix> 'echo $# arguments; echo "[$1] [$2]"' | ins - one two
2 arguments
[one] [two]

# As an aside: script as argument, with pipeline input *as data*:
PSonUnix> 'one', 'two' | ins 'cat -n'
     1  one
     2  two
  • Windows上,出于技术原因,我不得不在后台使用临时的_batch file_,但是我认为使用批处理文件语法最终还是更好的选择( %%i而不是%i for循环变量,能够将%转义为%% )。

    • 使用批处理文件还可以支持传递参数,例如在Unix上:
# Script as argument
PSonWin> ins 'echo [%1] [%2]' one two
[one] [two]

# Script via pipeline; note the "-" to signal that the script is piped.
PSonWin> 'echo [%1] [%2]' | ins - one two
[one] [two]
此页面是否有帮助?
0 / 5 - 0 等级