Hàm Bash với `getopts` chỉ hoạt động lần đầu tiên khi nó chạy


9

Tôi đã định nghĩa hàm ftrong Bash dựa trên ví dụ ở đây (trong phần "Tùy chọn có đối số"):

f () {
  while getopts ":a:" opt; do
    case $opt in
      a)
        echo "-a was triggered, Parameter: $OPTARG" >&2
        ;;
      \?)
        echo "Invalid option: -$OPTARG" >&2
        return 1
        ;;
      :)
        echo "Option -$OPTARG requires an argument." >&2
        return 1
        ;;
    esac
  done
}

Trong khi họ sử dụng một tập lệnh, tôi trực tiếp xác định hàm trong trình bao.

Khi tôi lần đầu tiên khởi chạy Bash và định nghĩa hàm, mọi thứ đều hoạt động: f -a 123in -a was triggered, Parameter: 123. Nhưng khi tôi chạy chính xác cùng một dòng lần thứ hai, không có gì được in .

Điều gì gây ra hành vi này? Nó xảy ra trong Bash 3.2 và 4.3, nhưng nó hoạt động tốt trong Zsh 5.1. Điều này là đáng ngạc nhiên bởi vì ví dụ được cho là dành cho Bash, không phải cho Zsh.

Câu trả lời:


15

bash getopts sử dụng biến môi trường OPTIND để theo dõi đối số tùy chọn cuối cùng được xử lý. Thực tế OPTINDlà không tự động thiết lập lại mỗi lần bạn gọi getoptstrong cùng một phiên shell, chỉ khi shell được gọi. Vì vậy, từ lần thứ hai bạn gọi getoptsvới cùng một đối số trong cùng một phiên, OPTINDđã không thay đổi, getoptsnghĩ rằng nó đã hoàn thành công việc và không làm gì cả.

Bạn có thể thiết lập lại OPTINDthủ công để làm cho nó hoạt động:

$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123

hoặc chỉ cần đặt chức năng vào một tập lệnh và gọi tập lệnh nhiều lần.


zsh getopts là hơi khác nhau. OPTINDthường được đặt lại thành 1 mỗi lần khi thoát khỏi chức năng shell.


Tôi đang chấp nhận câu trả lời này bởi vì nó hoàn chỉnh hơn một chút so với câu trả lời khác
Shadowtalker

Đã thêm unset opt OPTARG OPTINDtrước mỗi while getopts...cuộc gọi và bây giờ nó hoạt động hoàn hảo. Cảm ơn :)
Sâu

6

Đó là một thói quen của thần để khai báo các biến cục bộ trong bất kỳ chức năng nào. Nếu bạn khai báo $ opt, $ OPTARG và $ OPTIND thì getopts sẽ hoạt động bất cứ khi nào bạn gọi hàm. Các biến cục bộ được loại bỏ sau khi hàm kết thúc.

#!/bin/bash
function some_func {
  declare opt
  declare OPTARG
  declare OPTIND

  while getopts ":a:" opt; do
    echo $opt is $OPTARG
  done
}

1
Chỉ tuyên bố đã không làm việc cho tôi. Tôi đã phải đặt unset opt OPTARG OPTINDtrước while getopts...câu lệnh sao cho giá trị của từng biến này không được đặt. Nếu tôi không làm điều này và chỉ thực hiện tuyên bố, vì trong phiên bash đã cho mà tôi đã sử dụng getoptsmột lần khi OPTINDnó có mặt, nó vẫn không thay đổi
Sâu

2

Bạn cần thiết lập OPTIND=1khi bắt đầu chức năng f. Theo mặc định, nó là 1, nhưng sau đó được tăng lên khi các đối số của bạn được phân tích cú pháp. Khi bạn gọi getoptslại, nó tiếp tục ở nơi nó rời đi. Bạn có thể thấy điều này nếu cuộc gọi thứ 2 của bạn là:

f -a 123 -a 999

khi nào nó sẽ in 999.


Tất nhiên! Đến từ R và Python tôi luôn bị vấp ngã bởi vỏ bọc. Nó sẽ hoạt động nếu tôi khai báo local OPTINDbên trong hàm trước khi getoptsđược gọi?
Shadowtalker

1
Nên làm, giải pháp tốt.
meuh

1

Khi getoptđược gọi nó theo dõi các tùy chọn xử lý theo biến OPTIND.

Hãy thử như sau:

#!/bin/bash

f () {
    printf "Intro OPTIND: %d\n" "$OPTIND"
    while getopts ":a:b:" opt; do
        printf "Current OPTIND: %d\n" "$OPTIND"
        case $opt in
            a)
                echo "-a was triggered, Parameter: $OPTARG" >&2
                ;;
            b)
                echo "-b was triggered, Parameter: $OPTARG" >&2
                ;;
        esac
    done
    printf "Exit OPTIND: %d\n" "$OPTIND"
}

echo "Run #1"
f "$@"
echo "Run #2"
f "$@"

Năng suất:

./test -a foo -b bar
Run #1
Intro OPTIND: 1
Current OPTIND: 3
-a was triggered, Parameter: foo
Current OPTIND: 5
-b was triggered, Parameter: bar
Exit OPTIND: 5
Run #2
Intro OPTIND: 5
Exit OPTIND: 5

Như vậy, bạn có thể làm một cái gì đó như:

OPTIND=1

khi bắt đầu chức năng. Hoặc, tùy thuộc vào tình huống, và thường tốt hơn:

local OPTIND

Nếu OPTINDkhông được sử dụng, khi hàm được thực thi, vòng lặp while sẽ đi mãi mãi. Người ta cũng có thể sử dụng nó để tiếp tục xử lý các đối số, sau khi thất bại hoặc bất cứ điều gì, gọi một hàm khác nếu x hoặc y và nó sẽ tiếp tục ở nơi trước đó đã tắt, v.v.

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.