我有bash脚本,然后接受几个参数测试并运行命令,如下所示
call script => /bin/bash myscript arg1 arg2 arg3 arg4
comm called => command -a arg1 -b arg2 -c arg3 -d arg4
如果参数为空,则不调用该选项.
call script => /bin/bash myscript arg1 arg2 '' arg4
comm called => command -a arg1 -b arg2 -d arg4
我可以通过在脚本中使用以下行来测试参数来实现这一点
if test "${arg3:-t}" != "t" ; then
从提示符调用时,此脚本的工作方式类似于魅力.即使我将”with“替换为空参数也能正常工作.
当我使用exec从java调用此脚本时,这开始失败.
Process p = Runtime.getRuntime().exec("/bin/bash myscript arg1 arg2 '' arg4");
expected command => command -a arg1 -b arg2 -d arg4 (as in above example)
actual command => command -a arg1 -b arg2 -c '' -d arg4
我无法理解为什么会发生这种情况.问题出在哪儿?在shell脚本中或从java执行命令的方式?
怎么解决这个问题?
解决方法:
基本的问题是java根本不会做任何事情来解析你给它的字符串并将它很好地分解为参数.
简短的回答是使用Runtime.exec的String []版本:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "myscript", "arg1", "arg2", "", "arg4"});
如果你有其他地方参数解析比那更复杂,你可以将字符串的解析传递给bash,并执行:
Runtime.getRuntime().exec(
new String[] {"/bin/bash", "-c", "/bin/bash myscript arg1 arg2 '' arg4"});
如果您正在转换某些内容,而不是正在执行大量复杂的重定向(如2>& 1)或设置整个管道,则可能需要使用此bash -c技巧.
编辑:
要了解这里发生了什么,你必须意识到当用户空间代码告诉内核“使用这些参数加载这个可执行文件并基于它启动一个进程”(*)时,传递给内核的是一个可执行文件,参数的字符串数组(此参数数组的第0个/第一个元素是可执行文件的名称,除非做了一些奇怪的事情),以及环境的字符串数组.
当看到这一行时bash会做什么:
/bin/bash myscript arg1 arg2 '' arg4
我想是“好吧,/ bin / bash不是内置的,所以我正在执行一些东西.让我们使用我的字符串解析算法将子进程的参数数组放在一起,这些算法知道引用和诸如此类的东西”.然后Bash确定为新进程传递内核的参数是:
> / bin / bash
> myscript
> arg1
> arg2
>(空字符串)
> arg4
现在bash有相当复杂的字符串处理算法,它适用于此处 – 它接受两种不同的引号,当它发生在字符串之外或双引号内时它会扩展$VAR,它会用输出替换反引号中的子命令,等等
当你调用exec的单字符串版本时,Java不会做任何如此复杂的事情.它只是创建一个新的StringTokenizer并使用它来分解你将它赋予参数的字符串.那堂课对报价一无所知;它将该字符串拆分为:
> / bin / bash
> myscript
> arg1
> arg2
>”(带有两个字符的字符串,两者都是单引号)
> arg4
Java然后调用exec的String []版本. (好吧,其中一个)
人们通过以下方式挑选尼特的说明: