Escape аргументы командной строки в c#



короткая версия:



достаточно ли завернуть аргумент в кавычки и escape и " ?



код



Я хочу передать аргументы командной строки string[] args к другому процессу с помощью ProcessInfo.Аргументы.



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


проблема в том, что я получаю аргументы в виде массива и должен объединить их в одну строку. Аргументы могут быть созданы, чтобы обмануть мою программу.



my.exe "C:Documents and SettingsMyPath " --kill-all-humans " except fry"


по данным этот ответ я создал следующую функцию, чтобы избежать одного аргумента, но я, возможно, что-то пропустил.



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


это достаточно хорошо, или есть какие-то рамки функция для этого?

605   8  

8 ответов:

это сложнее, чем это, хотя!

у меня была связанная проблема (написание front-end .exe, который будет вызывать серверную часть со всеми переданными параметрами + некоторые дополнительные), и поэтому я посмотрел, как люди это делают, столкнулся с вашим вопросом. Изначально все казалось хорошо делать это, как вы предлагаете arg.Replace (@"\", @"\").Replace(quote, @"\"+quote).

однако когда я звоню с аргументами c:\temp a\b, это передается как c:\temp и a\b, что приводит к СО "c:\temp" "a\\b" - что неверно, потому что там будет два аргумента c:\temp и a\\b - не то, что мы хотели! Мы были чрезмерно усердны в побегах (windows - это не unix!).

и поэтому я читаю подробно http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx и это на самом деле описывает там, как эти случаи обрабатываются: обратные косые черты рассматриваются как escape только перед двойной кавычкой.

есть поворот к нему, как несколько \ обрабатываются там, объяснение может оставить одно головокружение на некоторое время. Я попытаюсь перефразировать сказанное здесь правило unescape: скажем, у нас есть подстрока N\, следовал по ". Когда невыход, мы заменить строкой int (N/2)\ и МФЛ N странно, мы добавляем " в конце.

кодировка для такого декодирования будет выглядеть так: для аргумент, найти каждую подстроку 0-или-больше \ следовал по " и заменить его в два раза больше \, следовал по \". Что мы можем сделать так:

s = Regex.Replace(arg, @"(\*)" + "\"", @"\" + "\"");

вот и все...

PS. ... не. Подождите, подождите - это еще не все! :)

мы сделали кодировку правильно, но есть поворот, потому что вы заключаете все параметры в двойные кавычки (в случае, если в некоторых из них есть пробелы). Есть вопрос этики - в случае параметр заканчивается на \ добавление " после того, как он сломает смысл закрытия цитаты. Пример c:\one\ two разбирается c:\one\ и two затем будет повторно собран в "c:\one\" "two" что будет меня (МИС)понимать как один аргумент c:\one" two (я пробовал, я не выдумываю). Так что нам нужно дополнительно проверить, заканчивается ли аргумент на \ и если да, то двойной количество обратных косых черт в конце, например:

s = "\"" + Regex.Replace(s, @"(\+)$", @"") + "\"";

мой ответ был похож на ответ нас Банова, но я хотел двойные кавычки только в случае необходимости.

вырезание лишних ненужных двойных кавычек

мой код сохраняет излишне положить двойные кавычки вокруг него все время, что важно *когда вы приближаетесь к пределу символов для параметров.

/// <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, @"(\*)" + "\"", @"$0");
    value = Regex.Replace(value, @"^(.*\s.*?)(\*)$", "\"\"");
    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, @"(\*)" + "\"", @"$0");
    value = Regex.Replace(value, @"^(.*\s.*?)(\*)$", "\"\"", RegexOptions.Singleline);

    return value;
}

объяснение

бежать символы и двойная цитаты правильно, вы можете просто заменить все экземпляры несколько символы затем один двойной кавычки С:

string value = Regex.Replace(original, @"(\*)" + "\"", @"$1");

в два раза больше оригинала символы + 1 и оригинал двойной кавычки. то есть, ' \ ' + originalbackslashes + originalbackslashes+'"'. Я использовал $1$0, так как $0 имеет оригинал символы и оригинал двойной кавычки так что это делает замену приятнее один для чтения.

value = Regex.Replace(value, @"^(.*\s.*?)(\*)$", "\"\"");

Это может совпадать только с целой строкой, которая содержит пробелы.

если он соответствует, то он добавляет двойные кавычки до начала и конца.

