Мога ли да получа аргументите към молбата си в оригиналната форма (напр. включително кавички)?

Имам .Net приложение, което приема куп аргументи от командния ред, обработва част от тях и използва останалите като аргументи за друго приложение

E.g.

MyApp.exe foo1 App2.exe arg1 arg2 ...

MyApp.exe е моето приложение, foo1 е параметър, който се грижи за моето приложение. App2.exe е друго приложение и моето приложение ще изпълнява App2 с arg1 arg2 и т.н. като аргументи.

В момента моето приложение просто изпълнява App2.exe, използвайки нещо подобно

Process.Start(args[1], String.Join(" ", args.Skip(2)). Така горната команда ще се изпълни правилно: App2.exe с аргументи "arg1 arg2". Помислете обаче за нещо подобно

MyApp.exe foo1 notepad.exe "C:\Program Files\readme.txt"

Кодът по-горе няма да разпознае кавичките и ще изпълни notepad.exe с аргументи C:\Program Files\readme.txt (без кавички). Как мога да поправя този проблем?


person Louis Rhys    schedule 09.05.2013    source източник
comment
Можете ли да опитате MyApp.exe foo1 "notepad.exe \"C:\Program Files\readme.txt\" "? Ще трябва да видите как можете да кодирате двойни кавички.   -  person Akash Kava    schedule 04.09.2013


Отговори (9)


Environment.CommandLine

ще ви даде точния команден ред - ще трябва да анализирате пътя до вашето приложение, но иначе работи като чар - @idle_mind намекна за това по-рано (нещо като)

Редактирано, за да се премести пример в отговор (защото хората очевидно все още търсят този отговор). Имайте предвид, че при отстраняване на грешки vshost обърква малко командния ред.

#if DEBUG             
    int bodgeLen = "\"vshost.\"".Length; 
#else             
    int bodgeLen = "\"\"".Length; 
#endif              
string a = Environment.CommandLine.Substring(Assembly.GetExecutingAssembly().Location.Lengt‌​h+bodgeLen).Trim();
person mp3ferret    schedule 02.09.2013
comment
някаква идея как лесно да анализирам пътя до приложението? - person Louis Rhys; 04.09.2013
comment
нещо като: Environment.CommandLine.Substring(Assembly.GetExecutingAssembly().Location.Length+bodgeLen).Trim() - person mp3ferret; 04.09.2013
comment
съжалявам, трябваше да поставя остатъка от кода. Трябва да е толкова просто, колкото по-горе - но отстраняването на грешки въпреки vshost го обърква. използвай това: #if DEBUG int bodgeLen = "\"vshost.\"".Length; #else int bodgeLen = "\"\"".Length; #endif string a = Environment.CommandLine.Substring(Assembly.GetExecutingAssembly().Location.Length+bodgeLen).Trim(); - person mp3ferret; 04.09.2013
comment
това работи, просто не работи, когато се изпълняват .net основни приложения в режим на отстраняване на грешки. - person Michael Brown; 24.04.2020

Ще трябва да промените MyApp, за да оградите всички аргументи с кавички.

Накратко, новият код трябва да бъде следният:

var argsToPass = args.Skip(2).Select(o => "\"" + o.Replace("\"", "\\\"") + "\"");
Process.Start(args[1], String.Join(" ", argsToPass);

Логиката е следната:

  1. всеки аргумент трябва да бъде ограден с кавички, така че ако се обаждате с:

    MyApp.exe foo1 notepad.exe "C:\Program Files\readme.txt"

    Приложението ще се нарича по следния начин:

    notepad.exe "C:\Program Files\readme.txt"

  2. всеки аргумент трябва да избягва кавичките (ако има такива), така че ако се обаждате с:

    MyApp.exe foo1 notepad.exe "C:\Program Files\Some Path with \"Quote\" here\readme.txt"

    Приложението ще се нарича по следния начин:

    notepad.exe "C:\Program Files\Some Path with \"Quote\" here\readme.txt"

person csg    schedule 01.09.2013

Използвайте Environment.GetCommandLine(), тъй като ще запази параметър в кавички заедно като един аргумент.

person Idle_Mind    schedule 09.05.2013
comment
това ще доведе до същия проблем като мен, когато използвам аргументи string[]. - person Louis Rhys; 04.09.2013

Е, простият отговор е просто да поставите всеки аргумент в кавички, когато извиквате MyApp2.exe. Няма да навреди да обвиете аргументи, които са една дума, и ще коригира случая, че има интервали в аргумента.

Единственото нещо, което може да се обърка е, ако аргументът вече има екраниран цитат в него.

person Theo Belaire    schedule 09.05.2013

Можете да използвате обратни наклонени черти за екраниращи кавички. по-долу ще работи

MyApp.exe foo1 notepad.exe \"C:\Program Files\readme.txt\"

Горното ще бъде най-доброто решение, ако нямате представа кои други бивши ще избягат и какви са аргументите, които очакват. В този случай не можете да добавяте цитати от вашата програма.

дайте инструкции за добавяне на обратни наклонени черти, когато има кавички, когато изпълнявате приложението си

person Damith    schedule 09.05.2013

Благодарение на @mp3ferret за правилната идея. Но нямаше пример за решение, използващо Environment.CommandLine, така че продължих и създадох OriginalCommandLine клас, който ще получи аргументите на командния ред, както са въведени първоначално.

Аргументът се дефинира в регулярния израз tokenizer като низ в двойни кавички от всякакъв тип знак или низ без кавички от знаци без интервали. В рамките на низовете в кавички символът за кавички може да бъде екраниран с обратна наклонена черта. Въпреки това обратна наклонена черта в края, последвана от двойна кавичка и след това бяло пространство, няма да бъдат екранирани.

Причината, поради която избрах изключението за бягство поради празно пространство, беше да приспособя кавички, които завършват с обратна наклонена черта. Вярвам, че е много по-малко вероятно да се натъкнете на ситуация, в която всъщност бихте искали екранираните двойни кавички.

Код

static public class OriginalCommandLine
{
    static Regex tokenizer = new Regex(@"""(?:\\""(?!\s)|[^""])*""|[^\s]+");
    static Regex unescaper = new Regex(@"\\("")(?!\s|$)");
    static Regex unquoter = new Regex(@"^\s*""|""\s*$");
    static Regex quoteTester = new Regex(@"^\s*""(?:\\""|[^""])*""\s*$");

    static public string[] Parse(string commandLine = null)
    {
        return tokenizer.Matches(commandLine ?? Environment.CommandLine).Cast<Match>()
            .Skip(1).Select(m => unescaper.Replace(m.Value, @"""")).ToArray();
    }

    static public string UnQuote(string text)
    {
        return (IsQuoted(text)) ? unquoter.Replace(text, "") : text;
    }

    static public bool IsQuoted(string text)
    {
        return text != null && quoteTester.Match(text).Success;
    }
}

Резултати

Както можете да видите от резултатите по-долу, корекциите на горния метод поддържат кавичките, като същевременно се справят по-елегантно с реалистичен сценарий, който може да срещнете.

Test:
    ConsoleApp1.exe foo1 notepad.exe "C:\Progra\"m Files\MyDocuments\"  "C:\Program Files\bar.txt"

    args[]:
[0]: foo1
[1]: notepad.exe
[2]: C:\Progra"m Files\MyDocuments"  C:\Program
[3]: Files\bar.txt

    CommandLine.Parse():
[0]: foo1
[1]: notepad.exe
[2]: "C:\Progra"m Files\MyDocuments\"
[3]: "C:\Program Files\bar.txt"

Накрая

Обсъждах използването на алтернативна схема за избягване на двойни кавички. Смятам, че използването на "" е по-добро, като се има предвид, че командните редове толкова често се занимават с обратни наклонени черти. Запазих метода за екраниране на обратната наклонена черта, защото той е обратно съвместим с това как обикновено се обработват аргументите на командния ред.

Ако искате да използвате тази схема, направете следните промени в регулярните изрази:

static Regex tokenizer = new Regex(@"""(?:""""|[^""])*""|[^\s]+");
static Regex unescaper = new Regex(@"""""");
static Regex unquoter = new Regex(@"^\s*""|""\s*$");
static Regex quoteTester = new Regex(@"^\s*""(?:""""|[^""])*""\s*$");

Ако искате да се доближите до това, което очаквате от args, но с непокътнати кавички, променете двата регулярни израза. Все още има малка разлика, "abc"d ще върне abcd от args, но [0] = "abc", [1] = d от моето решение.

static Regex tokenizer = new Regex(@"""(?:\\""|[^""])*""|[^\s]+");
static Regex unescaper = new Regex(@"\\("")");

Ако наистина, наистина искате да получите същия брой елементи като args, използвайте следното:

static Regex tokenizer = new Regex(@"(?:[^\s""]*""(?:\\""|[^""])*"")+|[^\s]+");
static Regex unescaper = new Regex(@"\\("")");

Резултат от точно съвпадение

Test: "zzz"zz"Zzz" asdasd zz"zzz" "zzz"

args               OriginalCommandLine
-------------      -------------------
[0]: zzzzzZzz      [0]: "zzz"zz"Zzz"
[1]: asdasd        [1]: asdasd
[2]: zzzzz         [2]: zz"zzz"
[3]: zzz           [3]: "zzz"
person Daniel Gimenez    schedule 05.09.2013
comment
+1, но ConsoleApp1.exe foo anotherApp \"My Arg\" не работи, \My Arg\ се извлича по някакъв начин като My Arg\ - person Louis Rhys; 05.09.2013
comment
@LouisRhys, това е измислен случай. Защо бихте направили това вместо само ConsoleApp1.exe foo anotherApp "My Arg"? Причината да се държи по този начин е, че би било по-вероятно аргумент, завършващ на \", да бъде част от път. Бих могъл да опростя отговора си, за да се приспособя към това, но вярвам, че това, което предоставих, е от по-голяма полза. Няма идеален отговор, но вярвам, че вторият метод за бягство на дъното е най-добрият за всички вероятни случаи. - person Daniel Gimenez; 05.09.2013
comment
@LouisRhys, ще добавя малко в края на отговора си, за да съответства на точното поведение, което бихте очаквали от args. - person Daniel Gimenez; 05.09.2013

Опитайте следното.

Този код запази символите с двойни кавички, както и даде възможност за екраниране на символите \ и " (вижте коментарите в кода по-долу).

static void Main(string[] args)
{
    // This project should be compiled with "unsafe" flag!
    Console.WriteLine(GetRawCommandLine());
    var prms = GetRawArguments();
    foreach (var prm in prms)
    {
        Console.WriteLine(prm);
    }
}

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern System.IntPtr GetCommandLine();
public static string GetRawCommandLine()
{
    // Win32 API
    string s = Marshal.PtrToStringAuto(GetCommandLine());

    // or better, managed code as suggested by @mp3ferret
    // string s = Environment.CommandLine;
    return s.Substring(s.IndexOf('"', 1) + 1).Trim();
}

public static string[] GetRawArguments()
{
    string cmdline = GetRawCommandLine();

    // Now let's split the arguments. 
    // Lets assume the fllowing possible escape sequence:
    // \" = "
    // \\ = \
    // \ with any other character will be treated as \
    //
    // You may choose other rules and implement them!

    var args = new ArrayList();
    bool inQuote = false;
    int pos = 0;
    StringBuilder currArg = new StringBuilder();
    while (pos < cmdline.Length)
    {
        char currChar = cmdline[pos];

        if (currChar == '"')
        {
            currArg.Append(currChar);
            inQuote = !inQuote;
        }
        else if (currChar == '\\')
        {
            char nextChar = pos < cmdline.Length - 1 ? cmdline[pos + 1] : '\0';
            if (nextChar == '\\' || nextChar == '"')
            {
                currArg.Append(nextChar);
                pos += 2;
                continue;
            }
            else
            {
                currArg.Append(currChar);
            }
        }
        else if (inQuote || !char.IsWhiteSpace(currChar))
        {
            currArg.Append(currChar);
        }
        if (!inQuote && char.IsWhiteSpace(currChar) && currArg.Length > 0)
        {
            args.Add(currArg.ToString());
            currArg.Clear();
        }
        pos++;
    }

    if (currArg.Length > 0)
    {
        args.Add(currArg.ToString());
        currArg.Clear();
    }
    return (string[])args.ToArray(typeof(string));
}
person Ilan    schedule 02.09.2013

Опитайте с "\". Трябва да предам и url като аргументи, това е начинът:

_filenameDestin и _zip са url адреси. Надявам се да помогне.

string ph = "\"";
var psi = new ProcessStartInfo();
psi.Arguments = "a -r " + ph + _filenameDestin + ".zip " + ph + _filenameDestin + ph;
psi.FileName = _zip;
var p = new Process();
p.StartInfo = psi;
p.Start();
p.WaitForExit();
person soydachi    schedule 04.09.2013

Едно решение може да бъде да опитате да използвате Command Line Parser, безплатен инструмент на трета страна, за да настроите приложението си да вземете конкретни знамена.

Например, можете да дефинирате приетите опции, както следва:

    internal sealed class Options
    {
        [Option('a', "mainArguments", Required=true, HelpText="The arguments for the main application")]
        public String MainArguments { get; set; }

        [Option('t', "targetApplication", Required = true, HelpText = "The second application to run.")]
        public String TargetApplication { get; set; }

        [Option('p', "targetParameters", Required = true, HelpText = "The arguments to pass to the target application.")]
        public String targetParameters { get; set; }

        [ParserState]
        public IParserState LastParserState { get; set; }


        [HelpOption]
        public string GetUsage()
        {
            return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current));
        }
    }

Което след това може да се използва във вашия Program.cs, както следва:

    static void Main(string[] args)
    {
        Options options = new Options();
        var parser = new CommandLine.Parser();

        if (parser.ParseArgumentsStrict(args, options, () => Environment.Exit(-2)))
        {
            Run(options);
        }
    }


private static void Run(Options options)
{
    String mainArguments = options.MainArguments;
    // Do whatever you want with your main arguments.

    String quotedTargetParameters = String.Format("\"{0}\"", options.TargetParameters);   
    Process targetProcess = Process.Start(options.TargetApplication, quotedTargetParameters);
}

След това ще го извикате от командния ред по следния начин:

myApp -a mainArgs -t targetApp -p "target app parameters"

Това премахва всички догадки от опитите да разберете какво е аргумент за кое приложение, като същевременно позволява на вашия потребител да ги посочи в какъвто ред пожелае. И ако решите да добавите друг аргумент по пътя, можете лесно да го направите, без да нарушавате всичко.

РЕДАКТИРАНЕ: Актуализиран метод Run, за да включва възможност за добавяне на кавички около целевите параметри.

person aleppke    schedule 04.09.2013