Bagaimana cara mengurai argumen baris perintah di Jawa?


586

Apa cara yang baik untuk mem-parsing argumen baris perintah di Jawa?


4
Lihat args4j dan contoh terperinci bagaimana menggunakannya: martin-thoma.com/how-to-parse-command-line-arguments-in-java
Martin Thoma

Sepertinya saya cukup terlambat ke pesta ini, tapi saya menulis pengolah argumen baris perintah untuk Java dan menaruhnya di GitHub: MainArgsHandler . Adapun utas sedang ditutup, saya pikir ini adalah utas yang sangat membantu, tetapi mungkin harus dimigrasikan ke situs Pemrogram Stack Exchange untuk diskusi pemrograman umum.
Bobulous

@RedGlyph - Sepertinya SO / SE perlu menyederhanakan aturan mereka. Pertanyaannya seharusnya: How to parse java command line arguments?. Tapi tidak ada yang benar-benar ingin menulis kode untuk melakukan ini, melainkan menggunakan alat. Tetapi mencari alat dan sejenisnya tidak konstruktif :(
AlikElzin-kilaka

6
Dipilih untuk dibuka kembali. @ AllikElzin: Memang, mereka perlu meninjau proses moderasi mereka. Saya curiga ada lencana untuk menutup begitu banyak pertanyaan, dan memikat orang yang ingin menjadi moderator terlalu bersemangat.
RedGlyph

8
Pertanyaan ini adalah honeypot untuk jawaban yang buruk / satu baris dan rekomendasi alat. Itu harus tetap ditutup.
JAL

Jawaban:


405

Lihat ini:

Atau gulir sendiri:


Misalnya, ini adalah bagaimana Anda gunakan commons-cliuntuk mem-parsing 2 argumen string:

import org.apache.commons.cli.*;

public class Main {


    public static void main(String[] args) throws Exception {

        Options options = new Options();

        Option input = new Option("i", "input", true, "input file path");
        input.setRequired(true);
        options.addOption(input);

        Option output = new Option("o", "output", true, "output file");
        output.setRequired(true);
        options.addOption(output);

        CommandLineParser parser = new DefaultParser();
        HelpFormatter formatter = new HelpFormatter();
        CommandLine cmd;

        try {
            cmd = parser.parse(options, args);
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            formatter.printHelp("utility-name", options);

            System.exit(1);
        }

        String inputFilePath = cmd.getOptionValue("input");
        String outputFilePath = cmd.getOptionValue("output");

        System.out.println(inputFilePath);
        System.out.println(outputFilePath);

    }

}

penggunaan dari baris perintah:

$> java -jar target/my-utility.jar -i asd                                                                                       
Missing required option: o

usage: utility-name
 -i,--input <arg>    input file path
 -o,--output <arg>   output file

40
Perhatikan bahwa tidak seperti banyak pustaka Apache lainnya, Apache CLI tidak memiliki dependensi.
Vladimir Dyuzhev

9
Satu-satunya downside ke banyak proyek apache-commons adalah mereka mendapatkan lebih sedikit dan lebih sedikit komit dan akhirnya berakhir usang.
Brett Ryan

4
Inilah halaman "Skenario Penggunaan" untuk proyek Apache CLI, merinci cara cepat mulai menggunakannya: commons.apache.org/cli/usage.html
Brad Parks

Saya tidak mengira ada satu persis seperti Args4J / JCommander kecuali di mana Anda mendefinisikan antarmuka dan menjelaskan metode? Saya tidak pernah bisa menyukai kelas yang bidang privatnya "menakutkan"
Trejkaz

5
@RemkoPopma perpustakaan picocli Anda tampak hebat dan terima kasih telah melakukannya, sungguh. Tetapi saya menganggap apa yang Anda lakukan di sini dan di pos lain (edit jawaban yang diterima dan promosikan perpustakaan Anda di bagian atasnya bahkan tanpa mengungkapkannya itu bukan pengeditan dari penulis asli kiriman dan ini lib Anda) penyalahgunaan mengerikan kekuatan moderasi Anda. Memberi tanda ini ke mod lain.
Alexander Malakhov

312

Lihatlah JCommander yang lebih baru .

Saya membuatnya. Saya senang menerima pertanyaan atau permintaan fitur.


7
Senang Anda menyukai JCommander :-) Saya tidak ingin menambahkan terlalu banyak semantik untuk bagaimana bendera diperlakukan, jadi Anda hanya perlu menambahkan sinonim dalam anotasi yang Anda gunakan: @Parameter (names = {"-h", "- -help "}) Saya pikir ini kompromi yang masuk akal.
Cedric Beust

14
Alat hebat. Kuat, fleksibel, dan Anda tidak harus berurusan dengan parser opsi tradisional yang menjengkelkan.
IanGilham

3
Yup, saya pikir saya akan menulis argumen baris perintah parser saya sendiri dengan cara yang sama persis seperti Anda menulis JCommander. Kerja bagus.
SRG

9
@CedricBeust, ini adalah perpustakaan yang brilian, terima kasih banyak. Karena kita dapat mendefinisikan kelas Args kita sendiri yang kemudian dapat diedarkan tanpa ketergantungan pada kelas perpustakaan itu membuatnya sangat fleksibel.
Brett Ryan

2
meniupkan kompetisi keluar dari air!
Marcus Junius Brutus

235

7
@ Ben Flynn hehe, ada beberapa roda berbentuk cukup mengejutkan dan menarik di sana. Saya kira ini cara yang paling tidak berbahaya untuk menunjukkan bahwa ada lebih dari satu cara untuk melakukannya!
lexicalscope

14
Saya perhatikan penulis JOpt Simple memiliki daftar yang sangat mirip! Yang kami butuhkan adalah mengubah daftar ini menjadi tabel, mencantumkan fitur dan tempat menarik, sehingga kami pengguna yang miskin dapat membuat pilihan yang tepat.
Tom Anderson

1
Saya telah membangun Rop - github.com/ryenus/rop , yang menampilkan solusi berbasis anotasi yang Anda nyatakan perintah dan opsi melalui kelas dan bidang sederhana, cukup banyak cara deklaratif untuk membangun parser baris perintah. itu dapat membangun aplikasi seperti Git (single-cmd) atau Maven (multi-cmd).
ryenus

8
Sebagian besar proyek yang terdaftar pada dasarnya adalah meninggalkan perangkat. Setelah melalui daftar saya akan mengatakan pemukul besar, yang secara aktif dipelihara dan populer, tampaknya commons-cli, jcommander, args4j, jopt-simple dan picocli. Permintaan maaf kepada penulis hal-hal seperti argparse4j dan cli-parser - Saya harus membuat peringkat yang agak sewenang-wenang, dan memilih lima besar, jelas proyek-proyek lain dalam daftar itu populer dan masih dalam pengembangan aktif juga.
George Hawkins

2
Saya menantang seseorang untuk memasukkan tanggal rilis stabil terakhir dari masing-masing pengurai.
Sledge

50

Ini tahun 2020, waktu untuk melakukan yang lebih baik daripada Commons CLI ... :-)

Haruskah Anda membuat parser baris perintah Java Anda sendiri, atau menggunakan perpustakaan?

Banyak aplikasi kecil yang mirip utilitas mungkin memutar parsing baris perintah mereka sendiri untuk menghindari ketergantungan eksternal tambahan. picocli mungkin menjadi alternatif yang menarik.

Picocli adalah pustaka dan kerangka kerja modern untuk membangun aplikasi baris perintah yang kuat, mudah digunakan, dan mendukung GraalVM dengan mudah. Ia hidup dalam 1 file sumber sehingga aplikasi dapat memasukkannya sebagai sumber untuk menghindari penambahan ketergantungan.

Ini mendukung warna, pelengkapan otomatis, sub-perintah, dan banyak lagi. Ditulis di Jawa, dapat digunakan dari Groovy, Kotlin, Scala, dll.

Bantuan penggunaan minimal dengan warna ANSI

