Làm thế nào để sử dụng getopt trong dòng lệnh bash chỉ với các tùy chọn dài?


13

Có một getoptlệnh trong dòng lệnh bash. getoptcó thể được sử dụng với các tùy chọn ngắn (chẳng hạn như getopt -o axby "$@") và có thể được sử dụng với cả tùy chọn ngắn và dài (chẳng hạn như getopt -o axby -l long-key -- "$@"), nhưng bây giờ tôi chỉ cần các tùy chọn dài (ví dụ: tùy chọn ngắn không tồn tại), tuy nhiên lệnh getopt -l long-key -- "$@"không --long-keytùy chọn phân tích chính xác. Vì vậy, làm thế nào tôi có thể sử dụng getoptlệnh với chỉ lựa chọn lâu? Hoặc là nó không thể hoặc nó chỉ là một lỗi của getoptlệnh?


Bạn gắn thẻ cho nội bộ getopts, nhưng bạn đang sử dụng /usr/bin/getoptlệnh.
Anthon

@Anthon Xin lỗi, tôi đã sử dụng thẻ sai, nhưng tôi không có đủ danh tiếng để thêm một thẻ khác, cần 300 danh tiếng. Tuy nhiên, tôi đã xóa thẻ sai ngay bây giờ.
Victor

Câu trả lời:


15

getoptlà hoàn toàn tốt với không có lựa chọn ngắn. Nhưng bạn cần nói với nó rằng bạn không có lựa chọn ngắn. Đó là một sự châm biếm trong cú pháp - từ hướng dẫn:

Nếu không có -ohoặc --optionstùy chọn được tìm thấy trong phần đầu tiên, tham số đầu tiên của phần thứ hai được sử dụng làm chuỗi tùy chọn ngắn.

Đó là những gì đang xảy ra trong bài kiểm tra của bạn: getopt -l long-key -- --long-key foocoi --long-keylà danh sách các lựa chọn -egklnoyfoolà đối số duy nhất. Sử dụng

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

ví dụ

$ 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'

Có phải OP đã pha trộn getoptsgetoptlây nhiễm vào câu trả lời của bạn? Bạn bắt đầu với bình luận getoptssau đó chỉ đề cập getopt.
Anthon

@Anthon Tất cả câu trả lời của tôi là về getoptchương trình từ GNU coreutils, đó là câu hỏi gì. Tôi đã sửa văn bản nói getopts. Cảm ơn. getoptsthậm chí không thực hiện các tùy chọn dài, vì vậy không ai trong số này sẽ áp dụng cho getopts.
Gilles 'SO- ngừng trở nên xấu xa'

OP ban đầu có getoptsthẻ. Tôi không muốn thay đổi câu trả lời của bạn, bởi vì bạn thường biết nhiều hơn tôi những gì bạn đang viết về :-)
Anthon

Tôi đã mất gần một giờ để cố gắng tìm ra điều này. Bạn chỉ tiết kiệm cho tôi một vài ngụm cà phê vô ích. Cảm ơn ... hãy đặt cà phê này để sử dụng tốt hơn bây giờ. ☕️
dmmd

1

Dunno về getoptnhưng getoptsđược xây dựng trong có thể được sử dụng để xử lý chỉ tùy chọn dài như thế này:

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

Tất nhiên, như vậy, điều đó sẽ không hoạt động nếu các tùy chọn dài được cho là có đối số. Nó có thể được thực hiện, mặc dù, nhưng, như tôi đã học được khi làm việc này. Mặc dù ban đầu tôi đưa nó vào đây nhưng tôi nhận ra rằng đối với các tùy chọn dài, nó không có nhiều tiện ích. Trong trường hợp này, nó chỉ rút ngắn case (match)các lĩnh vực của tôi bằng một nhân vật duy nhất, có thể dự đoán được. Bây giờ, những gì tôi biết, là nó rất tuyệt vời cho các tùy chọn ngắn - nó hữu ích nhất khi nó được lặp qua một chuỗi có độ dài không xác định và chọn các byte đơn theo chuỗi tùy chọn của nó. Nhưng khi tùy chọn đối số, sẽ không có nhiều bạn đang làm với for var do case $var insự kết hợp mà nó có thể làm. Nó là tốt hơn, tôi nghĩ, để giữ cho nó đơn giản.

