Bagaimana cara menggunakan getopt di baris perintah bash dengan hanya opsi panjang?

13

Ada getoptperintah di baris perintah bash. getoptdapat digunakan dengan opsi pendek (seperti getopt -o axby "$@"), dan dapat digunakan dengan opsi pendek dan panjang (seperti getopt -o axby -l long-key -- "$@"), tapi sekarang saya hanya perlu opsi panjang (yaitu opsi pendek tidak ada sama sekali), namun perintahnya getopt -l long-key -- "$@"tidak --long-keyOpsi parse dengan benar. Jadi bagaimana saya bisa menggunakan getoptperintah dengan hanya opsi panjang? Atau tidak mungkin atau hanya bug dari getoptperintah?

Pemenang
sumber
Anda menandai internal getopts, tetapi Anda menggunakan /usr/bin/getoptperintah.
Anthon
@Anthon Maaf, saya telah menggunakan tag yang salah, tapi saya tidak punya cukup reputasi untuk menambahkan tag lain, yang membutuhkan 300 reputasi. Namun, saya telah menghapus tag yang salah tadi.
Victor

Jawaban:

15

getoptbaik-baik saja dengan tidak memiliki opsi pendek. Tetapi Anda harus mengatakan bahwa Anda tidak memiliki opsi pendek. Ini adalah kekhasan dalam sintaksis - dari manual:

Jika tidak ada -oatau --optionsopsi ditemukan di bagian pertama, parameter pertama dari bagian kedua digunakan sebagai string opsi pendek.

Itulah yang terjadi dalam pengujian Anda: getopt -l long-key -- --long-key foomemperlakukan --long-keysebagai daftar opsi -egklnoydan foosebagai satu-satunya argumen. Menggunakan

getopt -o '' -l long-key -- "$@"

misalnya

$ getopt -l long-key -o '' -- --long-key foo
 --long-key -- 'foo'
$ getopt -l long-key -o '' -- --long-key --not-recognized -n foo
getopt: unrecognized option '--not-recognized'
getopt: invalid option -- 'n'
 --long-key -- 'foo'
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Apakah pencampuran OP getoptsdan getoptmenginfeksi jawaban Anda? Anda mulai dengan berkomentar getoptslalu hanya menyebutkan getopt.
Anthon
@Anthon Semua jawaban saya adalah tentang getoptprogram dari GNU coreutils yang merupakan pertanyaannya. Saya telah memperbaiki teks yang mengatakan getopts. Terima kasih. getoptsbahkan tidak melakukan opsi panjang, jadi semua ini tidak berlaku getopts.
Gilles 'SANGAT berhenti menjadi jahat'
OP awalnya memiliki getoptstag. Saya tidak ingin mengubah jawaban Anda, karena Anda biasanya tahu lebih baik daripada saya tentang apa yang Anda tulis :-)
Anthon
Saya kehilangan hampir satu jam mencoba mencari tahu ini. Anda baru saja menyelamatkan saya beberapa teguk kopi yang sia-sia. Terima kasih ... mari gunakan kopi ini untuk penggunaan yang lebih baik sekarang. ☕️
dmmd
1

Tidak tahu tentang getopttetapi getoptsbuiltin dapat digunakan untuk menangani hanya opsi panjang seperti ini:

while getopts :-: o
do  case "$o$OPTARG" in
(-longopt1) process ;;
(-longopt2) process ;;
esac; done

Tentu saja, sebagaimana adanya, itu tidak berfungsi jika opsi-opsi panjang seharusnya memiliki argumen. Itu bisa dilakukan, tetapi, karena saya sudah belajar mengerjakan ini. Sementara saya awalnya termasuk di sini saya menyadari bahwa untuk opsi lama tidak memiliki banyak utilitas. Dalam hal ini hanya memperpendek case (match)bidang saya dengan satu karakter yang dapat diprediksi. Sekarang, yang saya tahu, itu sangat baik untuk opsi pendek - ini sangat berguna ketika looping pada string yang panjangnya tidak diketahui dan memilih byte tunggal sesuai dengan option-string-nya. Tetapi ketika pilihannya adalah arg, tidak banyak yang Anda lakukan dengan for var do case $var inkombinasi yang bisa dilakukan. Lebih baik, saya pikir, untuk membuatnya tetap sederhana.

Saya menduga hal yang sama berlaku getopttetapi saya tidak cukup tahu tentang hal itu untuk mengatakan dengan pasti. Dengan susunan arg berikut, saya akan menunjukkan parser arg kecil saya sendiri - yang terutama tergantung pada hubungan evaluasi / penugasan yang saya hargai untuk aliasdan $((shell=math)).

set -- this is ignored by default --lopt1 -s 'some '\'' 
args' here --ignored   and these are ignored \
--alsoignored andthis --lopt2 'and 

some "`more' --lopt1 and just a few more

Itulah string arg yang akan saya gunakan. Sekarang:

aopts() { env - sh -s -- "$@"
} <<OPTCASE 3<<\OPTSCRIPT
acase() case "\$a" in $(fmt='
        (%s) f=%s; aset "?$(($f)):";;\n'
        for a do case "$a" in (--) break;;
        (--*[!_[:alnum:]]*) continue;;
        (--*) printf "$fmt" "$a" "${a#--}";;
        esac;done;printf "$fmt" '--*' ignored)
        (*) aset "" "\$a";;esac
