在c#中转义命令行参数

hul*_*ist 72 .net c# command-line-arguments

精简版:

它是足够用引号括参数和逃生\"

代码版本

我想string[] args使用ProcessInfo.Arguments 将命令行参数传递给另一个进程.

ProcessStartInfo info = new ProcessStartInfo();
info.FileName = Application.ExecutablePath;
info.UseShellExecute = true;
info.Verb = "runas"; // Provides Run as Administrator
info.Arguments = EscapeCommandLineArguments(args);
Process.Start(info);
Run Code Online (Sandbox Code Playgroud)

问题是我将参数作为数组获取,并且必须将它们合并为单个字符串.可以设计一个参数来欺骗我的程序.

my.exe "C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry"
Run Code Online (Sandbox Code Playgroud)

根据这个答案,我创建了以下函数来逃避单个参数,但我可能错过了一些东西.

private static string EscapeCommandLineArguments(string[] args)
{
    string arguments = "";
    foreach (string arg in args)
    {
        arguments += " \"" +
            arg.Replace ("\\", "\\\\").Replace("\"", "\\\"") +
            "\"";
    }
    return arguments;
}
Run Code Online (Sandbox Code Playgroud)

这是否足够好或者是否有任何框架功能?

Nas*_*nov 64

虽然它比这更复杂!

我遇到了相关问题(编写前端.exe会调用后端传递所有参数+一些额外的参数)所以我看了人们如何做到这一点,遇到了你的问题.最初所有人都像你建议的那样做得很好arg.Replace (@"\", @"\\").Replace(quote, @"\"+quote).

然而,当我与参数来调用c:\temp a\\b,这被作为传递c:\tempa\\b,从而导致后端被调用"c:\\temp" "a\\\\b"-这是不正确的,因为这将是两个参数 c:\\tempa\\\\b-不是我们想要的!我们在逃脱中过于热心(窗户不是unix!).

所以我详细阅读了http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx,它实际上描述了这些案例的处理方式:反斜杠在双面前处理为转义引用.

在那里如何\处理多个是有一个扭曲,解释可以留下一段时间的头晕.我会尝试在这里重新表达所说的unescape规则:说我们有一个N 的子串\,然后是".当取消转换时,我们用int(N/2) 替换那个子串,\iff N是奇数,我们"在最后添加.

这种解码的编码就是这样:对于一个参数,找到0或更多的每个子字符串,\然后"将其替换为两次\,然后是\".我们可以这样做:

s = Regex.Replace(arg, @"(\\*)" + "\"", @"$1$1\" + "\"");
Run Code Online (Sandbox Code Playgroud)

就这样...

PS....... 不是.等等,等等 - 还有更多!:)

我们正确地进行了编码,但是有一个转折,因为你将所有参数括在双引号中(如果其中一些参数中有空格).存在边界问题 - 如果参数结束\,"在它之后添加将破坏结束引用的含义.实例c:\one\ two解析,c:\one\two随后将被重新装配到"c:\one\" "two"能在我(MIS)理解为一个参数c:\one" two(我试过了,我不是捏造出来的).所以我们还需要检查参数是否结束\,如果是,最后加上反斜杠的数量加倍,如下所示:

s = "\"" + Regex.Replace(s, @"(\\+)$", @"$1$1") + "\"";
Run Code Online (Sandbox Code Playgroud)

  • +1用于解释这种精神错乱.但是上面的匹配表达式中的分组括号中的`*`和`+`是否*不应该?否则`$ 1`替换只会是一个反斜杠. (6认同)
  • 仅供参考:http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx (2认同)

Mat*_*vic 29

我的回答类似于Nas Banov的回答,但我只想在必要时使用双引号.

削减额外不必要的双引号

我的代码不必要地在它周围放置双引号,这很重要*当你接近参数的字符限制时.

/// <summary>
/// Encodes an argument for passing into a program
/// </summary>
/// <param name="original">The value that should be received by the program</param>
/// <returns>The value which needs to be passed to the program for the original value 
/// to come through</returns>
public static string EncodeParameterArgument(string original)
{
    if( string.IsNullOrEmpty(original))
        return original;
    string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
    value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
    return value;
}

// This is an EDIT
// Note that this version does the same but handles new lines in the arugments
public static string EncodeParameterArgumentMultiLine(string original)
{
    if (string.IsNullOrEmpty(original))
        return original;
    string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
    value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"", RegexOptions.Singleline);

    return value;
}
Run Code Online (Sandbox Code Playgroud)

说明

要正确地转义反斜杠双引号,您只需替换多个反斜杠的任何实例,后跟一个双引号:

string value = Regex.Replace(original, @"(\\*)" + "\"", @"\$1$0");
Run Code Online (Sandbox Code Playgroud)

