4

我有一个 .Net 应用程序,它接受一堆命令行参数,处理其中的一些,然后将其余的用作另一个应用程序的参数

例如

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。我该如何解决这个问题?

4

9 回答 9

8
Environment.CommandLine

会给你确切的命令行——你必须解析出你的应用程序的路径,否则就像一个魅力——@idle_mind 之前提到过这个(有点)

编辑以将示例移动到答案中(因为人们显然仍在寻找这个答案)。请注意,在调试 vshost 时会稍微弄乱命令行。

#if DEBUG             
    int bodgeLen = "\"vshost.\"".Length; 
#else             
    int bodgeLen = "\"\"".Length; 
#endif              
string a = Environment.CommandLine.Substring(Assembly.GetExecutingAssembly().Location.Lengt‌​h+bodgeLen).Trim();
于 2013-09-02T19:23:56.957 回答
4

您将需要修改 MyApp 以将任何参数括在引号中。

短篇小说,新代码应该是这样的:

var argsToPass = args.Skip(2).Select(o => "\"" + o.Replace("\"", "\\\"") + "\"");
Process.Start(args[1], String.Join(" ", argsToPass);

逻辑是这样的:

  1. 每个参数都应该用引号括起来,所以如果你用 :

    MyApp.exe foo1 notepad.exe "C:\Program Files\readme.txt"

    该应用程序将以这种方式调用:

    notepad.exe "C:\Program Files\readme.txt"

  2. 每个参数都应该转义引号(如果有的话),所以如果你打电话:

    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"

于 2013-09-01T08:14:03.470 回答
2

使用Environment.GetCommandLine()因为它会将引号中的参数作为一个参数保留在一起。

于 2013-05-09T04:23:49.387 回答
1

嗯,简单的答案是在调用 MyApp2.exe 时将每个参数用引号括起来。包装一个单词的参数并没有什么坏处,它可以解决参数中有空格的情况。

唯一可能出错的是参数中是否已经包含转义引号。

于 2013-05-09T03:01:22.447 回答
1

您可以使用反斜杠作为转义引号。下面会工作

MyApp.exe foo1 notepad.exe \"C:\Program Files\readme.txt\"

如果您不知道要运行哪些其他 exe 以及他们期望的论点是什么,上述将是最好的解决方案。在这种情况下,您无法从程序中添加引号。

在运行应用程序时,如果有引号,请给出添加反斜杠的说明

于 2013-05-09T03:15:32.463 回答
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返回。 abcdargs[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"
于 2013-09-05T03:11:50.537 回答
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));
}
于 2013-09-02T16:17:54.410 回答
0

一种解决方案可能是尝试使用免费的第 3 方工具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"

这消除了试图找出哪个应用程序的参数是什么的所有猜测,同时还允许您的用户以他们想要的任何顺序指定它们。而且,如果您决定在以后添加另一个参数,您可以轻松地这样做而不会破坏所有内容。

编辑:更新了运行方法,包括在目标参数周围添加引号的能力。

于 2013-09-04T17:21:13.733 回答
0

尝试使用“\””。我也必须作为参数 url 传递,这是这样的:

_filenameDestin 和 _zip 是 url。我希望它有所帮助。

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();
于 2013-09-04T08:32:05.207 回答