簡體   English   中英

將包含命令行參數的字符串拆分為Java中的String []

[英]Split a string containing command-line parameters into a String[] in Java

C#的這個線程類似,我需要將包含命令行參數的字符串拆分到我的程序中,這樣我就可以讓用戶輕松運行多個命令。 例如,我可能有以下字符串:

-p /path -d "here's my description" --verbose other args

鑒於上述情況,Java通常會將以下內容傳遞給main:

Array[0] = -p
Array[1] = /path
Array[2] = -d
Array[3] = here's my description
Array[4] = --verbose
Array[5] = other
Array[6] = args

我不需要擔心任何shell擴展,但它必須足夠聰明,以處理單引號和雙引號以及字符串中可能存在的任何轉義。 有沒有人知道在這些條件下shell會解析字符串的方法?

:我不需要做命令行解析,我已經使用joptsimple做到這一點。 相反,我想讓我的程序易於編寫腳本。 例如,我希望用戶能夠在一個文件中放置一組命令,每個命令在命令行上都是有效的。 例如,他們可能會在文件中鍵入以下內容:

--addUser admin --password Admin --roles administrator,editor,reviewer,auditor
--addUser editor --password Editor --roles editor
--addUser reviewer --password Reviewer --roles reviewer
--addUser auditor --password Auditor --roles auditor

然后用戶將運行我的管理工具,如下所示:

adminTool --script /path/to/above/file

然后main()會找到--script選項並迭代文件中的不同行,將每一行拆分成一個數組,然后我將在一個joptsimple實例中反射,然后將該數據傳遞給我的應用程序驅動程序。

joptsimple帶有一個具有parse方法的Parser,但它只支持一個String數組。 類似地, GetOpt構造函數也需要String[] - 因此需要解析器。

這是將文本行從文件拆分為參數向量的一種非常簡單的替代方法,以便您可以將其提供給選項解析器:

這是解決方案:

public static void main(String[] args) {
    String myArgs[] = Commandline.translateCommandline("-a hello -b world -c \"Hello world\"");
    for (String arg:myArgs)
        System.out.println(arg);
}

魔術類Commandline螞蟻的一部分。 因此,您必須將ant放在類路徑上,或者只是使用Commandline類,因為使用的方法是靜態的。

您應該使用功能齊全的現代對象命令行參數解析器我建議我最喜歡的Java Simple Argument Parser 以及如何使用JSAP ,這是以Groovy為例,但直接Java也是如此。 還有args4j在某些方面比JSAP更現代,因為它使用注釋,遠離apache.commons.cli的東西,它是舊的和破壞的,並且在其API中非常過程和非Java等。 但我仍然依賴於JSAP,因為構建自己的自定義參數處理程序非常容易。

URL,數字,InetAddress,顏色,日期,文件,類等都有很多默認的解析器,添加自己的解析器非常容易。

例如,這是一個將args映射到枚舉的處理程序:

import com.martiansoftware.jsap.ParseException;
import com.martiansoftware.jsap.PropertyStringParser;

/*
This is a StringParser implementation that maps a String to an Enum instance using Enum.valueOf()
 */
public class EnumStringParser extends PropertyStringParser
{
    public Object parse(final String s) throws ParseException
    {
        try
        {
            final Class klass = Class.forName(super.getProperty("klass"));
            return Enum.valueOf(klass, s.toUpperCase());
        }
        catch (ClassNotFoundException e)
        {
            throw new ParseException(super.getProperty("klass") + " could not be found on the classpath");
        }
    }
}

而且我不喜歡通過XML進行配置編程,但JSAP有一種非常好的方法可以在代碼之外聲明選項和設置,因此您的代碼不會被數百行設置混亂,這些設置會混淆和模糊真實的功能代碼,請參閱我的鏈接, 了解如何使用JSAP作為示例,比我嘗試的任何其他庫更少的代碼。

這是您的更新中闡明的問題的方向解決方案, “腳本”文件中的行仍然是命令行。 逐行從文件中讀取它們並調用JSAP.parse(String);

我使用這種技術一直為Web應用程序提供“命令行”功能。 一個特別的用途是在一個帶有Director / Flash前端的大型多人在線游戲中,我們啟用了來自聊天的“命令”,並在后端使用JSAP來解析它們並根據它解析的內容執行代碼。 非常類似於您想要做的事情,除了您從文件而不是套接字讀取“命令”。 我會拋棄joptsimple而只是使用JSAP,你會被它強大的可擴展性所破壞。

如果您只需要支持類UNIX操作系統,那么有一個更好的解決方案 Commandline從螞蟻, ArgumentTokenizer從DrJava更sh式的:它支持逃逸!

說真的,甚至像sh -c 'echo "\\"un'\\''kno\\"wn\\$\\$\\$'\\'' with \\$\\"\\$\\$. \\"zzz\\""'一樣瘋狂 sh -c 'echo "\\"un'\\''kno\\"wn\\$\\$\\$'\\'' with \\$\\"\\$\\$. \\"zzz\\""' sh -c 'echo "\\"un'\\''kno\\"wn\\$\\$\\$'\\'' with \\$\\"\\$\\$. \\"zzz\\""'被正確地標記為[bash, -c, echo "\\"un'kno\\"wn\\$\\$\\$' with \\$\\"\\$\\$. \\"zzz\\""] (順便說一句,當運行時,此命令輸出"un'kno"wn$$$' with $"$$. "zzz" )。

/**
 * [code borrowed from ant.jar]
 * Crack a command line.
 * @param toProcess the command line to process.
 * @return the command line broken into strings.
 * An empty or null toProcess parameter results in a zero sized array.
 */
public static String[] translateCommandline(String toProcess) {
    if (toProcess == null || toProcess.length() == 0) {
        //no command? no string
        return new String[0];
    }
    // parse with a simple finite state machine

    final int normal = 0;
    final int inQuote = 1;
    final int inDoubleQuote = 2;
    int state = normal;
    final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
    final ArrayList<String> result = new ArrayList<String>();
    final StringBuilder current = new StringBuilder();
    boolean lastTokenHasBeenQuoted = false;

    while (tok.hasMoreTokens()) {
        String nextTok = tok.nextToken();
        switch (state) {
        case inQuote:
            if ("\'".equals(nextTok)) {
                lastTokenHasBeenQuoted = true;
                state = normal;
            } else {
                current.append(nextTok);
            }
            break;
        case inDoubleQuote:
            if ("\"".equals(nextTok)) {
                lastTokenHasBeenQuoted = true;
                state = normal;
            } else {
                current.append(nextTok);
            }
            break;
        default:
            if ("\'".equals(nextTok)) {
                state = inQuote;
            } else if ("\"".equals(nextTok)) {
                state = inDoubleQuote;
            } else if (" ".equals(nextTok)) {
                if (lastTokenHasBeenQuoted || current.length() != 0) {
                    result.add(current.toString());
                    current.setLength(0);
                }
            } else {
                current.append(nextTok);
            }
            lastTokenHasBeenQuoted = false;
            break;
        }
    }
    if (lastTokenHasBeenQuoted || current.length() != 0) {
        result.add(current.toString());
    }
    if (state == inQuote || state == inDoubleQuote) {
        throw new RuntimeException("unbalanced quotes in " + toProcess);
    }
    return result.toArray(new String[result.size()]);
}

擴展Andreas_D的答案 ,而不是復制,使用優秀的Plexus Common Utilities庫中的CommandLineUtils.translateCommandline(String toProcess)

我使用Java Getopt端口來完成它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM