86 votes

Échapper arguments de ligne de commande en c#

Version courte:

Est-ce assez pour envelopper l'argument de citations et d'échapper \ et " ?

Version du Code de

Je veux passer les arguments de ligne de commande string[] args à un autre processus à l'aide de ProcessInfo.Les 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);

Le problème est que je reçois les arguments sous la forme d'un tableau et doivent fusionner en une seule et même chaîne. Un des arguments pourrait être conçu pour tromper mon programme.

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

Selon cette réponse , j'ai créé la fonction suivante pour échapper à un seul argument, mais j'ai peut-être manqué quelque chose.

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

Est-ce assez bon ou est-il un cadre de la fonction pour cela?

72voto

Nas Banov Points 7293

C'est plus compliqué que cela!

J'ai eu des problème lié (écrit avant la fin de l' .exe qui va appeler le back-end avec tous les paramètres transmis à + supplémentaire) et donc j'ai cherché comment les gens font que, couru dans votre question. Au départ tout semblait bon de le faire comme vous le suggérez, arg.Replace (@"\", @"\\").Replace(quote, @"\"+quote).

Cependant quand je l'appelle avec des arguments c:\temp a\\b, cela devient passé en c:\temp et a\\b, ce qui conduit à la fin d'être appelé avec l' "c:\\temp" "a\\\\b" - ce qui est incorrect, car il y qui seront deux arguments c:\\temp et a\\\\b - pas ce que nous voulions! Nous avons été trop zélés dans échappe (windows n'est pas unix!).

Et j'ai donc lu en détail http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx et il décrit en fait il y a combien de ces cas sont traités: les barres obliques inverses sont traités comme s'échapper seul en face de guillemets doubles.

Il y a un twist dans la façon dont plusieurs \ sont traités là, l'explication peut laisser un étourdi pendant un moment. Je vais essayer de reformuler dit ne pas encoder la règle ici: disons que nous avons une sous-chaˆ ıne de N \, suivie par l' ". Lorsque unescaping, nous remplaçons qui sous-chaîne avec int(N/2) \ et le forum N est impair, nous ajoutons " à la fin.

L'encodage de ces décodage irait comme ça: pour un argument, trouver chaque sous-chaîne de 0 ou plus \ suivie par " et de la remplacer par deux fois-comme-nombre \, suivie par l' \". Qui on peut faire comme ceci:

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

C'est tout...

PS. ... pas. Attendre, attendre - il n'y est plus! :)

Nous avons fait le codage correctement, mais il y a un twist parce que vous êtes en joignant tous les paramètres en double-guillemets (dans le cas où il y a des espaces dans certains d'entre eux). Il y a un problème de limite - dans le cas d'un paramètre se termine \, l'ajout d' " après ça va casser le sens de la fermeture de devis. Exemple c:\one\ two analysée à l' c:\one\ et two alors être ré-assemblé à l' "c:\one\" "two" qui m' (mal)compris comme un argument c:\one" two (j'ai essayé, je ne suis pas qui la composent). Donc, ce que nous avons besoin de plus est de vérifier si l'argument se termine \ et, dans l'affirmative, double le nombre de barres obliques inverses à la fin, comme ceci:

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

34voto

Matt Vukomanovic Points 114

Ma réponse était similaire à Nas Banov de la réponse, mais je voulais guillemets uniquement si nécessaire.

La découpe supplémentaire inutile de guillemets doubles

Mon code enregistre inutilement de mettre des guillemets autour d'elle tout le temps ce qui est important *lorsque vous êtes près de la limite de caractères pour les paramètres.

/// <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;
}

explication

Pour échapper à l' barres obliques inverses et les guillemets correctement, vous pouvez remplacer toutes les instances de plusieurs antislash suivi d'un seul guillemet double avec:

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

Un supplément de deux fois l'original barres obliques inverses + 1 et l'origine des guillemets doubles. c'est à dire, '\' + originalbackslashes + originalbackslashes + '"'. J'ai utilisé $1$0 puisque $0 a l'origine barres obliques inverses et l'original de la citation double , si bien que le remplacement de la plus agréable pour lire.

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

Cela ne peut jamais correspondre à la totalité d'une ligne qui contient un espace.

Si elle correspond, puis il ajoute des guillemets au début et à la fin.

Si il a été à l'origine des barres obliques inverses sur la fin de l'argument qu'ils n'auront pas été cité, maintenant qu'il y a un guillemet double sur la fin, ils ont besoin de l'être. Donc, ils sont dupliqués, qui les cite tous, et empêche involontairement citant le dernier guillemet double

Il fait un minimum correspondant à la première section ainsi que la dernière .*? ne pas manger en correspondance de la finale des barres obliques inverses

Sortie

Donc, ces entrées d'obtenir les résultats suivants

bonjour

bonjour

\bonjour\12\3\

\bonjour\12\3\

bonjour tout le monde

"bonjour le monde"

\"bonjour\"

\\"bonjour\\\"

\"bonjour\ monde

"\\"bonjour\ monde"

\"bonjour\\\ monde\

"\\"bonjour\\\ monde\\"

bonjour tout le monde\\

"hello world\\\\"

7voto

Jeremy Murray Points 616

J'ai été en cours d'exécution dans des problèmes avec cela, aussi. Au lieu de unparsing args, je suis allé à la prise de la pleine ligne de commande et coupez l'exécutable. Cela a eu supplémentaires bebefit de maintenir l'espace à l'appel, même si elle n'est pas requis/utilisé. Il a encore de chase s'échappe dans l'exécutable, mais cela semblait plus facile que le 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);

4voto

Eric P Points 1427

J'ai publié petit projet sur GitHub qui gère la plupart des problèmes avec la ligne de commande de codage/d'échappement:

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

Il y a un CommandLineEncoder.Utils.cs de la classe, ainsi que les Tests Unitaires qui permettent de vérifier l'Encodage/Décodage de la fonctionnalité.

2voto

HABJAN Points 5681

Je vous ai écrit un petit exemple pour vous montrer comment utiliser échapper les caractères dans la ligne de commande.

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

Et ici est une méthode de test:

    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

Le point est d'envelopper chaque arg avec double-guillemets doubles ( ""arg"" ) et de le remplacer tous les guillemets à l'intérieur arg valeur avec échappé à la citation ( test de\"123 ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X