Apa kebiasaan baik untuk merancang argumen baris perintah?

190

Saat mengembangkan aplikasi saya mulai bertanya-tanya - Bagaimana saya harus merancang argumen baris perintah?

Banyak program menggunakan rumus seperti ini -argument valueatau /argument value. Solusi yang muncul di benak saya adalah argument:value. Saya pikir itu baik karena tanpa spasi putih tidak ada cara yang bisa mengacaukan nilai dan argumen. Juga mudah untuk membagi string menjadi dua pada karakter pertama dari :karakter kiri .

Pertanyaan saya adalah:

  1. Apakah -argument valueformula populer lebih baik daripada argument:value(lebih mudah dibaca, lebih mudah ditulis, bebas bug, lebih mudah dipahami oleh pengembang ahli)?
  2. Apakah ada beberapa aturan umum yang harus saya ikuti saat mendesain argumen baris perintah (selain jika berfungsi, itu OK)?

Diminta beberapa detail lagi saya akan berikan. Namun saya pikir mereka seharusnya tidak mempengaruhi jawaban. Pertanyaannya adalah tentang kebiasaan yang baik secara umum. Saya pikir mereka semua sama untuk semua jenis aplikasi.

Kami sedang mengerjakan aplikasi yang akan digunakan di tempat umum (sentuh totem, tabel). Aplikasi ditulis menggunakan Qt Quick 5 (C ++, QML, JS). Perangkat akan menginstal Windows 8.1 / 10. Kami akan menyediakan antarmuka ujung depan untuk mengelola perangkat. Namun beberapa administrator tingkat lanjut mungkin ingin mengkonfigurasi aplikasi sendiri. Ini tidak terlalu penting dari sisi bisnis tetapi karena saya setuju dengan apa yang dikatakan Kilian Foth , saya tidak ingin aplikasi saya menjadi masalah bagi pengguna. Tidak menemukan di internet apa yang saya inginkan, saya bertanya di sini.


Untuk pengguna Stack Exchange yang lebih maju: Saya ingin pertanyaan ini bersifat umum. Mungkin itu memenuhi syarat untuk komunitas wiki (saya tidak tahu apakah pertanyaan yang ada dapat dikonversi dengan jawaban). Karena saya ingin pertanyaan ini menjadi sistem operasi dan bahasa pemrograman independen jawaban yang muncul di sini bisa menjadi pelajaran berharga bagi pengembang lain.

Filip Hazubski
sumber
14
Lihat ke alat baris perintah populer. Misalnya, tanda hubung tunggal sering digunakan untuk memungkinkan opsi menggabungkan. misalnya Anda dapat menulis ls -ltruntuk menggabungkan opsi -l, -tdan -r. Program gaya GNU juga biasanya memungkinkan opsi berbasis kata dengan tanda hubung ganda --reversesebagai ganti -r. Ada konvensi populer lainnya seperti -hmenunjukkan bantuan, --memberi tanda pada akhir opsi, menetapkan -sebagai nama file untuk memungkinkan membaca dari stdin, dll.
Brandin
72
Keduanya -argument valuetidak -argument:valuebiasa. Umum adalah -a value, -avalue, dan --argument=value.
reinierpost
39
Gunakan parsing perpustakaan baris perintah populer (biasanya disebut sesuatu seperti getopt(s)) di mana Anda bisa.
reinierpost
5
@ k3b Kami bekerja dengan Qt. Seperti kata kevin cline dalam komentarnya, kita dapat menggunakan perpustakaan yang sudah tersedia Saya kira itu multi-platform dan dipikirkan dengan baik. QCommandLineParser
Filip Hazubski
11
Masalah dengan uraian terakhir Anda adalah bahwa penguraian argumen bukan merupakan masalah platform-independen.
pydsigner

Jawaban:

237

