4

一連のコマンド ライン引数を受け取り、その一部を処理し、残りを別のアプリケーションの引数として使用する .Net アプリケーションがあります。

例えば

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

MyApp.exe私のアプリケーション foo1です。私のアプリケーションが気にするパラメータです。App2.exeは別のアプリケーションであり、私のアプリケーションは arg1 arg2 などを引数として App2 を実行します。

現在、私のアプリケーションは、このようなものを使用して App2.exe を実行するだけです

Process.Start(args[1], String.Join(" ", args.Skip(2)). したがって、上記のコマンドは正しく実行されます: 引数 "arg1 arg2" を指定した App2.exe。ただし、次のようなことを考慮してください

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

上記のコードは引用符を認識せず、引数 C:\Program Files\readme.txt (引用符なし) を指定して notepad.exe を実行します。この問題を解決するにはどうすればよいですか?

4

9 に答える 9

8
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();
于 2013-09-02T19:23:56.957 に答える
4

引数を引用符で囲むように 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"

于 2013-09-01T08:14:03.470 に答える
1

正しいアイデアを思いついた @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引用符をそのままにして、期待するものに近づけたい場合は、2 つの正規表現を変更してください。まだ小さな違いがありますが、私のソリューションから 返され"abc"dます。abcdargs[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"
于 2013-09-05T03:11:50.537 に答える
0

1 つの解決策は、無料のサードパーティ ツールである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 メソッドを更新して、ターゲット パラメーターの前後に引用符を追加する機能を含めました。

于 2013-09-04T17:21:13.733 に答える
0

以下を試してください。

このコードは、二重引用符文字を保持するだけでなく、\ および " 文字をエスケープするオプションを提供します (以下のコードのコメントを参照)。

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));
}
于 2013-09-02T16:17:54.410 に答える
0

"\"" で試してください。引数として 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();
于 2013-09-04T08:32:05.207 に答える