我有一个.Net应用程序,它接受一堆命令行参数,处理其中一些,并将其余部分用作另一个应用程序的参数
E.g。
MyApp.exe foo1 App2.exe arg1 arg2 ...
MyApp.exe
是我的申请,
foo1
是我的应用程序关注的参数。 App2.exe
是另一个应用程序,我的应用程序将运行带有arg1 arg2等的App2作为参数。
目前我的应用程序只是使用类似这样的
运行App2.exe Process.Start(args[1], String.Join(" ", args.Skip(2))
。因此上面的命令将正确运行:带有参数“arg1 arg2”的App2.exe。但是,请考虑这样的事情
MyApp.exe foo1 notepad.exe "C:\Program Files\readme.txt"
上面的代码不会知道引号,并且会运行带有参数C:\ Program Files \ readme.txt(不含引号)的notepad.exe。 我该如何解决这个问题?
答案 0 :(得分:6)
Environment.CommandLine
会给你一个确切的命令行 - 你必须解析你的应用程序的路径,否则就像一个魅力 - @idle_mind提到这个早期(有点)
编辑将示例移到答案中(因为人们显然仍然在寻找这个答案)。注意,当debuging vshost稍微搞砸命令行时。
#if DEBUG
int bodgeLen = "\"vshost.\"".Length;
#else
int bodgeLen = "\"\"".Length;
#endif
string a = Environment.CommandLine.Substring(Assembly.GetExecutingAssembly().Location.Length+bodgeLen).Trim();
答案 1 :(得分:4)
您需要修改MyApp以包含带引号的任何参数。
简短的故事,新代码应该是这样的:
var argsToPass = args.Skip(2).Select(o => "\"" + o.Replace("\"", "\\\"") + "\"");
Process.Start(args[1], String.Join(" ", argsToPass);
逻辑是:
每个参数都应该用引号括起来,所以如果你打电话给:
MyApp.exe foo1 notepad.exe "C:\Program Files\readme.txt"
该应用程序将以这种方式调用:
notepad.exe "C:\Program Files\readme.txt"
每个参数都应该转义引号(如果有的话),所以如果你打电话给:
MyApp.exe foo1 notepad.exe "C:\Program Files\Some Path with \"Quote\" here\readme.txt"
该应用程序将以这种方式调用:
notepad.exe "C:\Program Files\Some Path with \"Quote\" here\readme.txt"
答案 2 :(得分:2)
使用Environment.GetCommandLine(),因为它会将参数保持在引号中作为一个参数。
答案 3 :(得分:1)
嗯,简单的答案是在调用MyApp2.exe时将每个参数都包装在引号中。 包装一个单词的参数并没有什么坏处,它将修复它在参数中有空格的情况。
唯一可能出错的是,如果参数已经有一个转义引号。
答案 4 :(得分:1)
您可以使用反斜杠作为转义引号。以下将工作
MyApp.exe foo1 notepad.exe \"C:\Program Files\readme.txt\"
如果您不知道将要运行哪些其他exes以及他们期望的参数是什么,那么上面将是最佳解决方案。在这种情况下,您无法在程序中添加引号。
提供在运行应用程序时有引号时添加反斜杠的说明
答案 5 :(得分:1)
感谢@ mp3ferret有正确的想法。但是没有使用Environment.CommandLine
的解决方案的示例,所以我继续创建了一个OriginalCommandLine
类,它将获得最初输入的命令行参数。
参数在tokenizer
正则表达式中定义为任何类型字符的双引号字符串,或非空格字符的未加引号字符串。在引用的字符串中,引号字符可以通过反斜杠进行转义。 然而一个尾部反斜杠后跟一个双引号,然后空格不会被转义。
有理由我选择了转义的例外,因为空格是为了容纳以反斜杠结尾的引用路径。我相信你不太可能遇到你真正想要转义双重报价的情况。
static public class OriginalCommandLine
{
static Regex tokenizer = new Regex(@"""(?:\\""(?!\s)|[^""])*""|[^\s]+");
static Regex unescaper = new Regex(@"\\("")(?!\s|$)");
static Regex unquoter = new Regex(@"^\s*""|""\s*$");
static Regex quoteTester = new Regex(@"^\s*""(?:\\""|[^""])*""\s*$");
static public string[] Parse(string commandLine = null)
{
return tokenizer.Matches(commandLine ?? Environment.CommandLine).Cast<Match>()
.Skip(1).Select(m => unescaper.Replace(m.Value, @"""")).ToArray();
}
static public string UnQuote(string text)
{
return (IsQuoted(text)) ? unquoter.Replace(text, "") : text;
}
static public bool IsQuoted(string text)
{
return text != null && quoteTester.Match(text).Success;
}
}
正如您从下面的结果中看到的,上述方法修复程序维护引号,同时更优雅地处理您可能遇到的实际情况。
Test:
ConsoleApp1.exe foo1 notepad.exe "C:\Progra\"m Files\MyDocuments\" "C:\Program Files\bar.txt"
args[]:
[0]: foo1
[1]: notepad.exe
[2]: C:\Progra"m Files\MyDocuments" C:\Program
[3]: Files\bar.txt
CommandLine.Parse():
[0]: foo1
[1]: notepad.exe
[2]: "C:\Progra"m Files\MyDocuments\"
[3]: "C:\Program Files\bar.txt"
我讨论了使用替代方案来转义双引号。我觉得使用""
更好,因为命令行经常处理反斜杠。我保留了反斜杠转义方法,因为它向后兼容命令行参数的正常处理方式。
如果要使用该方案,请对正则表达式进行以下更改:
static Regex tokenizer = new Regex(@"""(?:""""|[^""])*""|[^\s]+");
static Regex unescaper = new Regex(@"""""");
static Regex unquoter = new Regex(@"^\s*""|""\s*$");
static Regex quoteTester = new Regex(@"^\s*""(?:""""|[^""])*""\s*$");
如果您希望更接近args
的预期,但引号保持不变,请更改两个正则表达式。仍然存在细微差别,"abc"d
将从abcd
返回args
,但从我的解决方案返回[0] = "abc", [1] = d
。
static Regex tokenizer = new Regex(@"""(?:\\""|[^""])*""|[^\s]+");
static Regex unescaper = new Regex(@"\\("")");
如果真的,确实希望获得与args
相同数量的元素,请使用以下内容:
static Regex tokenizer = new Regex(@"(?:[^\s""]*""(?:\\""|[^""])*"")+|[^\s]+");
static Regex unescaper = new Regex(@"\\("")");
完全匹配的结果
Test: "zzz"zz"Zzz" asdasd zz"zzz" "zzz"
args OriginalCommandLine
------------- -------------------
[0]: zzzzzZzz [0]: "zzz"zz"Zzz"
[1]: asdasd [1]: asdasd
[2]: zzzzz [2]: zz"zzz"
[3]: zzz [3]: "zzz"
答案 6 :(得分:0)
尝试以下方法。
此代码保留了双引号字符,并提供了转义\和“字符”的选项(请参阅下面代码中的注释)。
static void Main(string[] args)
{
// This project should be compiled with "unsafe" flag!
Console.WriteLine(GetRawCommandLine());
var prms = GetRawArguments();
foreach (var prm in prms)
{
Console.WriteLine(prm);
}
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern System.IntPtr GetCommandLine();
public static string GetRawCommandLine()
{
// Win32 API
string s = Marshal.PtrToStringAuto(GetCommandLine());
// or better, managed code as suggested by @mp3ferret
// string s = Environment.CommandLine;
return s.Substring(s.IndexOf('"', 1) + 1).Trim();
}
public static string[] GetRawArguments()
{
string cmdline = GetRawCommandLine();
// Now let's split the arguments.
// Lets assume the fllowing possible escape sequence:
// \" = "
// \\ = \
// \ with any other character will be treated as \
//
// You may choose other rules and implement them!
var args = new ArrayList();
bool inQuote = false;
int pos = 0;
StringBuilder currArg = new StringBuilder();
while (pos < cmdline.Length)
{
char currChar = cmdline[pos];
if (currChar == '"')
{
currArg.Append(currChar);
inQuote = !inQuote;
}
else if (currChar == '\\')
{
char nextChar = pos < cmdline.Length - 1 ? cmdline[pos + 1] : '\0';
if (nextChar == '\\' || nextChar == '"')
{
currArg.Append(nextChar);
pos += 2;
continue;
}
else
{
currArg.Append(currChar);
}
}
else if (inQuote || !char.IsWhiteSpace(currChar))
{
currArg.Append(currChar);
}
if (!inQuote && char.IsWhiteSpace(currChar) && currArg.Length > 0)
{
args.Add(currArg.ToString());
currArg.Clear();
}
pos++;
}
if (currArg.Length > 0)
{
args.Add(currArg.ToString());
currArg.Clear();
}
return (string[])args.ToArray(typeof(string));
}
答案 7 :(得分:0)
尝试使用“\”“。我必须传递url作为参数,这就是:
_filenameDestin和_zip是网址。我希望它有所帮助。
string ph = "\"";
var psi = new ProcessStartInfo();
psi.Arguments = "a -r " + ph + _filenameDestin + ".zip " + ph + _filenameDestin + ph;
psi.FileName = _zip;
var p = new Process();
p.StartInfo = psi;
p.Start();
p.WaitForExit();
答案 8 :(得分:0)
一种解决方案可能是尝试使用免费的第三方工具Command Line Parser来设置应用程序以获取特定标记。
例如,您可以按如下方式定义接受的选项:
internal sealed class Options
{
[Option('a', "mainArguments", Required=true, HelpText="The arguments for the main application")]
public String MainArguments { get; set; }
[Option('t', "targetApplication", Required = true, HelpText = "The second application to run.")]
public String TargetApplication { get; set; }
[Option('p', "targetParameters", Required = true, HelpText = "The arguments to pass to the target application.")]
public String targetParameters { get; set; }
[ParserState]
public IParserState LastParserState { get; set; }
[HelpOption]
public string GetUsage()
{
return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current));
}
}
然后可以在Program.cs中使用,如下所示:
static void Main(string[] args)
{
Options options = new Options();
var parser = new CommandLine.Parser();
if (parser.ParseArgumentsStrict(args, options, () => Environment.Exit(-2)))
{
Run(options);
}
}
private static void Run(Options options)
{
String mainArguments = options.MainArguments;
// Do whatever you want with your main arguments.
String quotedTargetParameters = String.Format("\"{0}\"", options.TargetParameters);
Process targetProcess = Process.Start(options.TargetApplication, quotedTargetParameters);
}
然后你可以在命令行上调用它:
myApp -a mainArgs -t targetApp -p "target app parameters"
这可以避免尝试弄清楚哪个应用程序的参数是什么,同时也允许用户以他们想要的任何顺序指定它们。如果你决定在路上添加另一个参数,你可以轻松地做到这一点而不会破坏一切。
编辑:更新了运行方法,以包括在目标参数周围添加引号的功能。