Pada sistem POSIX (mis. Linux, MacOSX), setidaknya untuk program yang mungkin dimulai pada terminal shell (mis. Sebagian besar dari mereka), saya akan merekomendasikan menggunakan konvensi pengkodean GNU (yang juga mencantumkan nama argumen umum) dan melihat ke dalam pedoman utilitas POSIX , bahkan untuk perangkat lunak berpemilik:

  • selalu menangani --versiondan--help (bahkan /bin/truemenerimanya !!). Saya mengutuk penulis perangkat lunak tidak mengerti --help, saya benci mereka (karena prog --help adalah perintah pertama yang saya coba pada program baru)! Seringkali --helpdapat disingkat-h

  • Minta --helppesan mencantumkan semua opsi (kecuali Anda memiliki terlalu banyak di antaranya ... dalam hal ini cantumkan yang paling umum dan secara eksplisit merujuk ke beberapa manhalaman atau beberapa URL) dan nilai default opsi, dan mungkin penting (dan spesifik program) ) variabel lingkungan. Tampilkan daftar opsi ini pada kesalahan argumen opsi.

  • menerima -aargumen singkat (huruf) dan memiliki beberapa setara --long-argument, jadi -a2 --long-argument=2, --long-argument 2; tentu saja Anda dapat memiliki (untuk opsi yang jarang digunakan) beberapa --only-long-argumentnama; untuk argumen modal tanpa opsi tambahan -cfumumnya ditangani sebagai -c -f, dll. jadi -argument:valueproposal Anda aneh, dan saya tidak menyarankan melakukan itu.

  • gunakan GLIBC getopt_long atau lebih baik (mis. argp_parse , di OCaml Argmodulnya , ...)

  • sering digunakan -untuk input atau output standar (jika Anda tidak dapat melakukannya, pegang /dev/stdin& /dev/stdoutbahkan pada beberapa sistem operasi yang tidak memilikinya)

  • meniru perilaku program serupa dengan menggunakan kembali sebagian besar konvensi opsi mereka; khususnya -nuntuk dry run (à la make), -huntuk bantuan, -vuntuk kata kerja, dll ...

  • gunakan --sebagai pemisah antara opsi & file atau argumen lainnya

  • jika program Anda menggunakan isattyuntuk menguji daripada stdin adalah terminal (dan berperilaku "interaktif" dalam kasus itu), berikan opsi untuk memaksakan mode non-interaktif, juga jika program Anda memiliki antarmuka GUI (dan tes getenv("DISPLAY")pada desktop X11) tetapi juga bisa digunakan dalam batch atau command line.

  • Beberapa program (misalnya gcc) menerima daftar argumen tidak langsung, demikian @somefile.txtjuga artinya membaca argumen program dari somefile.txt; ini bisa berguna ketika program Anda mungkin menerima banyak argumen (lebih banyak dari kernel Anda ARG_MAX)

BTW, Anda bahkan dapat menambahkan beberapa fasilitas lengkapi-otomatis untuk program Anda dan shell yang biasa (seperti bashatau zsh)

Beberapa perintah Unix lama (misalnya dd, atau bahkan sed) memiliki argumen perintah aneh untuk kompatibilitas historis. Saya akan merekomendasikan untuk tidak mengikuti kebiasaan buruk mereka (kecuali jika Anda membuat varian yang lebih baik dari mereka).

Jika perangkat lunak Anda adalah serangkaian program baris perintah terkait, mengambil inspirasi dari git (yang pasti Anda gunakan sebagai alat pembangunan), yang menerima git helpdan git --helpdan memiliki banyak gitsubcommanddangitsubcommand--help

Dalam kasus yang jarang terjadi, Anda mungkin juga menggunakan argv[0](dengan menggunakan symlink pada program Anda), misalnya bashdipanggil karena rbashmemiliki perilaku yang berbeda ( shell terbatas ). Tetapi saya biasanya tidak merekomendasikan melakukan itu; mungkin masuk akal jika program Anda dapat digunakan sebagai penerjemah skrip menggunakan shebang yaitu #!pada baris pertama yang ditafsirkan oleh exec (2) . Jika Anda melakukan trik semacam itu, pastikan untuk mendokumentasikannya, termasuk dalam --helppesan.

