【发布时间】:2013-01-04 02:42:59
【问题描述】:
我需要将命令行参数从 A.exe 传递给 B.exe。如果 A.exe 具有多参数,例如
A.exe -a="a" -b="b"'
我可以使用
BeginProcess("B.exe", **args!**)
启动 B.exe。 如何获取原始命令行参数,如
'-a="a" -b="b"'
【问题讨论】:
我需要将命令行参数从 A.exe 传递给 B.exe。如果 A.exe 具有多参数,例如
A.exe -a="a" -b="b"'
我可以使用
BeginProcess("B.exe", **args!**)
启动 B.exe。 如何获取原始命令行参数,如
'-a="a" -b="b"'
【问题讨论】:
如果您使用的是 Windows,则使用 GetCommandLine 获取原始命令行。
请注意,GetCommandLine 还包括 argv[0]。因此,在将 GetCommandLine 的输出传递给 B 之前,您必须超越 argv[0]。
这是一些非错误检查代码
#include <string.h>
#include <windows.h>
#include <iostream>
#include <ctype.h>
int main(int argc, char **argv)
{
LPTSTR cmd = GetCommandLine();
int l = strlen(argv[0]);
if(cmd == strstr(cmd, argv[0]))
{
cmd = cmd + l;
while(*cmd && isspace(*cmd))
++cmd;
}
std::cout<<"Command Line is : "<<cmd;
}
当我以A.exe -a="a" -b="b" 运行上述程序时,我得到以下输出
A.exe -a="a" -b="b"
Command Line is : -a="a" -b="b"
【讨论】:
lpApplicationName 参数显式指定可执行文件,则lpCommandLine 参数中指定的可执行文件将被忽略。
这是跳过可执行文件名称的唯一正确方法,基于Wine's implementation of CommandLineToArgvW:
char *s = lpCmdline;
if (*s == '"') {
++s;
while (*s)
if (*s++ == '"')
break;
} else {
while (*s && *s != ' ' && *s != '\t')
++s;
}
/* (optionally) skip spaces preceding the first argument */
while (*s == ' ' || *s == '\t')
s++;
注意!当前的 Wine 实现,截至 2020 年 2 月 19 日 - git commit a10267172,现在从 dlls/shell32/shell32_main.c 移动到 dlls/shcore/main.c。
【讨论】:
main的标准定义是
int main(int argc, char* argv[])
argv 变量包含命令行参数。 argc 变量表示使用了argv 数组中的条目数。
【讨论】:
strcat() 函数。
std::string(argv[1])+argv[2] 保留参数中的任何空格。您的意思是新流程无法再次可靠地分离参数吗?如果是这样,那正是我在 cmets 中对 PotatoSwatter 回答的批评......传递 argv 保留了单独的参数,连接和重新拆分不会。
在您的程序开始运行之前,输入到 shell 的原始字符串由 shell 转换为 argv。除了argv之外,我从未听说过提供“原始”命令行的操作系统或shell。
如果用户使用引号将空格字符传递到您的参数中怎么办?如果他们使用反斜杠来转义引号内的引号怎么办?不同的shell甚至可能有不同的引用规则。
如果您有一个类似argv 的列表,您应该尝试找到一个接受它的 API,而不是尝试实现仅辅助实际目标的字符串处理。 Microsoft 非常重视安全性,他们当然提供了一些不需要为您的应用程序添加安全漏洞的东西。
我找不到关于任何 C/C++ API BeginProcess 的文档;我有点假设这是 Windows,但无论如何您都应该仔细检查您平台的参考手册以获取替代系统调用。
【讨论】:
argv之外,我从未听说过有操作系统或外壳提供“原始”命令行。 Win32通过GetCommandLine(和大概你必须自己解析它......呃!多么原始!)我认为BeginProcess 应该是CreateProcess
execv() 系列函数允许您传递已经拆分为单独单词的参数数组,这更安全(不可能出现引用或转义问题。)早在 Windows 存在之前,它就已解决。
这就是我将命令行转回 shell args 的方式。有时这很好地回显到输出文件中,以将“使用的参数”与输出一起保存。转义是基本的,对于大多数情况来说已经足够了。
我在命令 (i=0) 处开始输出。如果您只需要参数等,您可以更改为 (i=1)。
//you have to free() the result!, returns null if no args
char *arg2cmd(int argc, char** argv) {
char *buf=NULL;
int n = 0;
int k, i;
for (i=0; i <argc;++i) {
int k=strlen(argv[i]);
buf=( char *)realloc(buf,n+k+4);
char *p=buf+n;
char endq=0;
// this is a poor mans quoting, which is good enough for anything that's not rediculous
if (strchr(argv[i], ' ')) {
if (!strchr(argv[i], '\'')) {
*p++='\'';
endq='\'';
} else {
*p++='\"';
endq='\"';
}
}
memcpy(p, argv[i], k);
p+=k;
if (i < (argc-1)) *p++=' ';
if (endq) *p++=endq;
*p='\0';
n = p-buf;
}
return buf;
}
还有一个简单的 cpp 包装器:
std::string arg2string(int argc, char **argv) {
char *tmp=arg2cmd(argc, argv);
std::string ret=tmp;
free(tmp);
return ret;
}
【讨论】:
在 C++/CLI 中是这样的:
String^ cmdarg = Environment::CommandLine;
【讨论】:
如果您使用的是 Windows,我相信正确的解决方案是调用 GetCommandLine() 来获取完整的命令行,然后调用 PathGetArgs(CommandLine) 从开头删除 arg0(您的 exe 路径)。
【讨论】: