Bash 调用 windows 程序,引用如何工作?
Bash calling windows program, how does quoting work?
我正在为我的虚拟机编写一些脚本,我正在使用 cygwin。我需要设置计算机名称和IP地址。这部分很简单,顺序是:
wmic computersystem where caption=name "vm-01"
netsh interface ip set address "Local Area Connection 2" static 10.155.155.50 255.255.255.0
这在 cmd.exe 中工作得很好。现在我想从 bash 执行它。我想查看我正在执行的命令,所以我正在使用这个 bash 函数来执行它:
call() {
echo "$@"
$@
}
我以直观的方式尝试了转义引号:
$ call wmic computersystem where caption=name \"wm-01\"
Executing (\BOH\ROOT\CIMV2:Win32_ComputerSystem.Name="XXX")->rename()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ReturnValue = 87;
};
哪个不起作用(不要像我一开始那样被 "Method execution successful" 误导),错误代码 87。
$ call netsh interface ip set address \"Local Area Connection 2\" static 10.155.155.50 255.255.255.0
另一方面,这很管用。
我设法通过
为 wmic
命令解决了这个问题
$ call wmic computersystem where caption=name \'wm-01\'
Executing (\BOH\ROOT\CIMV2:Win32_ComputerSystem.Name="XXX")->rename()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ReturnValue = 0;
};
这确实有效。我用 netsh
做了同样的尝试
$ call netsh interface ip set address \'Local Area Connection 2\' static 10.155.155.101 255.255.255.0
The filename, directory name, or volume label syntax is incorrect.
这不是。我想了解的是为什么一个命令需要 \'
而第二个命令需要 \"
?
这里有几点重要的是要认识到:
引用的意义是 shell,而不是(通常)正在启动的命令。 shell 在识别命令名称和传递其参数之前,但在所有其他命令行处理之后删除了为此目的而重要的引号。
只有 命令 的原始文本中出现的未加引号对 shell 的引用才有意义。特别是,参数扩展产生的引号字符并不特殊——它们只是代表它们自己。
虽然 shell 在执行任何扩展之前将命令行拆分为标记,但它稍后会在扩展命令上执行单词拆分。在已识别的引号内出现的扩展不会被分词,但是,同样地,来自 扩展的引号不会被识别为特殊的;他们不防止分词。
然后,考虑一下您的命令的这个版本:
call netsh interface ip set address "Local Area Connection 2" static 10.155.155.50 255.255.255.0
bash
读取该行,将其拆分为单词,平凡地执行扩展,执行单词拆分,然后执行引用删除,得到这些单词:
- 通话
- netsh
- 界面
- ip
- 设置
- 地址
- 本地连接2
- 静态
- 10.155.155.50
- 255.255.255.0
请注意 "Local Area Connection 2"(不带引号)是一个 'word'。第一个单词 'call' 是命令,其余单词是参数,由 $@
表示(也由 $*
和各个位置参数 </code> 表示, <em>etc</em>.) 内部函数 <code>call()
.
现在考虑一下当 call()
开始执行命令时会发生什么:在单词拆分之前,它与之前大致相同,但在那里,连接名称被拆分为多个参数,并显示netsh
命令的方式。
我相信你已经很感激了,但现在考虑一下你的命令的这个变化:
call netsh interface ip set address \"Local Area Connection 2\" static 10.155.155.50 255.255.255.0
在这种情况下,"
个字符被转义,因此不能达到使连接名称成为单个 shell 单词的目的。因此,它甚至在达到 call()
之前就已经分裂了。因此,您得到与以前相同的结果,只是 netsh
的两个参数中包含引号字符。
那么解决方法是什么?你几乎已经明白了。特殊参数 $@
和 $*
之间的区别在于它们与分词的交互。当 $@
在引号内扩展时,它是不对结果进行单词拆分的规则的一个特殊例外——结果在 $@
的元素之间拆分,而不是 在 他们之中。主要目的正是您要尝试做的事情:将 shell 的位置参数传递给另一个命令。
换句话说,使用这个版本的 shell 函数:
call() {
echo $@
"$@"
}
并使用您原来的命令行调用它,使用不带引号的引号。
我正在为我的虚拟机编写一些脚本,我正在使用 cygwin。我需要设置计算机名称和IP地址。这部分很简单,顺序是:
wmic computersystem where caption=name "vm-01"
netsh interface ip set address "Local Area Connection 2" static 10.155.155.50 255.255.255.0
这在 cmd.exe 中工作得很好。现在我想从 bash 执行它。我想查看我正在执行的命令,所以我正在使用这个 bash 函数来执行它:
call() {
echo "$@"
$@
}
我以直观的方式尝试了转义引号:
$ call wmic computersystem where caption=name \"wm-01\"
Executing (\BOH\ROOT\CIMV2:Win32_ComputerSystem.Name="XXX")->rename()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ReturnValue = 87;
};
哪个不起作用(不要像我一开始那样被 "Method execution successful" 误导),错误代码 87。
$ call netsh interface ip set address \"Local Area Connection 2\" static 10.155.155.50 255.255.255.0
另一方面,这很管用。
我设法通过
为wmic
命令解决了这个问题
$ call wmic computersystem where caption=name \'wm-01\'
Executing (\BOH\ROOT\CIMV2:Win32_ComputerSystem.Name="XXX")->rename()
Method execution successful.
Out Parameters:
instance of __PARAMETERS
{
ReturnValue = 0;
};
这确实有效。我用 netsh
$ call netsh interface ip set address \'Local Area Connection 2\' static 10.155.155.101 255.255.255.0
The filename, directory name, or volume label syntax is incorrect.
这不是。我想了解的是为什么一个命令需要 \'
而第二个命令需要 \"
?
这里有几点重要的是要认识到:
引用的意义是 shell,而不是(通常)正在启动的命令。 shell 在识别命令名称和传递其参数之前,但在所有其他命令行处理之后删除了为此目的而重要的引号。
只有 命令 的原始文本中出现的未加引号对 shell 的引用才有意义。特别是,参数扩展产生的引号字符并不特殊——它们只是代表它们自己。
虽然 shell 在执行任何扩展之前将命令行拆分为标记,但它稍后会在扩展命令上执行单词拆分。在已识别的引号内出现的扩展不会被分词,但是,同样地,来自 扩展的引号不会被识别为特殊的;他们不防止分词。
然后,考虑一下您的命令的这个版本:
call netsh interface ip set address "Local Area Connection 2" static 10.155.155.50 255.255.255.0
bash
读取该行,将其拆分为单词,平凡地执行扩展,执行单词拆分,然后执行引用删除,得到这些单词:
- 通话
- netsh
- 界面
- ip
- 设置
- 地址
- 本地连接2
- 静态
- 10.155.155.50
- 255.255.255.0
请注意 "Local Area Connection 2"(不带引号)是一个 'word'。第一个单词 'call' 是命令,其余单词是参数,由 $@
表示(也由 $*
和各个位置参数 </code> 表示, <em>etc</em>.) 内部函数 <code>call()
.
现在考虑一下当 call()
开始执行命令时会发生什么:在单词拆分之前,它与之前大致相同,但在那里,连接名称被拆分为多个参数,并显示netsh
命令的方式。
我相信你已经很感激了,但现在考虑一下你的命令的这个变化:
call netsh interface ip set address \"Local Area Connection 2\" static 10.155.155.50 255.255.255.0
在这种情况下,"
个字符被转义,因此不能达到使连接名称成为单个 shell 单词的目的。因此,它甚至在达到 call()
之前就已经分裂了。因此,您得到与以前相同的结果,只是 netsh
的两个参数中包含引号字符。
那么解决方法是什么?你几乎已经明白了。特殊参数 $@
和 $*
之间的区别在于它们与分词的交互。当 $@
在引号内扩展时,它是不对结果进行单词拆分的规则的一个特殊例外——结果在 $@
的元素之间拆分,而不是 在 他们之中。主要目的正是您要尝试做的事情:将 shell 的位置参数传递给另一个命令。
换句话说,使用这个版本的 shell 函数:
call() {
echo $@
"$@"
}
并使用您原来的命令行调用它,使用不带引号的引号。