我能以原始形式获得应用程序的参数(例如包括引号)吗?

本文关键字:包括引 参数 原始 应用程序 | 更新日期: 2023-09-27 18:12:40

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

MyApp.exe foo1 App2.exe arg1 arg2 ...

MyApp.exe是我的应用程序,foo1是我的应用程序关心的一个参数。App2.exe是另一个应用程序,我的应用程序将运行App2, arg1 arg2等作为参数。

目前我的应用程序只是运行App2.exe使用像这样的东西

Process.Start(args[1], String.Join(" ", args.Skip(2))。因此上面的命令将正确运行:App2.exe,参数为"arg1 arg2"。但是,考虑这样的内容

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

上面的代码将不知道引号,并将运行带有参数C:'Program Files'readme.txt的notepad.exe(不带引号)。我该如何解决这个问题?

我能以原始形式获得应用程序的参数(例如包括引号)吗?

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();

您需要修改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"

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

嗯,简单的答案是在调用MyApp2.exe时将每个参数都括在引号中。对一个单词的参数进行换行也无妨,并且它将修复参数中有空格的情况。

唯一可能出错的是参数中已经有一个转义引号。

转义引号可以使用反斜杠。下面将运行

MyApp.exe foo1 notepad.exe '"C:'Program Files'readme.txt'"
如果你不知道其他前任会参加竞选,也不知道他们会期待什么争论,那么以上就是最好的解决方案。在这种情况下,你不能从你的程序中添加引号。

给指令添加反斜杠时,有引号运行您的应用程序

信用@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将从args返回abcd,但从我的解决方案返回[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"

尝试以下操作。

这段代码保留了双引号字符,并提供了转义'和"字符的选项(参见下面代码中的注释)。

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));
}

Try with "'"。我也必须传递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();

一个解决方案可能是尝试使用命令行解析器,一个免费的第三方工具,设置您的应用程序以获取特定的标志。

例如,您可以这样定义可接受的选项:

    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"

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

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