Fitur:

  • Berbasis anotasi: deklaratif , menghindari duplikasi dan mengekspresikan maksud programmer
  • Nyaman: parse input pengguna dan jalankan logika bisnis Anda dengan satu baris kode
  • Sangat diketik semuanya - opsi baris perintah serta parameter posisi
  • Opsi pendek berkerumun POSIX ( <command> -xvfInputFiledan juga <command> -x -v -f InputFile)
  • Kontrol berbutir halus : model arity yang memungkinkan jumlah parameter minimum, maksimum dan variabel, misalnya "1..*","3..5"
  • Sub-perintah (dapat disarangkan ke kedalaman sewenang-wenang)
  • Kaya fitur : grup arg yang dapat dikomposisikan, yang membagi argumen yang dikutip, sub-perintah berulang, dan banyak lagi
  • User-friendly : pesan bantuan penggunaan menggunakan warna untuk membedakan elemen-elemen penting seperti nama opsi dari sisa penggunaan bantuan untuk mengurangi beban kognitif pada pengguna
  • Distribusikan aplikasi Anda sebagai gambar asli GraalVM
  • Bekerja dengan Java 5 dan lebih tinggi
  • Dokumentasi yang luas dan teliti

Pesan bantuan penggunaan mudah disesuaikan dengan anotasi (tanpa pemrograman). Sebagai contoh:

Pesan bantuan penggunaan diperpanjang( sumber )

Saya tidak bisa menolak menambahkan satu tangkapan layar lagi untuk menunjukkan pesan bantuan penggunaan apa yang mungkin. Bantuan penggunaan adalah wajah aplikasi Anda, jadi jadilah kreatif dan bersenang-senang!

demo picocli

Penafian: Saya membuat picocli. Umpan balik atau pertanyaan sangat disambut.


5
Jenius murni! Sayang sekali jawaban ini terkubur di bagian bawah. Apache Commons CLI adalah verbose, buggy, dan belum diperbarui dalam waktu yang lama. Dan saya tidak ingin menggunakan parser CLI Google karena saya tidak ingin iklan bertarget berdasarkan riwayat penggunaan argumen baris perintah saya. Tapi itu terlihat sedikit lebih verbose daripada picocli.
Pete

2
Saya kedua @Pete di sini ... Saya pergi melalui daftar di atas yang benar-benar membuang-buang waktu dengan ini terkubur di bagian bawah. Ini harus menjadi jawaban teratas sejauh satu mil. Kerja bagus! Persyaratan saya tidak dapat dicakup oleh CLI apache atau sebagian besar parser lainnya. Mereka menantang bahkan untuk picocli tetapi mampu memberi saya hal yang paling dekat dengan sintaks / perilaku yang saya inginkan dan cukup fleksibel untuk meretas ke apa yang benar-benar saya butuhkan. Sebagai bonus, ini sangat bagus berkat barang-barang ANSI.
Shai Almog

@ShaiAlmog Jawaban terpilih adalah 10 tahun dan ketinggalan zaman. Saya setuju bahwa merekomendasikan Commons CLI pada tahun 2019 adalah IMHO yang menyesatkan. Harap pertimbangkan untuk menulis ulang jawaban teratas untuk menjadikannya lebih terkini.
Remko Popma

Saya melihat Anda sudah mencobanya, saya pikir itu tidak akan berhasil untuk saya juga ... Jika Anda akan memilih nama seperti 1Picoli kita bisa mengurutkan jawaban ke-3 sesuai abjad ;-)
Shai Almog

Saya pikir alasan perubahan saya dikembalikan adalah karena saya tidak mengungkapkan afiliasi saya (bahwa saya penulisnya). Orang lain seharusnya tidak memiliki masalah itu.
Remko Popma

23

Saya telah menggunakan JOPT dan merasa cukup berguna: http://jopt-simple.sourceforge.net/

Halaman depan juga menyediakan daftar sekitar 8 perpustakaan alternatif, periksa dan pilih satu yang paling sesuai dengan kebutuhan Anda.


21

Seseorang menunjuk saya ke args4j akhir-akhir ini yang didasarkan pada penjelasan. Aku benar-benar menyukainya!


1
+1 untuk Args4J! Sangat ramah manusia, fleksibel, dan mudah dipahami. Saya pikir itu harus menjadi perpustakaan standar untuk membangun aplikasi Java CLI.
Zearin

Sangat bagus karena dapat menangani cetak penggunaan yang tidak teratur (diurutkan berdasarkan pesanan lapangan), yang tidak bisa dilakukan oleh JCommander, dan lebih fleksibel.
Daniel Hári

