为什么在从PowerShell调用批处理文件时,插入符号会消失?

Eri*_*ric 2 powershell escaping batch-file

这是我的args.bat档案:

@echo off
python -c "import sys; print(sys.argv)" %*
Run Code Online (Sandbox Code Playgroud)

我在这里调用python因为我确切地知道如何解释输出,而不是猜测字符串中的引号

如果我从cmd调用它会发生什么:

>.\args.bat ^^^^^
More?
More?
['-c', '^']
>.\args.bat "^^^^^"
['-c', '^^^^^']
Run Code Online (Sandbox Code Playgroud)

但这是powershell的作用:

PS> .\args.bat ^^^^^
['-c', '^']
PS> .\args.bat "^^^^^"
['-c', '^']
PS> .\args.bat '^^^^^'
['-c', '^']
PS> .\args.bat "'^^^^^'"
['-c', '^']
PS> .\args.bat '"^^^^^"'
['-c', '^^^^^']
Run Code Online (Sandbox Code Playgroud)

为什么在从PowerShell调用批处理文件时必须加倍引用该参数?


从powershell调用python不存在同样的问题:

PS> python -c "import sys; print(sys.argv)" "^^^^^"
['-c', '^^^^^']
PS> python -c "import sys; print(sys.argv)" '^^^^^'
['-c', '^^^^^']
PS> python -c "import sys; print(sys.argv)" ^^^^^
['-c', '^^^^^']
Run Code Online (Sandbox Code Playgroud)

但是从PowerShell调用python批处理时仍然存在!

PS> python -c "import subprocess; subprocess.call(['args.bat', '^^^^^'])"
['-c', '^']
Run Code Online (Sandbox Code Playgroud)

从cmd的python批处理:

> python -c "import subprocess; subprocess.call(['args.bat', '^^^^^'])"
['-c', '^']
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 6

有关批处理文件是否可以稳健地传递任意参数的更大问题(扰流警报:否),请参阅Eric的后续问题.


tl;博士:

PS> .\args.bat --% "^^^^^"
['-c', '^^^^^']
Run Code Online (Sandbox Code Playgroud)

使用--%,停止解析符号,使PowerShell通过原样传递剩余的参数,除了扩展cmd.exe样式的环境变量引用,如%OS%.

相反,您将无法引用PowerShell变量或表达式.


PowerShell作为一个独立的shell,首先执行自己的解析,然后 - 在Windows上一个不幸的必要- 在为外部程序编写命令行时重新应用引用.

它的做法随着时间的推移而发生变化,并且是过去许多问题的根源.--%然而,如上所述,这些问题导致了其具有其自身挑战的引入.

然而,重新引用通常是必要的,因为PowerShell的语法不一定被外部程序理解.
最值得注意的是,'...'不能假设所有外部程序都能理解引用,因此需要转换为"...".

但是,PowerShell会根据是否认为有必要而有条件地重新引用.

在手头的情况下,由于内容的的PowerShell的 "^^^^^"字符串包含没有空格,PowerShell的决定不要引用它通过批处理文件时,它; 即:

.\args.bat "^^^^^"
Run Code Online (Sandbox Code Playgroud)

有效地成为

.\args.bat ^^^^^
Run Code Online (Sandbox Code Playgroud)

对于大多数^没有语法意义的外部程序来说这很好,但是在批处理文件(由解释cmd.exe)的情况下,它很重要,因为^函数作为转义字符.在那里没有引用字符串.

要在此方案中仍然强制使用双引号,作为上述--%方法的替代方法,您可以使用嵌入式双引号,如您在问题中已经演示的那样:

PS> .\args.bat '"^^^^^"' #  '...' quoting with embedded "..." quoting
['-c', '^^^^^']
Run Code Online (Sandbox Code Playgroud)

从powershell调用python不存在同样的问题:
PS> python -c "import sys; print(sys.argv)" "^^^^^"

那是因为cmd.exe没有涉及这种情况,即使从PowerShell调用时丢失了封闭的双引号,Python ^也会将实例视为文字.

但是当从PowerShell调用python批处理时仍然存在
PS> python -c "import subprocess; subprocess.call(['args.bat', '^^^^^'])"

从cmd的python批处理: > python -c "import subprocess; subprocess.call(['args.bat', '^^^^^'])"

那是因为你再次调用一个批处理文件 - 在这种情况下甚至是不加引号 ,因为封闭只对Python具有语法意义并且没有通过 - 这重新引入了特殊的解释.^^^^^cmd.exe'....'^

请注意,即使是"无shell"调用批处理文件(例如subprocess.call()来自Python)的方法仍然总是会调用cmd.exe,因为cmd.exe执行批处理文件需要解释器.

当将未加引号的字符串^^^^^传递给批处理文件时,似乎有两轮解释:首先,作为参数解析的一部分,每^^对成为单个^,任何非配对^被丢弃.因此,在批处理文件中,您会看到^^,当使用不带引号时,它会变为单个^- 如果您双引号%*%1在批处理文件中,您将看到^^已通过.