Tôi nghi ngờ điều tương tự cũng đúng getoptnhưng tôi không biết đủ về điều đó để nói với bất kỳ sự chắc chắn. Đưa ra mảng arg sau đây, tôi sẽ chứng minh trình phân tích cú pháp arg nhỏ của riêng tôi - điều này phụ thuộc chủ yếu vào mối quan hệ đánh giá / phân công mà tôi đã đánh giá cao alias$((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

Đó là chuỗi arg tôi sẽ làm việc với. Hiện nay:

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

Điều đó xử lý mảng arg theo một trong hai cách khác nhau tùy thuộc vào việc bạn trao cho nó một hoặc hai bộ đối số được phân tách bằng --dấu phân cách. Trong cả hai trường hợp, nó áp dụng cho các chuỗi xử lý cho mảng arg.

Nếu bạn gọi nó như:

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

Đơn hàng đầu tiên của doanh nghiệp sẽ là viết acase()chức năng của nó giống như:

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

Và bên cạnh shift 3. Thay thế lệnh trong acase()định nghĩa hàm được đánh giá khi shell gọi xây dựng đầu vào của hàm ở đây - tài liệu, nhưng acase()không bao giờ được gọi hoặc định nghĩa trong vỏ gọi. Tuy nhiên, nó được gọi trong subshell, và do đó, theo cách này, bạn có thể tự động chỉ định các tùy chọn quan tâm trên dòng lệnh.

Nếu bạn trao cho nó một mảng không phân cách, nó chỉ đơn giản là acase()khớp với tất cả các đối số bắt đầu bằng chuỗi --.

Hàm thực tế tất cả quá trình xử lý của nó trong lớp con - lặp lại lưu từng giá trị của đối số vào các bí danh được gán với các tên kết hợp. Khi thông qua nó sẽ in ra mọi giá trị mà nó đã lưu alias- được chỉ định POSIX để in tất cả các giá trị đã lưu được trích dẫn theo cách mà các giá trị của chúng có thể được gửi lại vào trình bao. Vì vậy, khi tôi làm ...

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

Đầu ra của nó trông như thế này:

...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'

Khi nó đi qua danh sách arg, nó sẽ kiểm tra khối trường hợp để tìm trận đấu. Nếu nó tìm thấy một trận đấu ở đó, nó sẽ ném một lá cờ - f=optname. Cho đến khi nó một lần nữa tìm thấy một tùy chọn hợp lệ, nó sẽ thêm từng đối số tiếp theo vào một mảng mà nó xây dựng dựa trên cờ hiện tại. Nếu cùng một tùy chọn được chỉ định nhiều lần hợp chất kết quả và không ghi đè. Bất cứ điều gì không có trong trường hợp - hoặc bất kỳ đối số nào sau các tùy chọn bị bỏ qua - đều được gán cho một mảng bị bỏ qua .

Đầu ra được bảo vệ an toàn cho shell-input tự động bởi shell, và vì vậy:

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

... nên hoàn toàn an toàn. Nếu vì lý do nào đó không an toàn, thì có lẽ bạn nên gửi báo cáo lỗi với người bảo trì hệ vỏ của bạn.

Nó gán hai loại giá trị bí danh cho mỗi trận đấu. Đầu tiên, nó đặt cờ - điều này xảy ra cho dù tùy chọn có trước các đối số không khớp hay không. Vì vậy, bất kỳ sự xuất hiện của --flagtrong danh sách arg sẽ kích hoạt flag=1. Điều này không hợp chất - --flag --flag --flagchỉ được flag=1. Giá trị này không tăng mặc dù - đối với bất kỳ đối số nào có thể theo sau nó. Nó có thể được sử dụng như một khóa chỉ mục. Sau khi làm như evaltrên tôi có thể làm:

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

... để có được ...

8
1

Và vì thế:

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

ĐẦU RA

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

Và để tranh luận không phù hợp, tôi sẽ thay thế bỏ qua trong trường trên for ... inđể có được:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.