Ste*_*ini 14 windows powershell r
如果我调用
Rscript -e "print('hello')"
Run Code Online (Sandbox Code Playgroud)
它正确打印出答案
[1] "hello"
Run Code Online (Sandbox Code Playgroud)
但是,如果我切换单引号和双引号,则不起作用,并且看起来双引号被删除了:
Rscript -e 'print("hello")'
Run Code Online (Sandbox Code Playgroud)
给出:
Error in print(hello) : object 'hello' not found
Execution halted
Run Code Online (Sandbox Code Playgroud)
请注意,它不是 powershell 错误地进行转义。Echoing 只给出预期的结果:
PS> echo 'print("hello")'
print("hello")
PS> echo "print('hello')"
print('hello')
Run Code Online (Sandbox Code Playgroud)
在 macOs 或 Linux 上没有观察到相同的行为,这两种变体都被正确解析。
有趣的是,command.com 更疯狂:
C:>Rscript -e "print('hello')"
[1] "hello"
C:>Rscript -e 'print("hello")'
[1] "print(hello)"
Run Code Online (Sandbox Code Playgroud)
我的意思是……什么?!?这已经在这里提到了:
但没有关于它的解释。在我看来,这是 Windows 上 Rscript 的一个错误,但我想听听其他意见。
mkl*_*nt0 13
Dabombber 的有用回答提供了所有的指导,但让我试着从概念上把它归结起来:
该问题并非特定于RScript.exe并可能影响从 PowerShell对任何外部可执行文件的调用:
至少在 PowerShell 7.0(在撰写本文时为最新版本),将带有嵌入式双引号 ( ") 的参数传递给外部程序的问题从根本上被破坏了,如GitHub 问题 #1995 中所述;简而言之:在幕后,PowerShell 为目标程序(进程)构建了一个"..."仅使用-quoting的命令行,但忽略了对嵌入的逐字字符进行转义。因为它们在语法上有效包含在这样的双引号字符串中。 "
现在,您必须手动转义嵌入的"字符。作为\"。
但是,如果错误得到修复,则此解决方法将中断,因为修复要求自动应用此转义,然后将逐字转义\"为\\\"。
# WORKAROUND as of v7.0, which will break if and when the problem gets fixed.
PS> Rscript -e 'print(\"hello\")'
Run Code Online (Sandbox Code Playgroud)
第三方Native模块(Install-Module -Scope CurrentUser Native例如用 安装)提供了辅助函数ie,可以补偿损坏的行为;它以向前兼容的方式编写,以便在它应该得到修复时,它将简单地遵循内置行为:
# Thanks to `ie`, no workarounds are required.
PS> ie Rscript -e 'print("hello")'
Run Code Online (Sandbox Code Playgroud)
至于临时解决方法- 它们都适用于Rscript.exe,但不能指望成为通用解决方案:
对于同时支持'...'和"..."引用的目标程序:交换引号以仅使用嵌入'字符。,如您的问题所示,但请注意,'...'和"..."字符串在 PowerShell 中具有不同的语义("..."字符串是可扩展的(插值)字符串),并且可能具有不同的语义在目标程序中也是如此(在 中不是这种情况Rscript):
Rscript -e "print('hello')"对于通过 stdin接受输入的目标程序,请使用 PowerShell管道,该错误不会出现(但请注意,您可能必须将$OutputEncoding首选项变量设置为目标程序预期的字符编码):
'print("hello")' | Rscript -至于您的观察和背景信息,包括 aboutcmd.exe和 POSIX-compatible shells:
请注意,它不是 powershell 错误地进行转义。
正如 Dabombber 指出的那样,问题在于PowerShell,但问题仅在调用外部程序时发生,而PowerShell 原生cmdletecho的内置别名(使用 验证)。在 Windows 上,您可以通过调用(忽略后缀)来查看有缺陷的参数传递的问题,如下所示:
Write-Output Get-Command echochoice.exe [Y,N]?N
PS> 'n' | choice /m 'print("hello")'
print(hello) [Y,N]?N
Run Code Online (Sandbox Code Playgroud)
choice.exe与/m可以用来呼应的参数,因为它会通过外部程序来接收,并且你可以看到双引号进行了有效丢失,因为PowerShell的错误放置print("hello") 逐字过程命令行上-没有逃避的"字符。- 外部程序解析为 verbatim print(hello),因为它们允许单个参数由未加引号和双引号部分组成(print(+ hello(去掉语法双引号) + ))。
print(hello)解释为 R 脚本,它会查找名为- 在这种情况下不存在的变量(对象)hello并触发您看到的错误消息。在类 Unix 平台(macOS、Linux)上,使用跨平台的PowerShell [Core] 版本,/bin/echo 'print("hello")'显示了同样的问题。
在 macOs 或 Linux 上没有观察到相同的行为,这两种变体都被正确解析。
是的,如果您在那里使用与POSIX 兼容的本机外壳,例如bash,您将获得正确的行为(见下文)。
command.com 更疯狂:
顺便说一句:您可能指的是cmd.exe基于 NT 的 Windows 平台上的传统命令处理器(命令提示符),直到当前的 Windows 10。(command.com是在以 Windows ME 结束的基于 DOS 的 Windows 版本上的命令处理器)。
cmd.exe只识别双引号 ( "...") 来为自己划分参数边界,而不识别单引号 ( '...') ;无论如何,它本质上将原始引用传递 给目标可执行文件(在执行自己对命令行的解释之后,例如环境变量扩展)。
这与 PowerShell 和 POSIX 兼容的 shell 所做的有根本的不同:
在类 Unix 平台上 - POSIX 兼容的 shell 识别'...'-quoted 参数 -进程命令行的概念不存在,并且类似 POSIX 的 shell 本身从其命令行中解析出的任何参数都按原样传递-作为逐字参数数组- 到目标可执行文件;因此,shell 字符串文字"print('hello')"和'print("hello")'分别作为逐字 传递print('hello')和print("hello"),按预期工作,因为 R 也识别'...'和"..."字符串文字。
PowerShell 也有'...'字符串(它逐字处理它们的内容),但在 Windows 上,它在幕后将它们转换为"..."字符串(如果需要引用),这是上述错误从 v7.0 开始出现的地方。撇开错误不谈,这种翻译是有道理的,因为在其他程序的命令行上,只能"..."假定引用具有句法意义(见底部)。不幸的是,PowerShell 在类 Unix 平台上做同样的事情,即使它不应该(它构造了一个伪命令行,.NET API 然后将其解析为传递给目标进程的逐字参数数组),因此错误浮出水面还有。
因为cmd.exe保留了原始引用,所以在命令行中将其RScript解释为字符串文字而不是command,因为它删除了任何字符。在将结果解释为 R 脚本之前,首先在命令行上使用语法函数(而按照惯例(单引号)在命令行上没有语法意义):'print("hello")'Rscript -e 'print("hello")'"'
'print("hello")'因此被解析为'print(+ hello(命令行"被剥离) + ),导致逐字'print(hello)'被解释为 R 代码,这是一个 R字符串文字,因此按原样打印(输出使用"..."引用,但这只是输出格式的产物; 请注意,print()不需要显式调用,表达式的结果 - 例如'print(hello)'在这种情况下的字符串文字- 会自动打印)。
相比之下,"print('hello')"被逐字解析print('hello')(命令行"被剥离),由于没有封闭引号,然后被解释为command,即print()函数调用,如预期。
最终,在 Windows上的进程命令行解析的无政府世界中没有硬性规定:最终由每个程序来解释其命令行-这个答案包含极好的背景信息。
然而,幸运的是,有广泛遵守的约定,如在 MS C/C++/.NET 编译器中实现并在此处记录的。
不幸的是,从 PowerShell 7.0 开始,由于上述错误,PowerShell 不遵守这些约定。由于该错误自 v1 以来就存在,因此用户已经学会了解决它,例如使用手动 \"转义,如上所示。问题是修复错误会破坏所有解决方法。现在正在考虑将修复程序作为实验性功能实施,最早适用于 v7.1 - 请参阅GitHub 上的此 PR和此处的相关讨论,这表明除了实施广泛建立的约定外,还应为调用批处理文件和msiexec.exe样式程序,它们具有非常规的引用要求。
小智 9
可能值得一看这个 PowerShell 问题:外部可执行文件的参数没有正确转义。Michael Klement的Native 模块提供了一种解决方法,直到问题得到修复(并且不应像许多当前的解决方法那样在修复后被破坏)。
请注意,它不是 powershell 错误地进行转义。回声只给出预期的结果
echo 是 PowerShell 函数而不是外部程序,因此您在使用它时不会注意到损坏的行为。
PS> Get-Command echo
CommandType Name Version Source
----------- ---- ------- ------
Alias echo -> Write-Output
Run Code Online (Sandbox Code Playgroud)
更好的测试是使用PowerShell Community Extensions 中的 EchoArgs.exe 命令行工具(可在此处下载)。
PS> echoargs.exe 'print("hello")'
Arg 0 is <print(hello)>
Command line:
"E:\echoargs.exe" print("hello")
PS> echoargs.exe "print('hello')"
Arg 0 is <print('hello')>
Command line:
"E:\echoargs.exe" print('hello')
Run Code Online (Sandbox Code Playgroud)
请注意,这不是 powershell 错误地进行了转义。回显仅给出预期结果:
在使用 echo 的情况下,它的 echo 直接消耗您传递给它的参数,因此对于单引号或双引号您会得到相同的结果。
就 Rscript 而言,我相信 Rscript 只是使用一些附加参数调用 R 的便捷方法。(有关说明,请参阅https://swcarpentry.github.io/r-novice-inflammation/05-cmdline/)。具体来说,它表示“从这个输出中,我们了解到 Rscript 只是运行 R 脚本的便捷命令......”
因此,也许发生的情况是,当您调用 RScript 时,它会将参数传递给单独的进程,因此它尝试将 hello 扩展为变量,从而导致错误(在 Powershell 情况下)
至于 cmd,它有自己的处理单引号和双引号的行为。
请参阅:单引号在 Windows 批处理文件中有何作用?CMD中单引号和双引号的 区别
所以问题可能不在于 RScript。您的用例的结果输出可能只是 powershell 和 cmd 如何处理双引号和单引号的副作用。
这也可以解释为什么问题只出现在 Windows 上,而不出现在 Linux 或 MacOS 上。
| 归档时间: |
|
| 查看次数: |
391 次 |
| 最近记录: |