文章目录
(一) 现象
某个java程序在传入命令行参数时,虽然大部分情况下正常。
但当参数中有字符*
的时候,也可能出现程序收到的参数,对应不上实际命令行参数的情况。
纳尼?从来没遇到过噢。
(二) 分析
不要业务部分,仅编写最简单的测试程序来测试。
测试程序仅仅打印出收到的参数(这也会错?)
输入固定的参数1a 2b 3c 4d "C:\mysql\*" 6e 7f
(2.1) Java
public static void main(String[] args) {
for(String aa: args){
System.out.println(aa);
}
System.out.println();
}
(2.1.1) Java + IntellJ IDEA 调试(正常)
(2.1.2) Java + CMD(正常)
正常:
再测试,去掉参数中的双引号。
此时参数 C:\mysql\*
这个字符串,变成了目录下列举到的文件名,依次传入程序了。
(2.1.3) Java + PowerShell(问题)
无论是不是带有引号,均无法把 C:\mysql\*
这个字符串传入程序,收到的都是列举目录的多个文件名。
(2.2) Python
为了对比,也用Python试了一下。
import sys
if __name__ == "__main__":
for aa in sys.argv:
print(aa)
print("")
(2.2.1) Python + CMD(正常)
再测试,去掉参数中的双引号。
没有任何变化,并没去列举文件名。
(2.2.2) Python + PowerShell(正常)
无论是不是带有引号,均不会去列举文件。
(三) 总结
到底是Powershell还是Java的锅我理解不了。
(3.1)神奇功能
结论是在Windows
中用CMD
或PowerShell
调用Java程序,命令行参数如果带有通配符*
和?
。
则会去列举这个字符串的文件,上例相当于dir C:\mysql\*
,并把列举到的文件名当作参数传递。
因此破坏了参数的个数检查,打乱了顺序。
仅调用Java时才这样,至少Python是正常的。
呃,可执行程序我准备再试试。
(3.2)引号包围参数
绕开这个神奇功能,可以在CMD
中通过双引号包围参数,如:"C:\mysql\*"
,将参数视为单个字符串传递。
PS:单个参数中如果有空格,也需要用双引号包围。
但是这个办法,在PowerShell
下失效了。
无论是否有引号,双引号或单引号(是的,PowerShell比CMD多支持了单引号),它都必须去匹配文件。
除非配备不到文件,才将字符串本身传递(这是什么奇葩逻辑)。
也就是PS根本不遵守引号内的字符串是单个参数,这个基本的约定(可恶的索尼😄)。
(3.3)转义字符
在PowerShell下,参数中如果包含了*
或?
,用转义字符是没有办法转义它的。
而且对于转义字符引号自己,比如刚才的"C:\mysql\*"
,即使改为"""C:\mysql\*"""
看上去正确了,其实也只是匹配不到带引号的"C:\mysql\*"
文件而已。
如果换成单引号,换个写法,比如'''*''.txt'
,是可以匹配到带单引号的文件'A'.txt
的。
PS:文件名中确实允许有单引号。
(四) 解决
办法很简答,用微软抛弃的CMD
。
或者参数复杂点,比如前面带横杠-param
,--param
,而不是直接param
。(啥,你喜欢用args
?)