Kurzfassung:
Reicht es aus, das Argument in Anführungszeichen zu setzen und zu entkommen \
und "
?
Code-Version
Ich möchte die Befehlszeilenargumente string[] args
mit ProcessInfo.Arguments an einen anderen Prozess übergeben.
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);
Das Problem ist, dass ich die Argumente als Array erhalte und sie zu einer einzigen Zeichenfolge zusammenführen muss. Es könnten Argumente formuliert werden, um mein Programm auszutricksen.
my.exe "C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry"
Nach dieser Antwort habe ich die folgende Funktion erstellt, um einem einzelnen Argument zu entgehen, aber ich habe möglicherweise etwas übersehen.
private static string EscapeCommandLineArguments(string[] args)
{
string arguments = "";
foreach (string arg in args)
{
arguments += " \"" +
arg.Replace ("\\", "\\\\").Replace("\"", "\\\"") +
"\"";
}
return arguments;
}
Ist das gut genug oder gibt es dafür eine Rahmenfunktion?
c#
.net
command-line-arguments
hultqvist
quelle
quelle
"C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry"
wäre keine gute Sache, da ich einen privilegierten Anruf tätige .my.exe "test\"test"
arg [0]test"test
abc"def
diesen bekommst,abc"def
warum willst du ihn jetzt entkommen? Wenn Sie etwas wie "abc" + "" "+" def "hinzufügen, ist dies sinnvoll. beobachten""""
entkommt"
abc"def
ist angesichts der Eingabe korrekt. Wenn ich sie jedoch an einen anderen Prozess übergeben möchte, muss ich sie maskieren, bevor ich sie dem Argument für eine einzelne Zeichenfolge hinzufüge. Weitere Informationen finden Sie in der aktualisierten Frage.Antworten:
Es ist jedoch komplizierter!
Ich hatte ein ähnliches Problem (das Schreiben der Front-End-EXE-Datei, die das Back-End mit allen übergebenen Parametern + einigen zusätzlichen Parametern aufruft) und ich habe mir angesehen, wie die Leute das machen, und bin auf Ihre Frage gestoßen. Anfangs schien alles gut zu sein, wie Sie vorschlagen
arg.Replace (@"\", @"\\").Replace(quote, @"\"+quote)
.Wenn ich jedoch mit Argumenten aufrufe
c:\temp a\\b
, wird dies alsc:\temp
und übergebena\\b
, was dazu führt, dass das Back-End mit aufgerufen wird"c:\\temp" "a\\\\b"
- was falsch ist, weil es zwei Argumente gibtc:\\temp
unda\\\\b
- nicht das, was wir wollten! Wir waren übereifrig bei Fluchten (Windows ist nicht Unix!).Und so las ich im Detail http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx und es beschreibt dort tatsächlich, wie diese Fälle behandelt werden: Backslashes werden nur vor Double behandelt Zitat.
Es gibt eine Wendung darin, wie mehrere
\
dort behandelt werden. Die Erklärung kann einen für eine Weile schwindelig machen. Ich werde versuchen, diese Unescape-Regel hier neu zu formulieren: Sagen wir, wir haben eine Teilzeichenfolge von N\
, gefolgt von"
. Beim Entspannen ersetzen wir diesen Teilstring durch int (N / 2),\
und wenn N ungerade war, fügen wir"
am Ende hinzu.Die Codierung für eine solche Decodierung würde folgendermaßen aussehen: Suchen Sie für ein Argument jede Teilzeichenfolge von 0 oder mehr,
\
gefolgt von"
und ersetzen Sie sie durch doppelt so viele\
, gefolgt von\"
. Was wir so machen können:s = Regex.Replace(arg, @"(\\*)" + "\"", @"$1$1\" + "\"");
Das ist alles...
PS. ... nicht . Warten Sie, warten Sie - es gibt noch mehr! :) :)
Wir haben die Codierung korrekt durchgeführt, aber es gibt eine Wendung, da Sie alle Parameter in doppelte Anführungszeichen setzen (falls in einigen Leerzeichen Leerzeichen enthalten sind). Es gibt ein Grenzproblem - falls ein Parameter endet
\
, wird durch Hinzufügen eines Parameters"
die Bedeutung des schließenden Anführungszeichens aufgehoben. Das Beispiel wurdec:\one\ two
analysiertc:\one\
undtwo
dann wieder zusammengesetzt"c:\one\" "two"
, damit ich (falsch) als ein Argument verstanden werdec:\one" two
(das habe ich versucht, ich mache es nicht nach). Wir müssen also zusätzlich prüfen, ob das Argument endet,\
und wenn ja, die Anzahl der Backslashes am Ende verdoppeln , wie folgt:s = "\"" + Regex.Replace(s, @"(\\+)$", @"$1$1") + "\"";
quelle
*
und das+
nicht in den Gruppierungsklammern in den obigen Übereinstimmungsausdrücken stehen? Andernfalls wird der$1
Ersatz immer nur ein einziger Backslash sein."\""+Regex.Replace(s, "(\\\\*)(\\\\$|\")", "$1$1\\$2")+"\""
. Allerdings beginnt mein Gehirn jetzt zu sinken, so sehr geschätzt, wenn Sie die Richtigkeit überprüfen könnten :-)Meine Antwort war ähnlich wie die von Nas Banov, aber ich wollte nur bei Bedarf doppelte Anführungszeichen .
Schneiden Sie zusätzliche unnötige doppelte Anführungszeichen aus
Mein Code spart unnötig unnötige doppelte Anführungszeichen, was wichtig ist *, wenn Sie sich der Zeichenbeschränkung für Parameter nähern.
/// <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; }
Erläuterung
Um den Backslashes und doppelten Anführungszeichen zu entkommen korrekt zu können Sie einfach alle Instanzen mehrerer Backslashes gefolgt von einem einfachen doppelten Anführungszeichen durch Folgendes ersetzen :
string value = Regex.Replace(original, @"(\\*)" + "\"", @"\$1$0");
Ein zusätzliches Doppel der ursprünglichen Backslashes + 1 und des ursprünglichen doppelten Anführungszeichens . dh '\' + originalbackslashes + originalbackslashes + '"'. Ich habe $ 1 $ 0 verwendet, da $ 0 das Original hat Backslashes und das ursprüngliche doppelte Anführungszeichen enthält, sodass der Ersatz besser lesbar ist.
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
Dies kann immer nur mit einer ganzen Zeile übereinstimmen, die ein Leerzeichen enthält.
Wenn es übereinstimmt , werden am Anfang und am Ende doppelte Anführungszeichen hinzugefügt.
Wenn es ursprünglich Backslashes gab am Ende des Arguments sie nicht zitiert, jetzt, wo es am Ende ein doppeltes Anführungszeichen gibt, müssen sie sein. Sie werden also dupliziert, was sie alle zitiert und verhindert, dass das endgültige doppelte Anführungszeichen unbeabsichtigt zitiert wird
Es wird eine minimale Übereinstimmung für den ersten Abschnitt vorgenommen, so dass der letzte. *? frisst nicht in Übereinstimmung mit den endgültigen Backslashes
Ausgabe
Diese Eingänge erzeugen also die folgenden Ausgänge
Hallo
Hallo
\ hallo \ 12 \ 3 \
\ hallo \ 12 \ 3 \
Hallo Welt
"Hallo Welt"
\"Hallo\"
\\"Hallo\\\"
\"Hallo Welt
"\\"Hallo Welt"
\"Hallo Welt\
"\\"Hallo Welt\\"
Hallo Welt\\
"Hallo Welt\\\\"
quelle
""
anstelle einer leeren Zeichenfolge zurückgeben, damit die Befehlszeile weiß, dass ein Argument vorhanden ist. Davon abgesehen funktioniert das perfekt!<a>\n <b/>\n</a>
. Ausgabe :<a>\n <b/>\n</a>
. Sieht so aus, als würden äußere Qouten fehlen! Mache ich etwas falsch? (\n
bedeutet Newline, natürlich, SO Kommentare sind nicht wirklich Newline-freundlich)Ich hatte auch Probleme damit. Anstatt Argumente zu analysieren, habe ich die vollständige ursprüngliche Befehlszeile übernommen und die ausführbare Datei abgeschnitten. Dies hatte den zusätzlichen Vorteil, dass Leerzeichen im Aufruf beibehalten wurden, auch wenn sie nicht benötigt / verwendet werden. Es muss immer noch Fluchten in der ausführbaren Datei jagen, aber das schien einfacher als die Argumente.
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);
quelle
LPWSTR GetArgStrFromCommandLine(LPWSTR c) {if (*c++ != L'"') c = wcspbrk(--c, L" \t\r\n\v\f"); else while (*c && *c++ != L'"') if (*c == L'\\') ++c; return c;}
Ich habe eine C ++ - Funktion aus den Befehlszeilenargumenten "Jeder zitiert" falsch portiert .
Es funktioniert gut, aber Sie sollten beachten, dass
cmd.exe
die Befehlszeile unterschiedlich interpretiert wird. Wenn ( und nur wenn , wie der ursprüngliche Autor des Artikels angegeben hat) Ihre Befehlszeile voncmd.exe
Ihnen interpretiert wird , sollten Sie auch Shell-Metazeichen maskieren./// <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(); }
quelle
Ich habe ein kleines Projekt auf GitHub veröffentlicht, das die meisten Probleme mit der Befehlszeilencodierung /--Escapezeichen behandelt:
https://github.com/ericpopivker/Command-Line-Encoder
Es gibt eine CommandLineEncoder.Utils.cs- Klasse sowie Komponententests, die die Codierungs- / Decodierungsfunktionalität überprüfen.
quelle
Ich habe Ihnen ein kleines Beispiel geschrieben, um Ihnen zu zeigen, wie Sie Escapezeichen in der Befehlszeile verwenden.
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(); }
Und hier ist eine Testmethode:
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
Der Punkt ist, jedes Argument mit doppelten Anführungszeichen ("" arg "") zu versehen und alle Anführungszeichen innerhalb des arg-Werts durch ein maskiertes Anführungszeichen (test "123") zu ersetzen.
quelle
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; }
quelle
Fügt gute Argumente hinzu, entkommt aber nicht. Kommentar in Methode hinzugefügt, wohin die Escape-Sequenz gehen soll.
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(); }
quelle
my.exe "arg1\" \"arg2"
ein einziges Argument gebenarg1" "arg2
würde, würde Ihr Code zwei Argumente erzeugenarg1
undarg2
arg1" "arg2
obwohl ich mir nicht vorstellen kann, warum. Ihr Recht, ich hätte sowieso dort fliehen sollen, ich werde diesen Thread sehen, um zu sehen, wer den besten Mechanismus dafür findet.John "The Boss" Smith
Ein alternativer Ansatz
Wenn Sie ein komplexes Objekt wie verschachteltes JSON übergeben und die Kontrolle über das System haben, das die Befehlszeilenargumente empfängt, ist es viel einfacher, die Befehlszeilenargumente einfach als base64 zu codieren und sie dann vom empfangenden System zu decodieren.
Siehe hier: Encode / Decode String zu / von Base64
Anwendungsfall: Ich musste ein JSON-Objekt übergeben, das eine XML-Zeichenfolge in einer der Eigenschaften enthielt, deren Flucht zu kompliziert war. Das hat es gelöst.
quelle
Kopieren Sie die Beispielcodefunktion von dieser URL:
http://csharptest.net/529/how-to-correctly-escape-command-line-arguments-in-c/index.html
Sie können die Befehlszeile beispielsweise folgendermaßen ausführen lassen:
String cmdLine = EscapeArguments(Environment.GetCommandLineArgs().Skip(1).ToArray());
Skip(1)
Überspringt den Namen der ausführbaren Datei.quelle