shift "$((SHIFT$$))"; f=ignored; exec <&3 
OPTCASE
aset()  {  alias "$f=$(($f${1:-=$(($f))+}1))"
        [ -n "${2+?}" ] && alias "${f}_$(($f))=$2"; }
for a do acase; done; alias
#END
OPTSCRIPT

Itu memproses array arg dalam salah satu dari dua cara berbeda tergantung pada apakah Anda menyerahkannya satu atau dua set argumen yang dipisahkan oleh --pembatas. Dalam kedua kasus itu berlaku untuk urutan pemrosesan ke array arg.

Jika Anda menyebutnya seperti:

: $((SHIFT$$=3)); aopts --lopt1 --lopt2 -- "$@"

Urutan pertama bisnisnya adalah menulis acase()fungsinya agar terlihat seperti:

acase() case "$a" in 
    (--lopt1) f=lopt1; aset "?$(($f)):";;
    (--lopt2) f=lopt2; aset "?$(($f)):";;
    (--*) f=ignored; aset "?$(($f)):";;
    (*) aset "" "$a";;esac

Dan di sebelah shift 3. Substitusi perintah dalam acase()definisi fungsi dievaluasi ketika shell panggilan membangun input fungsi di sini-dokumen, tetapi acase()tidak pernah dipanggil atau didefinisikan dalam shell panggilan. Meskipun demikian, ini disebut dalam subkulit, jadi dengan cara ini Anda dapat menentukan opsi menarik secara dinamis pada baris perintah.

Jika Anda memberikannya array yang tidak dibatasi itu hanya diisi acase()dengan kecocokan untuk semua argumen yang dimulai dengan string --.

Fungsi ini praktis memproses semua dalam subkulit - iteratif menyimpan masing-masing nilai arg ke alias ditugaskan dengan nama asosiatif. Ketika itu melalui itu mencetak setiap nilai yang disimpan dengan alias- yang POSIX ditentukan untuk mencetak semua nilai yang disimpan dikutip sedemikian rupa sehingga nilai-nilai mereka dapat dikembalikan ke shell. Jadi ketika saya melakukannya ...

aopts --lopt1 --lopt2 -- "$@"

Outputnya terlihat seperti ini:

...ignored...
lopt1='8'
lopt1_1='-s'
lopt1_2='some '\'' args'
lopt1_3='here'
lopt1_4='and'
lopt1_5='just'
lopt1_6='a'
lopt1_7='few'
lopt1_8='more'
lopt2='1'
lopt2_1='and

some "`more'

Saat berjalan melalui daftar arg itu memeriksa blok kasus untuk pertandingan. Jika menemukan kecocokan di sana ia melempar bendera - f=optname. Sampai sekali lagi menemukan opsi yang valid itu akan menambahkan setiap argumen berikutnya ke array yang dibangun berdasarkan bendera saat ini. Jika opsi yang sama ditentukan beberapa kali hasilnya akan bertambah dan tidak menimpa. Apa pun yang tidak dalam kasus - atau argumen berikut opsi yang diabaikan - ditugaskan ke array yang diabaikan .

Outputnya diamankan dengan shell untuk input shell secara otomatis oleh shell, dan karenanya:

eval "$(: $((SHIFT$$=3));aopts --lopt1 --lopt2 -- "$@")"

... harus benar-benar aman. Jika karena alasan apa pun tidak aman, maka Anda mungkin harus mengajukan laporan bug dengan pengelola shell Anda.

Ini menetapkan dua jenis nilai alias untuk setiap pertandingan. Pertama, ia menetapkan sebuah bendera - ini terjadi terlepas dari suatu opsi mendahului argumen yang tidak cocok. Jadi setiap kejadian --flagdalam daftar arg akan memicu flag=1. Ini tidak majemuk - --flag --flag --flaghanya mendapat flag=1. Nilai ini memang bertambah - untuk setiap argumen yang mungkin mengikutinya. Ini dapat digunakan sebagai kunci indeks. Setelah melakukan hal di evalatas saya dapat melakukan:

printf %s\\n "$lopt1" "$lopt2"

...mendapatkan...

8
1

Dan sebagainya:

for o in lopt1 lopt2
do list= i=0; echo "$o = $(($o))"
        while [ "$((i=$i+1))" -le "$(($o))" ]
        do list="$list $o $i \"\${${o}_$i}\" "
done; eval "printf '%s[%02d] = %s\n' $list";  done

KELUARAN

lopt1 = 8
lopt1[01] = -s
lopt1[02] = some ' args
lopt1[03] = here
lopt1[04] = and
lopt1[05] = just
lopt1[06] = a
lopt1[07] = few
lopt1[08] = more
lopt2 = 1
lopt2[01] = and

some "`more

Dan untuk argumen yang tidak cocok saya akan mengganti diabaikan di for ... inbidang di atas untuk mendapatkan:

ignored = 10
ignored[01] = this
ignored[02] = is
ignored[03] = ignored
ignored[04] = by
ignored[05] = default
ignored[06] = and
ignored[07] = these
ignored[08] = are
ignored[09] = ignored
ignored[10] = andthis
mikeserv
sumber