Javascript Node.js中的引号生成参数
我在Node.jsJavascript Node.js中的引号生成参数,javascript,node.js,tar,spawn,Javascript,Node.js,Tar,Spawn,我在Node.jsspawn参数中使用双引号,因为它们可能包含空格: const excludes = ['/foo/bar', '/foo/baz', '/foo/bar baz']; const tar = spawn('tar', [ '--create', '--gzip', // '--exclude="/foo/bar"', '--exclude="/foo/baz"', '--exclude="/foo/bar baz"' ...excludes.map(exclude
spawn
参数中使用双引号,因为它们可能包含空格:
const excludes = ['/foo/bar', '/foo/baz', '/foo/bar baz'];
const tar = spawn('tar', [
'--create', '--gzip',
// '--exclude="/foo/bar"', '--exclude="/foo/baz"', '--exclude="/foo/bar baz"'
...excludes.map(exclude => `--exclude="${exclude}"`),
'/foo'
], { stdio: ['ignore', 'pipe', 'inherit'] });
出于某种原因,tar
忽略了——排除了以这种方式提供的参数。结果与spawn
为require('child_process')。spawn
和require('cross-spawn')
--exclude
在不需要双引号的路径中没有双引号时按预期工作
即使使用双引号,shell也能实现同样的效果:
tar --create --gzip --exclude="/foo/bar" --exclude="/foo/baz" /foo > ./foo.tgz
我不确定那里发生了什么,以及如何调试spawn
,以检查它是否对双引号进行了一些奇怪的转义。这是引号类型优先级中的一个问题。双引号优先于单引号,因此spawn调用失败
系统shell将去掉参数周围的引号,因此程序将在末尾获取未加引号的值。生成进程会绕过此步骤,因为它会绕过shell,因此程序会将这些文字引号作为参数的一部分,并且不知道如何适当地处理它们
我知道有两种解决方法:
这与直觉相反,但是切换报价类型应该可以解决这个问题。将上述代码切换到:
const tar = spawn("tar", [
"--create", "--gzip",
"--exclude='/foo/bar'", "--exclude='/foo/baz'", "/foo"
], { stdio: ["ignore", "pipe", "inherit"] });
或者,您可以使用{shell:true}
并使用当前格式。这将通过shell传递spawn请求,因此将发生当前跳过的解析步骤。了解更多有关此的信息
您应该了解shell如何处理空格和引号。我说“外壳”-有不同的外壳,我不知道它们之间的区别,所以我将要写的东西可能不适用于你。有人可以随意编辑,这样更精确
shell命令中可以包含各种复杂的语法:管道命令、输入和输出文件、插值变量、插值命令、环境变量,以及至少4种(是的,4种)引用字符串的不同方式。但就这个问题而言,让我们假设shell命令是一个命令名,后跟一个字符串参数列表(可能为空)。命令名可以是内置命令(cd
、ls
、sudo
等),也可以是可执行文件。或者,换句话说,shell命令是一个或多个字符串的列表(包括第一个字符串,它告诉shell它是什么类型的命令)
由于上述复杂因素,有几个字符是特殊字符。这意味着您可能需要使用引号将其转义。然而,引号在语言中引入了大量冗余。例如,以下命令是等效的:
tar --create --exclude=/foo/bar /foo
tar --create --exclude='/foo/bar' /foo
tar --create --exclude="/foo/bar" /foo
tar --create '--exclude=/foo/bar' /foo
tar --create "--exclude=/foo/bar" /foo
在每种情况下,命令都是使用参数列表--create
,--exclude=/foo/bar
,/foo
运行可执行文件tar
注意引号的行为,它不同于我所知道的所有其他语言。在大多数语言中,字符串文字完全由一对引号括起来——这就是编译器/解释器如何知道它们的起点和终点。但是在shell命令中,空格告诉shell一个参数的结束位置和下一个参数的开始位置。(引号/转义的空格不计算在内。)引号的唯一用途是改变某些字符的处理方式。Shell命令在这方面非常灵活,因此以下命令也相当于上面的命令:
tar -"-"create --exc'lude=/fo'o/bar /foo
tar --cr'eate' --exclude"="/foo"/bar" /foo
当我说这些命令是等效的时,我的意思是tar
可执行文件无法知道调用了哪一个。也就是说,不可能写入可执行文件mycommand
,使得命令mycommand foo
和mycommand“foo”
将不同的输出写入STDOUT或STDERR,或者返回不同的退出代码,或者以其他方式表现不同
但是,当从nodejs运行shell命令时,您不需要将shell功能用于管道、文件流、插入变量等,因为如果需要,javascript可以处理所有这些内容。因此,当您向spawn
提供参数时,它会绕过那些shell特性;它对shell特殊字符没有任何作用。您只需直接提供参数。因此,在下面的示例中,其中一个参数是--exclude=/foo/bar baz
,这将导致tar
忽略/foo
目录中名为bar baz
的文件/目录:
const tar = spawn('tar', [
'--create', '--gzip',
'--exclude=/foo/bar', '--exclude=/foo/baz', '--exclude=/foo/bar baz',
'/foo'
], { stdio: ['ignore', 'pipe', 'inherit'] });
(虽然很明显,如果您使用的是javascript字符串文字,可能需要在javascript级别转义一些字符。)
乔舒恩的回答我都不喜欢。(1) 甚至对我都不起作用,我很惊讶它对他起作用了——如果它起作用了,那么我就把它看作是nodejs(或者可能是tar
)中的一个bug。(我在Ubuntu 16.04.3 LTS中运行nodejs v6.9.5,GNU tar v1.28。)至于(2),这意味着不必要地将shell字符串处理的所有复杂性引入javascript代码中。正如上面所说:
注意:如果启用了shell
选项,请不要将未初始化的用户输入传递到此函数。任何包含shell元字符的输入都可用于触发任意命令执行
就我个人而言,我不知道shell转义的所有复杂之处,因此我不会冒险使用带有不可信输入的shell
选项运行spawn
。如果我理解您的要求,您只需要保留默认的shell行为,即剥离引号并将参数作为单个参数传递,即使它有空格
在这种情况下,您可以执行以下操作:
spawn(exe, args, { windowsVerbatimArguments: true });
见:
windowsVerbatimArguments
在Windows上不引用或转义参数。在Unix上被忽略。这被设置为true
spawn(exe, args, { windowsVerbatimArguments: true });