getopt, getopts hoặc phân tích thủ công - sử dụng cái gì khi tôi muốn hỗ trợ cả hai tùy chọn ngắn và dài?


32

Hiện tại tôi đang viết một tập lệnh Bash có các yêu cầu sau:

  • Nó nên chạy trên nhiều nền tảng Unix / Linux
  • cần hỗ trợ cả tùy chọn ngắn và dài (GNU)

Tôi biết đó getoptssẽ là cách ưa thích về tính di động nhưng AFAIK nó không hỗ trợ các tùy chọn dài.

getopthỗ trợ các tùy chọn dài nhưng BashGuide khuyến nghị mạnh mẽ chống lại nó:

Không bao giờ sử dụng getopt (1). getopt không thể xử lý các chuỗi đối số trống hoặc các đối số có khoảng trắng được nhúng. Hãy quên rằng nó từng tồn tại.

Vì vậy, vẫn có tùy chọn phân tích thủ công. Đây là lỗi dễ xảy ra, tạo ra một số mã soạn sẵn và tôi cần tự xử lý lỗi (tôi đoán là getopt(s)tự xử lý lỗi).

Vì vậy, điều gì sẽ là sự lựa chọn ưa thích trong trường hợp này?


Câu trả lời:


9

Nếu nó phải được di động tới một loạt các Thông báo, bạn sẽ phải sử dụng POSIX sh. Và AFAIU ở đó bạn không có lựa chọn nào khác ngoài việc xử lý đối số bằng tay.


25

getoptvs getoptsdường như là một vấn đề tôn giáo. Đối với các đối số chống lại getopttrong Câu hỏi thường gặp của Bash :

  • " getoptKhông thể xử lý các chuỗi đối số trống" dường như đề cập đến một vấn đề đã biết với các đối số tùy chọn , có vẻ như getoptskhông hỗ trợ gì cả (ít nhất là từ việc đọc help getoptscho Bash 4.2.24). Từ man getopt:

    getopt (3) có thể phân tích các tùy chọn dài với các đối số tùy chọn được cung cấp một đối số tùy chọn trống (nhưng không thể làm điều này cho các tùy chọn ngắn). Getopt (1) này xử lý các đối số tùy chọn trống như thể chúng không có mặt.

Tôi không biết các đối số " getoptkhông thể xử lý [...] với khoảng trắng được nhúng" đến từ đâu, nhưng hãy kiểm tra nó:

  • kiểm tra

    #!/usr/bin/env bash
    set -o errexit -o noclobber -o nounset -o pipefail
    params="$(getopt -o ab:c -l alpha,bravo:,charlie --name "$0" -- "$@")"
    eval set -- "$params"
    
    while true
    do
        case "$1" in
            -a|--alpha)
                echo alpha
                shift
                ;;
            -b|--bravo)
                echo "bravo=$2"
                shift 2
                ;;
            -c|--charlie)
                echo charlie
                shift
                ;;
            --)
                shift
                break
                ;;
            *)
                echo "Not implemented: $1" >&2
                exit 1
                ;;
        esac
    done
  • chạy:

    $ ./test.sh -
    $ ./test.sh -acb '   whitespace   FTW   '
    alpha
    charlie
    bravo=   whitespace   FTW   
    $ ./test.sh -ab '' -c
    alpha
    bravo=
    charlie
    $ ./test.sh --alpha --bravo '   whitespace   FTW   ' --charlie
    alpha
    bravo=   whitespace   FTW   
    charlie

Trông giống như kiểm tra và giao phối với tôi, nhưng tôi chắc chắn ai đó sẽ chỉ ra cách tôi hoàn toàn hiểu sai câu. Tất nhiên vấn đề tính di động vẫn còn; bạn sẽ phải quyết định đầu tư bao nhiêu thời gian vào các nền tảng có sẵn Bash hoặc cũ hơn. Mẹo riêng của tôi là sử dụng các nguyên tắc YAGNIKISS - Chỉ phát triển cho những nền tảng cụ thể mà bạn biết sẽ được sử dụng. Tính di động của mã Shell thường đạt đến 100% khi thời gian phát triển đến vô cùng.


11
OP đã đề cập đến nhu cầu di động đối với nhiều nền tảng Unix trong khi getoptbạn trích dẫn ở đây là dành riêng cho Linux. Lưu ý rằng đó getoptkhông phải là một phần của bash, nó thậm chí không phải là tiện ích GNU và trên Linux được cung cấp cùng với gói linux-linux.
Stéphane Chazelas

2
Hầu hết các nền tảng đều có getopt, chỉ có Linux AFAIK đi kèm với một nền tảng hỗ trợ các tùy chọn dài hoặc khoảng trống trong các đối số. Những cái khác sẽ chỉ hỗ trợ cú pháp System V.
Stéphane Chazelas