@ DanielHári Sekadar informasi, fungsi ini ditambahkan di JCommander (sekitar Februari februari 2017).
Nathan

9

Ini adalah parsing library baris perintah Google yang bersumber terbuka sebagai bagian dari proyek Bazel. Secara pribadi saya pikir itu yang terbaik di luar sana, dan jauh lebih mudah daripada Apache CLI.

https://github.com/pcj/google-options

Instalasi

Bazel

maven_jar(
    name = "com_github_pcj_google_options",
    artifact = "com.github.pcj:google-options:jar:1.0.0",
    sha1 = "85d54fe6771e5ff0d54827b0a3315c3e12fdd0c7",
)

Gradle

dependencies {
  compile 'com.github.pcj:google-options:1.0.0'
}

Maven

<dependency>
  <groupId>com.github.pcj</groupId>
  <artifactId>google-options</artifactId>
  <version>1.0.0</version>
</dependency>

Pemakaian

Buat kelas yang memperluas OptionsBasedan mendefinisikan @Option(s) Anda.

package example;

import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;

import java.util.List;

/**
 * Command-line options definition for example server.
 */
public class ServerOptions extends OptionsBase {

  @Option(
      name = "help",
      abbrev = 'h',
      help = "Prints usage info.",
      defaultValue = "true"
    )
  public boolean help;

  @Option(
      name = "host",
      abbrev = 'o',
      help = "The server host.",
      category = "startup",
      defaultValue = ""
  )
  public String host;

  @Option(
    name = "port",
    abbrev = 'p',
    help = "The server port.",
    category = "startup",
    defaultValue = "8080"
    )
    public int port;

  @Option(
    name = "dir",
    abbrev = 'd',
    help = "Name of directory to serve static files.",
    category = "startup",
    allowMultiple = true,
    defaultValue = ""
    )
    public List<String> dirs;

}

Pecahkan argumen dan gunakan.

package example;

import com.google.devtools.common.options.OptionsParser;
import java.util.Collections;

public class Server {

  public static void main(String[] args) {
    OptionsParser parser = OptionsParser.newOptionsParser(ServerOptions.class);
    parser.parseAndExitUponError(args);
    ServerOptions options = parser.getOptions(ServerOptions.class);
    if (options.host.isEmpty() || options.port < 0 || options.dirs.isEmpty()) {
      printUsage(parser);
      return;
    }

    System.out.format("Starting server at %s:%d...\n", options.host, options.port);
    for (String dirname : options.dirs) {
      System.out.format("\\--> Serving static files at <%s>\n", dirname);
    }
  }

  private static void printUsage(OptionsParser parser) {
    System.out.println("Usage: java -jar server.jar OPTIONS");
    System.out.println(parser.describeOptions(Collections.<String, String>emptyMap(),
                                              OptionsParser.HelpVerbosity.LONG));
  }

}

https://github.com/pcj/google-options


Hai Paul. Ketika saya membaca jawaban Anda atau dokumentasi proyek Anda, saya tidak tahu jenis perintah apa yang bisa ditangani. Misalnya, Anda dapat memberikan sesuatu seperti myexecutable -c file.json -d 42 --outdir ./out. Dan saya tidak melihat bagaimana Anda mendefinisikan opsi deskripsi pendek / panjang / ... Cheers
olibre



7

Saya tahu kebanyakan orang di sini akan menemukan 10 juta alasan mengapa mereka tidak menyukai cara saya, tetapi tidak pernah. Saya suka menjaga hal-hal sederhana, jadi saya hanya memisahkan kunci dari nilai menggunakan '=' dan menyimpannya dalam HashMap seperti ini:

Map<String, String> argsMap = new HashMap<>();
for (String arg: args) {
    String[] parts = arg.split("=");
    argsMap.put(parts[0], parts[1]);
} 

Anda selalu dapat mempertahankan daftar dengan argumen yang Anda harapkan, untuk membantu pengguna jika dia lupa argumen atau menggunakan yang salah ... Namun, jika Anda ingin terlalu banyak fitur, solusi ini bukan untuk Anda.