额外两倍原始反斜杠 + 1和原始双引号.即'\'+ originalbackslashes + originalbackslashes +'"'.我使用$ 1 $ 0,因为$ 0有原始的反斜杠和原始的双引号,所以它使得替换更好看.

value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
Run Code Online (Sandbox Code Playgroud)

这只能匹配包含空格的整行.

如果它匹配,那么它会在开头和结尾添加双引号.

如果在参数末尾有最初的反斜杠,则它们将不会被引用,现在它们需要在结尾处有双引号.所以它们是重复的,引用它们全部,并防止无意中引用最后的双引号

它为第一部分进行最小匹配,以便最后一个.*?不会吃掉最后的反斜杠

产量

因此,这些输入产生以下输出

你好

你好

\你好\ 12\3 \

\你好\ 12\3 \

你好,世界

"你好,世界"

\"你好\"

\\"你好\\\"

\"你好,世界

"\\"你好,世界"

\"你好,世界\

"\\"你好,世界\\"

你好,世界\\

"你好,世界\\\\"


Jer*_*ray 6

我也遇到了这个问题.我没有解析args,而是采用了完整的原始命令行并修剪了可执行文件.这具有在呼叫中保持空白的额外好处,即使不需要/使用它也是如此.它仍然必须追逐可执行文件中的转义,但这似乎比args更容易.

var commandLine = Environment.CommandLine;
var argumentsString = "";

if(args.Length > 0)
{
    // Re-escaping args to be the exact same as they were passed is hard and misses whitespace.
    // Use the original command line and trim off the executable to get the args.
    var argIndex = -1;
    if(commandLine[0] == '"')
    {
        //Double-quotes mean we need to dig to find the closing double-quote.
        var backslashPending = false;
        var secondDoublequoteIndex = -1;
        for(var i = 1; i < commandLine.Length; i++)
        {
            if(backslashPending)
            {
                backslashPending = false;
                continue;
            }
            if(commandLine[i] == '\\')
            {
                backslashPending = true;
                continue;
            }
            if(commandLine[i] == '"')
            {
                secondDoublequoteIndex = i + 1;
                break;
            }
        }
        argIndex = secondDoublequoteIndex;
    }
    else
    {
        // No double-quotes, so args begin after first whitespace.
        argIndex = commandLine.IndexOf(" ", System.StringComparison.Ordinal);
    }
    if(argIndex != -1)
    {
        argumentsString = commandLine.Substring(argIndex + 1);
    }
}

Console.WriteLine("argumentsString: " + argumentsString);
Run Code Online (Sandbox Code Playgroud)


sti*_*til 5

我已经用错误的方式Everyone quotes命令行参数移植了C ++函数。

它工作正常,但您应注意,cmd.exe命令行的解释有所不同。如果(并且仅当,如所述文章的原始作者所述)您的命令行将被解释时,cmd.exe您还应该转义外壳元字符。

/// <summary>
///     This routine appends the given argument to a command line such that
///     CommandLineToArgvW will return the argument string unchanged. Arguments
///     in a command line should be separated by spaces; this function does
///     not add these spaces.
/// </summary>
/// <param name="argument">Supplies the argument to encode.</param>
/// <param name="force">
///     Supplies an indication of whether we should quote the argument even if it 
///     does not contain any characters that would ordinarily require quoting.
/// </param>
private static string EncodeParameterArgument(string argument, bool force = false)
{
    if (argument == null) throw new ArgumentNullException(nameof(argument));

    // Unless we're told otherwise, don't quote unless we actually
    // need to do so --- hopefully avoid problems if programs won't
    // parse quotes properly
    if (force == false
        && argument.Length > 0
        && argument.IndexOfAny(" \t\n\v\"".ToCharArray()) == -1)
    {
        return argument;
    }

    var quoted = new StringBuilder();
    quoted.Append('"');

    var numberBackslashes = 0;

    foreach (var chr in argument)
    {
        switch (chr)
        {
            case '\\':
                numberBackslashes++;
                continue;
            case '"':
                // Escape all backslashes and the following
                // double quotation mark.
                quoted.Append('\\', numberBackslashes*2 + 1);
                quoted.Append(chr);
                break;
            default:
                // Backslashes aren't special here.
                quoted.Append('\\', numberBackslashes);
                quoted.Append(chr);
                break;
        }
        numberBackslashes = 0;
    }

    // Escape all backslashes, but let the terminating
    // double quotation mark we add below be interpreted
    // as a metacharacter.
    quoted.Append('\\', numberBackslashes*2);
    quoted.Append('"');

    return quoted.ToString();
}
Run Code Online (Sandbox Code Playgroud)