Java命令行参数中的空格

31

在我的 Java 命令行参数中,空格后面的任何字符都会被忽略。例如:

java test.AskGetCampaignByName "Dummy books"
我只得到了第一个参数(args [0])作为“Dummy”。单引号也没有起到帮助作用。是否有解决方法/修复方法?这可能是因为我的终端设置吗? 我的$TERM是xterm,$LANG是“en_IN”。
5个回答

60
参数由shell处理(我假设您在Linux下使用Bash?),因此任何终端设置都不应该影响这个问题。
由于您已经引用了参数,应该是可以工作的。唯一可能的解释是,如果您的java命令是一个包装脚本,并且在传递给实际程序时混淆了参数的转义。这很容易做到,或者可能有点难以正确完成。
一个正确的包装脚本应该将所有的参数作为${1+"$@"}传递,任何其他版本很可能在处理嵌入式空格时出现错误。这样做是不常见的,然而,任何$2或类似的出现都会有问题,必须写成"$2"(或可能是${2+"$2"})以正确处理嵌入式空格,这在很多情况下都是错误的。
之所以使用不太直观的语法${1+"$@"},是因为原来的$*将所有参数连接成了"$1 $2 $3 ...",这对于嵌入式空格不起作用。然后"$@"被引入,它(正确地)展开为"$1" "$2" "$3" ...,适用于所有的参数,如果没有参数被给定,它应该展开为空。不幸的是,一些Unix供应商搞砸了,使"$@"即使在没有参数的情况下也展开为"",为了解决这个问题,聪明但不太可读的技巧${1+"$@"}被发明出来,使"$@"只有在参数$1被设置时才展开(即在没有参数的情况下避免展开)。
如果我的包装脚本的假设是错误的,您可以尝试使用strace进行调试:https://en.wikipedia.org/wiki/Strace
strace -o outfile -f -ff -F java test.AskGetCampaignByName "Dummy books"

运行"strace /bin/echo '1 2' 3"的示例,查找传递给execve的参数。

execve("/bin/echo", ["/bin/echo", "1 2", "3"], [/* 93 vars */]) = 0
brk(0)                                  = 0x2400000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f420075b000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f420075a000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/usr/lib64/alliance/lib/tls/x86_64/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/alliance/lib/tls/x86_64", 0x7fff08757cd0) = -1 ENOENT (No such file or directory)
open("/usr/lib64/alliance/lib/tls/libc.so.6", O_RDONLY) = -1 ENOENT (No such file or directory)
...

2
非常感谢您,hlovdal! 正是这种情况。我们通过ant构建生成可执行文件,设置类路径等。我们在脚本中使用“java @”。用${1 + "@"}替换解决了问题。很抱歉我在示例中直接使用java给您带来了误导。我以为它的意思是一样的。 - ashweta
1
非常周到、全面的回答。+1 建议使用 strace。你的回答完美地解决了我的问题。 - Aquarelle

0

你只需要像这样转义空格:

normal String: "Hello World!"
escaped String: "Hello" "World!"

那对我有用。

我的环境:

23:39:19 Zarathustra@thora:/Users/Zarathustra~$bash -version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin11)
Copyright (C) 2007 Free Software Foundation, Inc.

2
你并没有转义任何东西,你只是将一个参数分成了两个参数,这完全不是 OP 所要求的。空格不需要被转义,只需要加上引号即可。 - Brad Wood

0
如果你的程序需要更多的参数(当命令行使用很重要时),你应该考虑选项和开关。Apache Commons 有一个很棒的可以做到这一点。

0

看起来你正在使用一个操作系统发行版,其中用户可用的java命令是一个包装器,它会找到正确的JVM并相应地调用它。

如果是这样的话,在调用实际的java可执行文件时,它很可能无法适当地转义参数。

你使用的是哪个发行版?


在我的Windows机器上,作为命令行参数传递的"hello world" "hello world" "hello world"将被选中为args[0(/1)(/2)] == "hello world",但在Linux上,这根本不起作用(我不记得它到底做了什么,因为我并不是太关心)。但是,我很乐意看到任何解决方案。感谢提问。@Thorbjørn Ravn Andersen,请详细说明。我该如何更改JVM调用的方式? - Peter Perháč

-3
只需在您的Java程序中重新组合参数即可。
StringBuilder allArgs = new StringBuilder();
for (int i=0; i < args.length; i++)
{
    //System.out.println("arg"+i+": "+args[i]);
    allArgs.append(args[i]+" ");
}
// Parse out the args the way you wish using allArgs

因为在 java test.AskGetCampaignByName "Dummy books" 中,Dummybooks 之间有多个空格会导致失败(Stack Overflow 输出为单个空格,所以你在这里看不到...)。 - leemes
因为java应该从shell正确获取命令行参数。 - Olathe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接