Если бы было изначально символы в конце аргумента они не будут цитироваться, теперь, когда есть двойной кавычки на конце они должны быть. Таким образом, они дублируются, что цитирует их все, и предотвращает непреднамеренное цитирование финал двойной кавычки

он делает минимальное соответствие для первого раздела, так что последний .* ? не ест в соответствии с окончательным символы

выход

таким образом, эти входы произвести следующие мероприятия

привет

привет

\ hello\12\3\

\ hello\12\3\

Привет, мир

"привет мир"

\"привет\"

\\"привет\\\"

\ " hello \ world

"\\"Привет мир\"

\"Привет\\\ мир\

"\\"Привет\\\ мир\\"

Привет, мир\\

"Привет, мир\\\\"

У меня тоже были проблемы с этим. Вместо того, чтобы распаковывать 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);

Я опубликовал небольшой проект на GitHub, который обрабатывает большинство проблем с кодировкой командной строки / экранированием:

https://github.com/ericpopivker/Command-Line-Encoder

есть CommandLineEncoder.Utils.cs класс, а также модульные тесты, которые проверяют функциональность кодирования/декодирования.

Я написал вам небольшой пример, чтобы показать вам, как использовать escape-символы в командной строке.

public static string BuildCommandLineArgs(List<string> argsList)
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();

    foreach (string arg in argsList)
    {
        sb.Append("\"\"" + arg.Replace("\"", @"\" + "\"") + "\"\" ");
    }

    if (sb.Length > 0)
    {
        sb = sb.Remove(sb.Length - 1, 1);
    }

    return sb.ToString();
}

и вот тестовый метод:

    List<string> myArgs = new List<string>();
    myArgs.Add("test\"123"); // test"123
    myArgs.Add("test\"\"123\"\"234"); // test""123""234
    myArgs.Add("test123\"\"\"234"); // test123"""234

    string cmargs = BuildCommandLineArgs(myArgs);

    // result: ""test\"123"" ""test\"\"123\"\"234"" ""test123\"\"\"234""

    // when you pass this result to your app, you will get this args list:
    // test"123
    // test""123""234
    // test123"""234

дело в том, чтобы обернуть каждый arg с двойными двойными кавычками (""arg"") и заменить все кавычки внутри значения arg экранированной кавычкой (test\ " 123 ).

я портировал функцию 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();
}
static string BuildCommandLineFromArgs(params string[] args)
{
    if (args == null)
        return null;
    string result = "";

    if (Environment.OSVersion.Platform == PlatformID.Unix 
        || 
        Environment.OSVersion.Platform == PlatformID.MacOSX)
    {
        foreach (string arg in args)
        {
            result += (result.Length > 0 ? " " : "") 
                + arg
                    .Replace(@" ", @"\ ")
                    .Replace("\t", "\\t")
                    .Replace(@"\", @"\")
                    .Replace(@"""", @"\""")
                    .Replace(@"<", @"\<")
                    .Replace(@">", @"\>")
                    .Replace(@"|", @"\|")
                    .Replace(@"@", @"\@")
                    .Replace(@"&", @"\&");
        }
    }
    else //Windows family
    {
        bool enclosedInApo, wasApo;
        string subResult;
        foreach (string arg in args)
        {
            enclosedInApo = arg.LastIndexOfAny(
                new char[] { ' ', '\t', '|', '@', '^', '<', '>', '&'}) >= 0;
            wasApo = enclosedInApo;
            subResult = "";
            for (int i = arg.Length - 1; i >= 0; i--)
            {
                switch (arg[i])
                {
                    case '"':
                        subResult = @"\""" + subResult;
                        wasApo = true;
                        break;
                    case '\':
                        subResult = (wasApo ? @"\" : @"\") + subResult;
                        break;
                    default:
                        subResult = arg[i] + subResult;
                        wasApo = false;
                        break;
                }
            }
            result += (result.Length > 0 ? " " : "") 
                + (enclosedInApo ? "\"" + subResult + "\"" : subResult);
        }
    }

    return result;
}

делает хорошую работу по добавлению аргументов, но не убегает. Добавлен комментарий в методе, где escape-последовательность должна идти.

public static string ApplicationArguments()
{
    List<string> args = Environment.GetCommandLineArgs().ToList();
    args.RemoveAt(0); // remove executable
    StringBuilder sb = new StringBuilder();
    foreach (string s in args)
    {
        // todo: add escape double quotes here
        sb.Append(string.Format("\"{0}\" ", s)); // wrap all args in quotes
    }
    return sb.ToString().Trim();
}

Comments

    Ничего не найдено.