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

Укороченная версия:

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

Версия кода

Я хочу передать аргументы командной строки 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); 

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

 my.exe "C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry" 

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

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

Достаточно ли это или есть какая-либо функция фреймворка для этого?

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

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

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

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

Существует трюк в том, как несколько \ обрабатываются там, объяснение может оставить головокружение на некоторое время. Я попытаюсь повторно сформулировать это правило unescape: скажем, что у нас есть подстрока из N \ , а затем: " Когда unescaping, мы заменяем эту подстроку на int (N / 2) \ а iff N нечетным, мы добавляем " в конце.

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

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

Это все…

PS. … нет . Подождите, подождите – больше! 🙂

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

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

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

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

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

 ///  /// Encodes an argument for passing into a program ///  /// The value that should be received by the program /// The value which needs to be passed to the program for the original value /// to come through 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; } 

объяснение

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

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

Дополнительно в два раза больше оригинальной обратной косой черты + 1 и оригинальной двойной кавычки . т. е. ‘\’ + originalbackslashes + originalbackslashes + ‘”‘. Я использовал $ 1 $ 0, так как $ 0 имеет исходную обратную косую черту и оригинальную двойную кавычку, что делает замену более приятной для чтения.

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

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

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

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

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

Вывод

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

Здравствуйте

Здравствуйте

\ Привет \ 12 \ 3 \

\ привет \ 12 \ 3 \

Привет мир

“Привет мир”

\”Здравствуйте\”

\\”Здравствуйте\\\”

\”Привет мир

“\\”Привет мир”

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

“\\”Привет мир\\”

Привет мир\\

“Привет мир\\\\”

У меня тоже были проблемы с этим. Вместо unparsing args я пошел с взятием полной исходной командной строки и обрезкой исполняемого файла. Это имело дополнительную выгоду от сохранения пробелов в вызове, даже если оно не требуется / используется. Он по-прежнему должен преследовать escape-последовательности в исполняемом файле, но это оказалось проще, чем аргументы.

 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

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

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

 public static string BuildCommandLineArgs(List 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 myArgs = new List(); 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 с помощью экранированной цитаты (test 123).

Я портировал функцию C ++ из аргументов командной строки «Все» в качестве неправильной статьи.

Он отлично работает, но вы должны заметить, что cmd.exe интерпретирует командную строку по-разному. Если ( и только если , как отметил оригинальный автор статьи), ваша командная строка будет интерпретироваться cmd.exe вы также должны избежать метасимволов оболочки.

 ///  /// 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. ///  /// Supplies the argument to encode. ///  /// Supplies an indication of whether we should quote the argument even if it /// does not contain any characters that would ordinarily require quoting. ///  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 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(); } 
  • Распространять все аргументы в сценарии оболочки bash
  • Как передать аргументы командной строки программе Node.js?
  • Давайте будем гением компьютера.