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;
}
это достаточно хорошо, или есть какие-то рамки функция для этого?
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