Ingat bahwa pada POSIX shell adalah globbing argumen ( sebelum menjalankan program Anda!), Jadi hindari meminta karakter (seperti *atau $atau ~) dalam opsi yang perlu shell-melarikan diri.

Dalam beberapa kasus, Anda dapat menyematkan juru bahasa seperti GNU Chile atau Lua di perangkat lunak Anda (hindari menciptakan bahasa skrip lengkap Turing sendiri jika Anda tidak ahli dalam bahasa pemrograman). Ini memiliki konsekuensi mendalam pada desain perangkat lunak Anda (jadi harus dipikirkan sejak dini!). Anda kemudian dapat dengan mudah memberikan beberapa skrip atau ekspresi kepada penerjemah itu. Jika Anda mengambil pendekatan yang menarik, rancang perangkat lunak Anda dan primitifnya yang ditafsirkan dengan hati-hati; Anda bisa memiliki beberapa pengguna aneh yang mengkodekan skrip besar untuk hal Anda.

Dalam kasus lain, Anda mungkin ingin membiarkan pengguna tingkat lanjut memuat plugin mereka ke perangkat lunak Anda (menggunakan teknik pemuatan dinamis à la dlopen& dlsym). Sekali lagi, ini adalah keputusan desain yang sangat penting (jadi tentukan dan dokumentasikan antarmuka plugin dengan hati-hati), dan Anda harus menentukan konvensi untuk meneruskan opsi program ke plugin ini.

Jika perangkat lunak Anda adalah hal yang rumit, buat ia menerima beberapa file konfigurasi (sebagai tambahan atau penggantian argumen program) dan mungkin memiliki beberapa cara untuk menguji (atau hanya menguraikan) file-file konfigurasi ini tanpa menjalankan semua kode. Sebagai contoh, agen transfer surat (seperti Exim atau Postfix) cukup kompleks, dan berguna untuk dapat "setengah kering" menjalankannya (misalnya mengamati bagaimana ia menangani beberapa alamat email yang diberikan tanpa benar-benar mengirim email).


Perhatikan bahwa /optionini adalah hal Windows atau VMS. Ini akan menjadi gila pada sistem POSIX (karena hirarki file digunakan /sebagai pemisah direktori, dan karena shell melakukan globbing). Semua jawaban saya sebagian besar untuk Linux (dan POSIX).


PS Jika memungkinkan, jadikan program Anda perangkat lunak gratis , Anda akan mendapatkan peningkatan dari beberapa pengguna & pengembang (dan menambahkan opsi program baru sering kali merupakan salah satu hal termudah untuk ditambahkan ke perangkat lunak gratis yang ada). Juga, pertanyaan Anda tergantung banyak pada penonton dimaksudkan : permainan untuk remaja atau browser untuk nenek mungkin tidak perlu jenis yang sama dan jumlah pilihan dari compiler, atau seorang inspektur jaringan untuk sysadmin datacenter, atau perangkat lunak CAD untuk mikroprosesor arsitek atau desainer jembatan. Seorang insinyur yang akrab dengan pemrograman & skrip mungkin lebih suka memiliki banyak opsi merdu dari nenek Anda, dan mungkin ingin menjalankan aplikasi Anda tanpa X11 (mungkin dalam crontabpekerjaan).

Basile Starynkevitch
sumber
18
Setuju, tetapi git merupakan contoh yang lebih baik. Saya tidak akan merekomendasikan bahkan melihat ke cvsdalam 2016.
Basile Starynkevitch
8
+ dalam hal opsi tidak valid hanya tampilkan bantuan. Sungguh menjengkelkan untuk mendapatkan pesan kesalahan yang tidak berguna, misalnya dd -h.
domen
8
Program-program GNU mendukung --helpsebagai aturan tetapi seringkali tidak mengenali -hmana yang menjengkelkan. Saya biasanya mengetik -h ketika saya lupa opsi untuk suatu program, itu mengganggu harus mengetik ulang perintah dengan opsi yang lebih panjang --help. Lagi pula, saya mengetik -h karena saya lupa sesuatu. Mengapa pengguna harus mengingat program mana yang membutuhkan '--help' dan program mana yang membutuhkan '-h' untuk menampilkan layar bantuan? Cukup sertakan opsi untuk -h dan --help.
Brandin
30
Bagian pertama dari jawaban ini bagus, tetapi di suatu tempat di sepanjang Anda ada semacam singgung. Misalnya, file konfigurasi, plugins dan dlopen, pilihan lisensi perangkat lunak dan browser Web tidak benar-benar terkait dengan konvensi antarmuka baris perintah lagi.
Brandin
5
Dan tolong. Jika Anda memformat output Anda untuk layar, tambahkan override format output. Tidak ada yang mengganggu saya lebih dari perintah yang memotong atau membungkus baris ketika saya mencoba menggunakannya dalam skrip.
Sobrique
68