7
getoptlà một lệnh truyền thống xuất phát từ System V từ lâu trước khi Linux được phát hành. getoptkhông bao giờ được chuẩn hóa. Không ai trong số POSIX, Unix hoặc Linux (LSB) từng chuẩn hóa getoptlệnh. getoptsđược chỉ định trong cả ba nhưng không hỗ trợ cho các tùy chọn dài.
Stéphane Chazelas

1
Chỉ để thêm vào cuộc thảo luận này: đây không phải là truyền thống getopt. Đó là hương vị linux-utils như được chỉ định bởi @ StéphaneChazelas. Nó có các tùy chọn kế thừa sẽ vô hiệu hóa cú pháp được mô tả ở trên, đặc biệt là trạng thái manpage "GETOPT_COMPATIBLE buộc getopt sử dụng định dạng cuộc gọi đầu tiên như được chỉ định trong SYNOPSIS". Tuy nhiên, nếu bạn có thể mong đợi các hệ thống đích sẽ cài đặt gói này thì đây hoàn toàn là cách để đi, vì getopt ban đầu rất tệ và getopt của Bash rất hạn chế
n.caillou

1
Liên kết của OP tuyên bố "Các phiên bản truyền thống của getopt không thể xử lý các chuỗi đối số trống hoặc các đối số có khoảng trắng được nhúng." và rằng phiên bản getopt của linux không nên được sử dụng không có bằng chứng và không còn chính xác nữa. Một khảo sát nhanh (hơn 5 năm sau) cho thấy phiên bản getopt mặc định có sẵn từ ArchLinux, SUSE, Ubuntu, RedHat, Fedora và CentOS (cũng như hầu hết các biến thể phái sinh) đều hỗ trợ các đối số và đối số tùy chọn với khoảng trắng.
mtalexan

10

getopts_long này được viết dưới dạng hàm POSIX mà bạn có thể nhúng vào tập lệnh của mình.

Lưu ý rằng Linux getopt(từ util-linux) hoạt động chính xác khi không ở chế độ truyền thống và hỗ trợ các tùy chọn dài, nhưng có lẽ không phải là một tùy chọn cho bạn nếu bạn cần di chuyển đến các Unice khác.

Các phiên bản gần đây của ksh93 ( getopts) và zsh ( zparseopts) có hỗ trợ tích hợp để phân tích cú pháp các tùy chọn dài có thể là một tùy chọn cho bạn vì chúng có sẵn cho hầu hết các Unices (mặc dù thường không được cài đặt theo mặc định).

Một tùy chọn khác là sử dụng perlGetopt::Longcả hai mô-đun của nó đều có sẵn trên hầu hết các Unice hiện nay, bằng cách viết toàn bộ tập lệnh vào perlhoặc chỉ gọi perl chỉ để phân tích tùy chọn và đưa thông tin được trích xuất vào trình bao. Cái gì đó như:

parsed_ops=$(
  perl -MGetopt::Long -le '

    @options = (
      "foo=s", "bar", "neg!"
    );

    Getopt::Long::Configure "bundling";
    $q="'\''";
    GetOptions(@options) or exit 1;
    for (map /(\w+)/, @options) {
      eval "\$o=\$opt_$_";
      $o =~ s/$q/$q\\$q$q/g;
      print "opt_$_=$q$o$q"
    }' -- "$@"
) || exit
eval "$parsed_ops"
# and then use $opt_foo, $opt_bar...

Xem perldoc Getopt::Longnhững gì nó có thể làm và làm thế nào nó khác với các trình phân tích cú pháp tùy chọn khác.


2

Mọi cuộc thảo luận về vấn đề này đều nêu bật tùy chọn viết mã phân tích thủ công - chỉ khi đó bạn mới có thể chắc chắn về chức năng và tính di động. Tôi khuyên bạn không nên viết mã mà bạn có thể đã tạo và tạo lại bằng các trình tạo mã nguồn mở dễ sử dụng. Sử dụng Argbash , được thiết kế để cung cấp câu trả lời dứt khoát cho vấn đề của bạn. Nó là một trình tạo mã tài liệu tốt có sẵn dưới dạng một ứng dụng dòng lệnh , trực tuyến hoặc dưới dạng hình ảnh Docker .

Tôi khuyên bạn nên chống lại các thư viện bash, một số trong số chúng sử dụng getopt(điều này khá khó hiểu) và thật khó khăn để bó một blob shell khổng lồ không thể đọc được với tập lệnh của bạn.


0

Bạn có thể sử dụng getopttrên các hệ thống hỗ trợ nó và sử dụng dự phòng cho các hệ thống không hỗ trợ.

Chẳng hạn, pure-getoptđược triển khai trong Bash thuần túy để thay thế cho GNU getopt.

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.