4

Jika Anda sudah menggunakan Spring Boot, parsing argumen keluar dari kotak.

Jika Anda ingin menjalankan sesuatu setelah startup, terapkan ApplicationRunnerantarmuka:

@SpringBootApplication
public class Application implements ApplicationRunner {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(ApplicationArguments args) {
    args.containsOption("my-flag-option"); // test if --my-flag-option was set
    args.getOptionValues("my-option");     // returns values of --my-option=value1 --my-option=value2 
    args.getOptionNames();                 // returns a list of all available options
    // do something with your args
  }
}

runMetode Anda akan dipanggil setelah konteksnya berhasil dimulai.

Jika Anda memerlukan akses ke argumen sebelum menjalankan konteks aplikasi Anda, Anda cukup menguraikan argumen aplikasi secara manual:

@SpringBootApplication
public class Application implements ApplicationRunner {

  public static void main(String[] args) {
    ApplicationArguments arguments = new DefaultApplicationArguments(args);
    // do whatever you like with your arguments
    // see above ...
    SpringApplication.run(Application.class, args);
  }

}

Dan akhirnya, jika Anda membutuhkan akses ke argumen Anda dalam kacang, cukup menyuntikkan ApplicationArguments:

@Component
public class MyBean {

   @Autowired
   private ApplicationArguments arguments;

   // ...
}

Ini luar biasa :).
John Humphreys - w00te

3

Argparse4j adalah yang terbaik yang saya temukan. Ini meniru perpustakaan argparse Python yang sangat nyaman dan kuat.


2

Jika Anda menginginkan sesuatu yang ringan (ukuran tabung ~ 20 kb) dan mudah digunakan, Anda dapat mencoba parser argumen . Ini dapat digunakan di sebagian besar kasus penggunaan, mendukung menentukan array dalam argumen dan tidak memiliki ketergantungan pada pustaka lain. Ini berfungsi untuk Java 1.5 atau lebih tinggi. Kutipan di bawah ini menunjukkan contoh cara menggunakannya:

public static void main(String[] args) {
    String usage = "--day|-d day --mon|-m month [--year|-y year][--dir|-ds directoriesToSearch]";
    ArgumentParser argParser = new ArgumentParser(usage, InputData.class);
    InputData inputData = (InputData) argParser.parse(args);
    showData(inputData);

    new StatsGenerator().generateStats(inputData);
}

Lebih banyak contoh dapat ditemukan di sini


1
Tautan sudah mati. Apakah Anda membunuh proyek Anda? :-(
beldaz

2

Saya tidak akan merekomendasikan menggunakan Apache Common CLIperpustakaan, karena ini adalah non-threadsafe.

Ia menggunakan kelas stateful dengan variabel statis dan metode untuk melakukan pekerjaan internal (misalnya OptionBuilder) dan hanya boleh digunakan dalam situasi berulir kuat yang dikontrol.


18
Ada baiknya untuk diingat bahwa perpustakaan CLI tidak aman untuk thread. Namun, saya akan menganggap parsing baris perintah biasanya dilakukan dalam satu utas selama startup aplikasi, dan kemudian, tergantung pada parameter, utas lainnya dapat dimulai.
Alexey Ivanov

0

Sebagai salah satu komentar yang disebutkan sebelumnya ( https://github.com/pcj/google-options ) akan menjadi pilihan yang baik untuk memulai.

Satu hal yang ingin saya tambahkan adalah:

1) Jika Anda mengalami kesalahan refleksi parser, coba gunakan versi jambu yang lebih baru. dalam hal ini:

maven_jar(
    name = "com_google_guava_guava",
    artifact = "com.google.guava:guava:19.0",
    server = "maven2_server",
)

maven_jar(
    name = "com_github_pcj_google_options",
    artifact = "com.github.pcj:google-options:jar:1.0.0",
    server = "maven2_server",
)

maven_server(
    name = "maven2_server",
    url = "http://central.maven.org/maven2/",
)

2) Saat menjalankan commandline:

bazel run path/to/your:project -- --var1 something --var2 something -v something

3) Saat Anda membutuhkan bantuan penggunaan, ketikkan:

bazel run path/to/your:project -- --help

-1
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.