Fakta bahwa konvensi format data populer adalah keuntungannya.

Anda dapat dengan mudah melihat bahwa menggunakan = atau: atau bahkan '' sebagai pemisah adalah perbedaan sepele yang dapat dikonversi satu sama lain oleh komputer dengan sedikit usaha. Apa yang akan menjadi upaya besar bagi manusia untuk mengingat "Sekarang lihat, apakah program yang jarang digunakan ini membatasi hal-hal dengan :atau dengan =? Hmmm ..."

Dengan kata lain, untuk cinta tuhan, jangan menyimpang dari konvensi yang sangat mengakar tanpa alasan kuat. Orang akan mengingat program Anda sebagai "yang dengan sintaks cmdline yang aneh dan menjengkelkan" alih-alih "yang menyelamatkan esai kuliah saya".

Kilian Foth
sumber
19
untuk hampir semua bahasa ada fungsi perpustakaan untuk menangani argumen baris perintah. Gunakan salah satunya.
kevin cline
9
Nasihat yang bagus, tetapi apa saja konvensi itu? -1
RubberDuck
14
Ada contoh menarik dari alat UNIX yang umum digunakan yang secara sengaja melanggar konvensi ini: ddmenggunakan key=valuesintaksis. Alasan untuk keputusan desain ini adalah bahwa alat ini (nama panggilan: d ata d estroyer) dapat menyebabkan banyak kerusakan ketika digunakan secara tidak benar. Dengan memaksa pengguna untuk meninggalkan kebiasaan mereka yang biasa, mereka memaksa pengguna untuk berpikir lebih dekat tentang apa yang mereka lakukan.
Philipp
18
Apakah Anda yakin itulah alasannya dd? Saya percaya bahwa itu hanya diberi kode pada suatu waktu (1970-an?) Ketika ARG_MAX kernel kecil, shell tidak memiliki pelengkapan otomatis, dan --long-argumentstidak ada. Sejak itu lebih baik ddtetap kompatibel ke belakang
Basile Starynkevitch
9
ddberasal dari OS lain (dari UNIX) - bahasa scripting (JCL) pada IBM OS / 360 - di mana konvensi berbeda, sebelum porting lebih atau kurang tidak berubah ke UNIX - sebagian karena mereka yang cenderung menggunakannya, mengetahuinya dari sistem sebelumnya.
Baard Kopperud
29

Dalam istilah awam

Ketika di Roma, lakukan seperti yang dilakukan orang Romawi.

  • Jika aplikasi CLI Anda ditujukan untuk Linux / Unix gunakan -p valueatau --parameter valuekonvensi. Linux memiliki alat untuk parsing parameter dan flag tersebut dengan cara yang mudah.

Saya biasanya melakukan sesuatu seperti ini:

while [[ $# > 0 ]]
do
key="$1"
case $key in
    --dummy)
    #this is a flag do something here
    ;;
    --audit_sessiones)
    #this is a flag do something here
    ;;
    --destination_path)
    # this is a key-value parameter
    # the value is always in $2 , 
    # you must shift to skip over for the next iteration
    path=$2
    shift
    ;;
    *)
    # unknown option
    ;;
esac
shift
done
  • Jika aplikasi CLI Anda ditujukan untuk Windows, maka gunakan /flagdan /flag:valuekonvensi.

  • Beberapa aplikasi seperti Oracle tidak menggunakan keduanya. Utilitas Oracle digunakan PARAMETER=VALUE.

  • Satu hal yang ingin saya lakukan adalah, selain menerima parameter di baris perintah, menyediakan opsi untuk menggunakan parfile , yang merupakan file pasangan nilai-kunci untuk menghindari rantai parameter yang panjang. Untuk itu Anda harus memberikan --parfile mifile.parparameter tambahan . Tentunya jika --parfiledigunakan, semua parameter lainnya dibuang demi apa yang ada di dalam parfile.

  • Saran tambahan memungkinkan penggunaan beberapa variabel lingkungan kustom , misalnya, pengaturan variabel lingkungan MYAPP_WRKSPACE=/tmpakan membuatnya tidak perlu untuk selalu ditetapkan --wrkspace /tmp.

  • Di Linux jangan lupa menambahkan parameter pelengkapan otomatis , artinya pengguna dapat mengetik setengah sakelar, tekan TABdan kemudian shell akan menyelesaikannya untuk mereka.
Tulains Córdova
sumber
1
Nah, beberapa utilitas GNU (mis. gcc) Menangani @mifile.parseperti --parfile mifile.parsaran Anda .
Basile Starynkevitch
7
Apakah Anda yakin tentang mengabaikan semua opsi lain jika ada file parameter? Kedengarannya berlawanan dengan intuisi bagiku.
Jonathan Leffler
8
Saya setuju dengan Jonathan tentang file parameter. Jika file parameter ada, maka saya berharap itu akan digunakan untuk nilai-nilai default, dengan argumen yang diberikan pada baris perintah yang diterapkan melebihi yang ada di parfile. Jika karena alasan tertentu penggunaan parfile menghalangi penggunaan argumen baris perintah tambahan, maka keberadaan argumen tambahan harus menjadi kesalahan.
Keju Eldritch
@EldritchCheese Dalam aplikasi CLI yang saya tulis, ketika parfile diberikan, setiap parameter tambahan menghasilkan kesalahan.
Tulains Córdova
1
Jika menggunakan shell yang mampu, opsi "config file parameter" semacam ini tidak diperlukan. misal Anda baru saja menulis fooCmd -opt1 -opt2 $(cat more_options.opts). Oleh karena itu saya akan mengharapkan opsi "config file parameter", jika disediakan, pada dasarnya harus bekerja dengan cara yang sama.
Brandin
19

Satu hal yang belum muncul:

Cobalah untuk merancang perangkat lunak Anda dari argumen baris perintah ke atas. Berarti:

Sebelum merancang fungsionalitas, desain antarmuka pengguna.

Ini akan memungkinkan Anda untuk menelusuri kasus tepi dan kasus umum di awal. Tentu saja Anda masih akan mengabstraksi bagian luar dan bagian dalam, tetapi ini akan memberikan hasil yang jauh lebih baik daripada hanya menulis semua kode dan kemudian membanting sebuah CLI ke dalamnya.

Selanjutnya, lihat docopt ( http://docopt.org/ ).

docopt sangat membantu dengan itu dalam banyak bahasa, terutama untuk python di mana Anda sangat terbatas, parser argumen yang merugikan pengguna seperti argparse masih dianggap sebagai "OK". Alih-alih memiliki parser dan subparser dan dicts kondisional, Anda hanya mendefinisikan bantuan sintaks dan ia melakukan sisanya.

Florian Heigl
sumber
Saya suka jawaban ini, dan terima kasih untuk itu, karena saya saat ini frustrasi argparsekarena tidak ada programmer, antarmuka pengguna atau ramah pengguna.
kucing
Saya mencoba docopt, dan saya tidak menyukainya. klik menghasilkan kode yang jauh lebih bersih, meskipun ada sedikit ketidakberuntungan dengan opsi ketika Anda memiliki sub-perintah.
jpmc26
2
Ini terlalu mirip iklan. Sebutkan sumber daya hanya sekali jika relevan. Tetapi seperti sekarang, 50% dari jawaban Anda hanya terdengar seperti mempromosikan sumber daya eksternal.
Brandin
Saya akan mengatakannya sebagai 'desain model penggunaan pertama'. Memisahkan pengalaman pengguna dari fungsi dapat menjadi perbedaan buatan dalam banyak kasus (keterbatasan alat mempengaruhi antarmuka).
copper.hat
1
+1 untuk Docopt. Itu memecahkan semua dilema CLI saya, benar-benar bebas rasa sakit. Terkadang sulit untuk mengatakan iklan dari antusiasme yang tulus, tetapi ini dia - saya telah menjadi penggemar Docopt selama bertahun-tahun sekarang, tidak berafiliasi dengan cara apa pun;)
frnhr
3

Beberapa komentar berharga sudah disediakan (@Florian, Basile), tapi izinkan saya menambahkan ... OP mengatakan,

Kami akan menyediakan antarmuka ujung depan untuk mengelola perangkat. Namun beberapa administrator tingkat lanjut mungkin ingin mengkonfigurasi aplikasi sendiri

Tetapi juga berkomentar:

Saya tidak ingin pertanyaan ini khusus untuk platform atau bahasa

Anda harus mempertimbangkan audiens target Anda - administrator tingkat lanjut . Platform apa yang biasanya mereka kerjakan - Win / Unix / Mac? Dan pada platform apa Anda menjalankan aplikasi? Ikuti konvensi CLI apa pun yang telah ditetapkan untuk platform itu. Apakah admin "mahir" Anda menginginkan / memerlukan alat berbasis GUI?

Anda ingin antarmuka konsisten secara internal dan dengan alat admin lainnya. Saya tidak ingin berhenti dan berpikir apakah itu cmd -p <arg>atau cmd -p:<arg>atau cmd /p <arg>. Apakah saya perlu mengutip karena ada spasi? Bisakah saya cmd -p <val1> <val2>atau cmd -p <val1> -p <val2>banyak target? Apakah mereka memesan khusus? Overloadable? Apakah cmd -p2 <arg> -p1 <arg>bekerja juga? Apakah ls -l -r -t dir1 dir2== ls -trl dir1 dir2?

Untuk alat admin Unix saya, saya selalu mengingat panduan yang diberikan oleh Heiner 's Shelldorado bersama dengan referensi lain yang disebutkan.

Sama pentingnya dengan merancang CLI adalah untuk memastikan aplikasi Anda dirancang untuk bekerja dengan argumen baris perintah sama dengan dari GUI - yaitu: tidak ada logika bisnis di GUI atau gunakan perintah umum yang disebut dari kedua GUI dan CLI.

Sebagian besar alat administratif berbasis UNIX sebenarnya dirancang terlebih dahulu sebagai alat baris perintah dan GUI yang disediakan hanya memfasilitasi "mengisi" opsi untuk baris perintah. Pendekatan itu memungkinkan untuk otomatisasi, penggunaan file respons, dll. Dan manajemen langsung (lebih sedikit pekerjaan untuk saya!)

Sebagai toolset klasik yang digunakan dengan pendekatan ini adalah Tcl / Tk . Tidak menyarankan Anda mengganti alat; cukup pertimbangkan pendekatan desain mulai dari menulis aplikasi administratif berbasis GUI ke aplikasi sebagai alat baris perintah terlebih dahulu; lalu lapisan GUI di atas untuk kenyamanan. Pada titik tertentu Anda mungkin akan menemukan bahwa GUI itu menyakitkan (dan rawan kesalahan) jika Anda harus melakukan banyak konfigurasi dan memasukkan kembali secara umum opsi yang sama berulang-ulang dan Anda akan mencari pendekatan otomatis.

Ingat admin Anda kemungkinan harus mengetikkan nilai yang benar di kotak yang benar, jadi berapa banyak usaha yang Anda hilangkan dengan GUI?

Ian W
sumber
Bagus ya! Satu pertanyaan juga: bisakah saya menjalankan ./cis-script ini --hosts <hosts.txt
Florian Heigl