Làm thế nào để tôi phân tích các đối số dòng lệnh trong Bash?


1921

Nói, tôi có một kịch bản được gọi với dòng này:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

hoặc cái này:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

Cách chấp nhận các phân tích này như vậy mà trong từng trường hợp (hoặc một số kết hợp cả hai) là gì $v, $f$dtất cả sẽ được thiết lập để true$outFilesẽ bằng /fizz/someOtherFile?


1
Đối với người dùng zsh, có một nội dung tuyệt vời được gọi là zparseopts có thể làm được: zparseopts -D -E -M -- d=debug -debug=dVà có cả hai -d--debugtrong $debugmảng echo $+debug[1]sẽ trả về 0 hoặc 1 nếu một trong số đó được sử dụng. Tham chiếu: zsh.org/mla/users/2011/msg00350.html
dezza

1
Hướng dẫn thực sự tốt: linuxcommand.org/lc3_wss0120.php . Tôi đặc biệt thích ví dụ "Tùy chọn dòng lệnh".
Gabriel Staples

Câu trả lời:


2676

Phương pháp # 1: Sử dụng bash mà không có getopt [s]

Hai cách phổ biến để truyền các đối số cặp khóa-giá trị là:

Bash Space-Tách (ví dụ, --option argument) (không có getopt [s])

Sử dụng demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts

cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash

POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -e|--extension)
    EXTENSION="$2"
    shift # past argument
    shift # past value
    ;;
    -s|--searchpath)
    SEARCHPATH="$2"
    shift # past argument
    shift # past value
    ;;
    -l|--lib)
    LIBPATH="$2"
    shift # past argument
    shift # past value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument
    ;;
    *)    # unknown option
    POSITIONAL+=("$1") # save it in an array for later
    shift # past argument
    ;;
esac
done
set -- "${POSITIONAL[@]}" # restore positional parameters

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 "$1"
fi
EOF

chmod +x /tmp/demo-space-separated.sh

/tmp/demo-space-separated.sh -e conf -s /etc -l /usr/lib /etc/hosts

đầu ra từ sao chép khối trên:

FILE EXTENSION  = conf
SEARCH PATH     = /etc
LIBRARY PATH    = /usr/lib
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

Bash Equals-Tách (ví dụ, --option=argument) (không có getopt [s])

Sử dụng demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/bin/bash

for i in "$@"
do
case $i in
    -e=*|--extension=*)
    EXTENSION="${i#*=}"
    shift # past argument=value
    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    shift # past argument=value
    ;;
    -l=*|--lib=*)
    LIBPATH="${i#*=}"
    shift # past argument=value
    ;;
    --default)
    DEFAULT=YES
    shift # past argument with no value
    ;;
    *)
          # unknown option
    ;;
esac
done
echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "LIBRARY PATH    = ${LIBPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)
if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
    tail -1 $1
fi
EOF

chmod +x /tmp/demo-equals-separated.sh

/tmp/demo-equals-separated.sh -e=conf -s=/etc -l=/usr/lib /etc/hosts

đầu ra từ sao chép khối trên:

FILE EXTENSION  = conf
SEARCH PATH     = /etc
LIBRARY PATH    = /usr/lib
DEFAULT         =
Number files in SEARCH PATH with EXTENSION: 14
Last line of file specified as non-opt/last argument:
#93.184.216.34    example.com

Để hiểu rõ hơn về ${i#*=}tìm kiếm "Loại bỏ chuỗi con" trong hướng dẫn này . Nó có chức năng tương đương với việc `sed 's/[^=]*=//' <<< "$i"`gọi một quy trình con không cần thiết hoặc `echo "$i" | sed 's/[^=]*=//'`gọi hai quy trình con không cần thiết.

Phương pháp # 2: Sử dụng bash với getopt [s]

từ: http://mywiki.wooledge.org/BashFAQ/035#getopts

giới hạn getopt (1) ( getoptphiên bản cũ hơn, tương đối gần đây ):

  • không thể xử lý các đối số là các chuỗi rỗng
  • không thể xử lý các đối số với khoảng trắng được nhúng

Các getoptphiên bản gần đây không có những hạn chế này.

Ngoài ra, vỏ POSIX (và các loại khác) cung cấp getoptskhông có những hạn chế này. Tôi đã bao gồm một đơn giảngetopts ví dụ .

Sử dụng demo-getopts.sh -vf /etc/hosts foo bar

cat >/tmp/demo-getopts.sh <<'EOF'
#!/bin/sh

# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

# Initialize our own variables:
output_file=""
verbose=0

while getopts "h?vf:" opt; do
    case "$opt" in
    h|\?)
        show_help
        exit 0
        ;;
    v)  verbose=1
        ;;
    f)  output_file=$OPTARG
        ;;
    esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift

echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
EOF

chmod +x /tmp/demo-getopts.sh

/tmp/demo-getopts.sh -vf /etc/hosts foo bar

đầu ra từ sao chép khối trên:

verbose=1, output_file='/etc/hosts', Leftovers: foo bar

Những lợi thế của getopts:

  1. Nó dễ mang theo hơn và sẽ hoạt động trong các shell khác dash.
  2. Nó có thể tự động xử lý nhiều tùy chọn đơn lẻ như -vf filenametheo cách Unix điển hình.

Nhược điểm của getoptsnó là chỉ có thể xử lý các tùy chọn ngắn ( -hkhông phải --help) mà không có mã bổ sung.

Có một hướng dẫn getopts giải thích tất cả các cú pháp và các biến có nghĩa là gì. Trong bash, cũng có help getopts, có thể là thông tin.


44
Điều này có thực sự đúng không? Theo Wikipedia, có một phiên bản nâng cao GNU mới hơn getoptbao gồm tất cả các chức năng getoptsvà sau đó là một số. man getopttrên đầu ra Ubuntu 13.04 getopt - parse command options (enhanced)như tên, vì vậy tôi cho rằng phiên bản nâng cao này là tiêu chuẩn.
Livven

47
Rằng một cái gì đó là một cách nhất định trên hệ thống của bạn là tiền đề rất yếu để dựa trên các giả định về "tiêu chuẩn".
szablica

13
@Livven, đó getoptkhông phải là tiện ích GNU, nó là một phần của util-linux.
Stephane Chazelas

4
Nếu bạn sử dụng -gt 0, hãy xóa phần shiftsau của bạn esac, tăng tất cả shiftbằng 1 và thêm trường hợp này: *) break;;bạn có thể xử lý các đối số không tùy chọn. Vd: pastebin.com/6DJ57HTc
Nicolas Lacombe

2
Bạn không lặp lại –default. Trong ví dụ đầu tiên, tôi nhận thấy rằng nếu –defaultlà đối số cuối cùng, thì nó không được xử lý (được coi là không chọn), trừ khi while [[ $# -gt 1 ]]được đặt là while [[ $# -gt 0 ]]
kolydart

562

Không có câu trả lời đề cập đến getopt tăng cường . Và câu trả lời được bình chọn hàng đầu là sai lệch: Nó bỏ qua -⁠vfdcác tùy chọn ngắn kiểu (được OP yêu cầu) hoặc các tùy chọn sau các đối số vị trí (cũng được OP yêu cầu); và nó bỏ qua lỗi phân tích cú pháp. Thay thế:

  • Sử dụng nâng cao getopttừ tiện ích linux hoặc trước đây là GNU glibc . 1
  • Nó hoạt động với getopt_long()chức năng C của GNU glibc.
  • tất cả các tính năng phân biệt hữu ích (những tính năng khác không có chúng):
    • xử lý khoảng trắng, trích dẫn các ký tự và thậm chí nhị phân trong các đối số 2 (không được tăng cường getoptkhông thể làm điều này)
    • nó có thể xử lý các tùy chọn ở cuối: script.sh -o outFile file1 file2 -v( getoptskhông làm điều này)
    • cho phép =các tùy chọn dài kiểu: script.sh --outfile=fileOut --infile fileIn(cho phép cả hai đều dài nếu tự phân tích cú pháp)
    • cho phép kết hợp các tùy chọn ngắn, ví dụ -vfd(công việc thực tế nếu tự phân tích cú pháp)
    • cho phép chạm vào các đối số tùy chọn, ví dụ -oOutfilehoặc-vfdoOutfile
  • Đã cũ 3 đến nỗi không có hệ thống GNU nào thiếu cái này (ví dụ như bất kỳ Linux nào cũng có nó).
  • Bạn có thể kiểm tra sự tồn tại của nó bằng: getopt --test→ giá trị trả về 4.
  • Khác getopthoặc vỏ dựng getoptsđược sử dụng hạn chế.

Các cuộc gọi sau

myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile
myscript -v -f -d -o/fizz/someOtherFile -- ./foo/bar/someFile
myscript --verbose --force --debug ./foo/bar/someFile -o/fizz/someOtherFile
myscript --output=/fizz/someOtherFile ./foo/bar/someFile -vfd
myscript ./foo/bar/someFile -df -v --output /fizz/someOtherFile

tất cả trở về

verbose: y, force: y, debug: y, in: ./foo/bar/someFile, out: /fizz/someOtherFile

với những điều sau đây myscript

#!/bin/bash
# saner programming env: these switches turn some bugs into errors
set -o errexit -o pipefail -o noclobber -o nounset

# -allow a command to fail with !’s side effect on errexit
# -use return value from ${PIPESTATUS[0]}, because ! hosed $?
! getopt --test > /dev/null 
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
    echo 'I’m sorry, `getopt --test` failed in this environment.'
    exit 1
fi

OPTIONS=dfo:v
LONGOPTS=debug,force,output:,verbose

# -regarding ! and PIPESTATUS see above
# -temporarily store output to be able to check for errors
# -activate quoting/enhanced mode (e.g. by writing out “--options”)
# -pass arguments only via   -- "$@"   to separate them correctly
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
    # e.g. return value is 1
    #  then getopt has complained about wrong arguments to stdout
    exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"

d=n f=n v=n outFile=-
# now enjoy the options in order and nicely split until we see --
while true; do
    case "$1" in
        -d|--debug)
            d=y
            shift
            ;;
        -f|--force)
            f=y
            shift
            ;;
        -v|--verbose)
            v=y
            shift
            ;;
        -o|--output)
            outFile="$2"
            shift 2
            ;;
        --)
            shift
            break
            ;;
        *)
            echo "Programming error"
            exit 3
            ;;
    esac
done

# handle non-option arguments
if [[ $# -ne 1 ]]; then
    echo "$0: A single input file is required."
    exit 4
fi

echo "verbose: $v, force: $f, debug: $d, in: $1, out: $outFile"

1 getopt nâng cao có sẵn trên hầu hết các hệ thống bash của hệ thống, bao gồm cả Cygwin; trên OS X, hãy thử cài đặt gnu-getopt hoặcsudo port install getopt
2exec() quy ướcPOSIXkhông có cách nào đáng tin cậy để vượt qua NULL nhị phân trong các đối số dòng lệnh; những byte đó kết thúc sớm đối số
3 phiên bản đầu tiên được phát hành vào năm 1997 hoặc trước đó (tôi chỉ theo dõi nó từ năm 1997)


4
Cám ơn vì cái này. Chỉ cần xác nhận từ bảng tính năng tại en.wikipedia.org/wiki/Getopts , nếu bạn cần hỗ trợ cho các tùy chọn dài, và bạn không ở trên Solaris, getoptlà cách để đi.
johncip

4
Tôi tin rằng điều đáng chú ý duy nhất getoptlà nó không thể được sử dụng một cách thuận tiện trong các tập lệnh trình bao bọc, trong đó người ta có thể có một vài tùy chọn cụ thể cho tập lệnh trình bao bọc, và sau đó chuyển các tùy chọn không phải trình bao bọc cho tệp thực thi được bao bọc, còn nguyên vẹn. Giả sử tôi có một greptrình bao bọc được gọi mygrepvà tôi có một tùy chọn --foocụ thể mygrep, sau đó tôi không thể thực hiện mygrep --foo -A 2-A 2tự động chuyển qua grep; Tôi cần phải làm mygrep --foo -- -A 2. Đây là triển khai của tôi trên đầu trang của giải pháp của bạn.
Kaushal Modi

2
@bobpaul Tuyên bố của bạn về produc-linux cũng sai và cũng sai: gói được đánh dấu là Essential Essential trên Ubuntu / Debian. Như vậy, nó luôn được cài đặt. - Bạn đang nói về những distro nào (nơi bạn nói nó cần được cài đặt trên mục đích)?
Robert Siemer

3
Lưu ý điều này không hoạt động trên Mac ít nhất lên đến 10.14.3 hiện tại. Getopt mà tàu là BSD getopt từ năm 1999 ...
jjj

2
@transang Phủ định Boolean của giá trị trả về. Và tác dụng phụ của nó: cho phép một lệnh thất bại (nếu không errexit sẽ hủy bỏ chương trình do lỗi). - Các ý kiến ​​trong kịch bản cho bạn biết nhiều hơn. man bash
Mặt

144

Cách cô đọng hơn

script.sh

#!/bin/bash

while [[ "$#" -gt 0 ]]; do
    case $1 in
        -d|--deploy) deploy="$2"; shift ;;
        -u|--uglify) uglify=1 ;;
        *) echo "Unknown parameter passed: $1"; exit 1 ;;
    esac
    shift
done

echo "Should deploy? $deploy"
echo "Should uglify? $uglify"

Sử dụng:

./script.sh -d dev -u

# OR:

./script.sh --deploy dev --uglify

3
Đây là những gì tôi đang làm. Phải while [[ "$#" > 1 ]]nếu tôi muốn hỗ trợ kết thúc dòng bằng cờ boolean ./script.sh --debug dev --uglify fast --verbose. Ví dụ: gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58
hfossli

12
Ồ Đơn giản và sạch sẽ! Đây là cách tôi đang sử dụng: gist.github.com/hfossli/4368aa5a577742c3c9f9266ed214aa58
hfossli

2
Điều này sẽ tốt hơn nhiều khi dán vào từng tập lệnh thay vì xử lý nguồn hoặc khiến mọi người tự hỏi chức năng của bạn thực sự bắt đầu từ đâu.
RealHandy

Cảnh báo: điều này chấp nhận các đối số trùng lặp, đối số mới nhất chiếm ưu thế. ví dụ ./script.sh -d dev -d prodsẽ dẫn đến deploy == 'prod'. Tôi vẫn sử dụng nó: P :): +1:
yair

Tôi đang sử dụng điều này (cảm ơn!) Nhưng lưu ý rằng nó cho phép giá trị đối số trống, ví dụ: ./script.sh -dsẽ không tạo ra lỗi mà chỉ được đặt $deploythành một chuỗi trống.
EM0

137

từ: digitalpeer.com với những sửa đổi nhỏ

Sử dụng myscript.sh -p=my_prefix -s=dirname -l=libname

#!/bin/bash
for i in "$@"
do
case $i in
    -p=*|--prefix=*)
    PREFIX="${i#*=}"

    ;;
    -s=*|--searchpath=*)
    SEARCHPATH="${i#*=}"
    ;;
    -l=*|--lib=*)
    DIR="${i#*=}"
    ;;
    --default)
    DEFAULT=YES
    ;;
    *)
            # unknown option
    ;;
esac
done
echo PREFIX = ${PREFIX}
echo SEARCH PATH = ${SEARCHPATH}
echo DIRS = ${DIR}
echo DEFAULT = ${DEFAULT}

Để hiểu rõ hơn về ${i#*=}tìm kiếm "Loại bỏ chuỗi con" trong hướng dẫn này . Nó có chức năng tương đương với việc `sed 's/[^=]*=//' <<< "$i"`gọi một quy trình con không cần thiết hoặc `echo "$i" | sed 's/[^=]*=//'`gọi hai quy trình con không cần thiết.


4
Khéo léo! Mặc dù điều này sẽ không làm việc cho các đối số phân tách không gian à la mount -t tempfs .... Một người có lẽ có thể khắc phục điều này thông qua một cái gì đó như while [ $# -ge 1 ]; do param=$1; shift; case $param in; -p) prefix=$1; shift;;vv
Tobias Kienzler

3
Điều này không thể xử lý -vfdphong cách kết hợp các tùy chọn ngắn.
Robert Siemer

105

getopt()/ getopts()là một lựa chọn tốt. Bị đánh cắp từ đây :

Cách sử dụng đơn giản "getopt" được hiển thị trong tập lệnh nhỏ này:

#!/bin/bash
echo "Before getopt"
for i
do
  echo $i
done
args=`getopt abc:d $*`
set -- $args
echo "After getopt"
for i
do
  echo "-->$i"
done

Những gì chúng tôi đã nói là bất kỳ -a, -b, -c hoặc -d sẽ được cho phép, nhưng -c được theo sau bởi một đối số ("c:" nói rằng).

Nếu chúng ta gọi đây là "g" và thử nó:

bash-2.05a$ ./g -abc foo
Before getopt
-abc
foo
After getopt
-->-a
-->-b
-->-c
-->foo
-->--

Chúng tôi bắt đầu với hai đối số và "getopt" phá vỡ các tùy chọn và đặt từng đối số vào đối số riêng của nó. Nó cũng thêm "-".


4
Sử dụng $*là sử dụng bị hỏng của getopt. (Nó tạo ra các đối số với khoảng trắng.) Xem câu trả lời của tôi để sử dụng đúng.
Robert Siemer

Tại sao bạn muốn làm cho nó phức tạp hơn?
SDsolar

@Matt J, phần đầu tiên của tập lệnh (đối với i) sẽ có thể xử lý các đối số có khoảng trắng trong chúng nếu bạn sử dụng "$ i" thay vì $ i. Các getopts dường như không thể xử lý các đối số có khoảng trắng. Điều gì sẽ là lợi thế của việc sử dụng getopt trên vòng lặp for i?
thebunnyrules

99

Có nguy cơ thêm một ví dụ khác để bỏ qua, đây là sơ đồ của tôi.

  • xử lý -n arg--name=arg
  • cho phép tranh luận ở cuối
  • hiển thị lỗi sane nếu bất cứ điều gì sai chính tả
  • tương thích, không sử dụng bashism
  • có thể đọc được, không yêu cầu duy trì trạng thái trong một vòng lặp

Hy vọng nó hữu ích cho ai đó.

while [ "$#" -gt 0 ]; do
  case "$1" in
    -n) name="$2"; shift 2;;
    -p) pidfile="$2"; shift 2;;
    -l) logfile="$2"; shift 2;;

    --name=*) name="${1#*=}"; shift 1;;
    --pidfile=*) pidfile="${1#*=}"; shift 1;;
    --logfile=*) logfile="${1#*=}"; shift 1;;
    --name|--pidfile|--logfile) echo "$1 requires an argument" >&2; exit 1;;

    -*) echo "unknown option: $1" >&2; exit 1;;
    *) handle_argument "$1"; shift 1;;
  esac
done

4
Xin lỗi về sự chậm trễ. Trong tập lệnh của tôi, hàm handle_argument nhận tất cả các đối số không phải là tùy chọn. Bạn có thể thay thế dòng đó bằng bất cứ thứ gì bạn thích, có thể *) die "unrecognized argument: $1"hoặc thu thập các đối số thành một biến *) args+="$1"; shift 1;;.
bronson

Kinh ngạc! Tôi đã thử nghiệm một vài câu trả lời, nhưng đây là câu trả lời duy nhất hoạt động cho tất cả các trường hợp, bao gồm nhiều tham số vị trí (cả trước và sau cờ)
Guilherme Garnier

2
mã ngắn gọn đẹp, nhưng sử dụng -n và không có đối số nào khác gây ra vòng lặp vô hạn do lỗi trên shift 2, phát hành shifthai lần thay vì shift 2. Đề nghị chỉnh sửa.
lauksas

42

Tôi trễ khoảng 4 năm cho câu hỏi này, nhưng muốn trả lại. Tôi đã sử dụng các câu trả lời trước đó như một điểm khởi đầu để dọn dẹp phân tích cú pháp adhoc param cũ của tôi. Sau đó tôi đã cấu trúc lại mã mẫu sau. Nó xử lý cả các tham số dài và ngắn, sử dụng các đối số được phân tách bằng dấu cách hoặc không gian, cũng như nhiều tham số ngắn được nhóm lại với nhau. Cuối cùng, nó chèn lại bất kỳ đối số không tham số nào vào các biến $ 1, $ 2 .. Tôi hy vọng nó hữu ích.

#!/usr/bin/env bash

# NOTICE: Uncomment if your script depends on bashisms.
#if [ -z "$BASH_VERSION" ]; then bash $0 $@ ; exit $? ; fi

echo "Before"
for i ; do echo - $i ; done


# Code template for parsing command line parameters using only portable shell
# code, while handling both long and short params, handling '-f file' and
# '-f=file' style param data and also capturing non-parameters to be inserted
# back into the shell positional parameters.

while [ -n "$1" ]; do
        # Copy so we can modify it (can't modify $1)
        OPT="$1"
        # Detect argument termination
        if [ x"$OPT" = x"--" ]; then
                shift
                for OPT ; do
                        REMAINS="$REMAINS \"$OPT\""
                done
                break
        fi
        # Parse current opt
        while [ x"$OPT" != x"-" ] ; do
                case "$OPT" in
                        # Handle --flag=value opts like this
                        -c=* | --config=* )
                                CONFIGFILE="${OPT#*=}"
                                shift
                                ;;
                        # and --flag value opts like this
                        -c* | --config )
                                CONFIGFILE="$2"
                                shift
                                ;;
                        -f* | --force )
                                FORCE=true
                                ;;
                        -r* | --retry )
                                RETRY=true
                                ;;
                        # Anything unknown is recorded for later
                        * )
                                REMAINS="$REMAINS \"$OPT\""
                                break
                                ;;
                esac
                # Check for multiple short options
                # NOTICE: be sure to update this pattern to match valid options
                NEXTOPT="${OPT#-[cfr]}" # try removing single short opt
                if [ x"$OPT" != x"$NEXTOPT" ] ; then
                        OPT="-$NEXTOPT"  # multiple short opts, keep going
                else
                        break  # long form, exit inner loop
                fi
        done
        # Done with that param. move to next
        shift
done
# Set the non-parameters back into the positional parameters ($1 $2 ..)
eval set -- $REMAINS


echo -e "After: \n configfile='$CONFIGFILE' \n force='$FORCE' \n retry='$RETRY' \n remains='$REMAINS'"
for i ; do echo - $i ; done

Mã này không thể xử lý các tùy chọn với các đối số như thế này : -c1. Và việc sử dụng =để tách các tùy chọn ngắn khỏi các đối số của họ là không bình thường ...
Robert Siemer 6/12/2015

2
Tôi gặp hai vấn đề với đoạn mã hữu ích này: 1) "sự thay đổi" trong trường hợp "-c = foo" kết thúc bằng việc ăn tham số tiếp theo; và 2) 'c' không nên được bao gồm trong mẫu "[cfr]" cho các tùy chọn ngắn có thể kết hợp.
sfnd

36

Tôi đã tìm thấy vấn đề để viết phân tích cú pháp di động trong các tập lệnh đến nỗi bực bội đến nỗi tôi đã viết Argbash - trình tạo mã FOSS có thể tạo mã phân tích đối số cho tập lệnh của bạn và nó có một số tính năng hay:

https://argbash.io


Cảm ơn bạn đã viết argbash, tôi chỉ sử dụng nó và thấy nó hoạt động tốt. Tôi chủ yếu dùng argbash vì đây là trình tạo mã hỗ trợ bash 3.x cũ hơn được tìm thấy trên OS X 10.11 El Capitan. Nhược điểm duy nhất là cách tiếp cận trình tạo mã có nghĩa là khá nhiều mã trong tập lệnh chính của bạn, so với việc gọi một mô-đun.
RichVel

Bạn thực sự có thể sử dụng Argbash theo cách nó tạo ra thư viện phân tích cú pháp được thiết kế riêng cho bạn mà bạn có thể đưa vào tập lệnh của mình hoặc bạn có thể có nó trong một tệp riêng biệt và chỉ cần nguồn nó. Tôi đã thêm một ví dụ để chứng minh điều đó và tôi cũng đã làm cho nó rõ ràng hơn trong tài liệu.
bubla

Tốt để biết. Ví dụ đó rất thú vị nhưng vẫn chưa thực sự rõ ràng - có thể bạn có thể thay đổi tên của tập lệnh được tạo thành 'parse_lib.sh' hoặc tương tự và hiển thị nơi tập lệnh chính gọi nó (như trong phần tập lệnh đóng gói là trường hợp sử dụng phức tạp hơn).
RichVel

Các vấn đề đã được giải quyết trong phiên bản gần đây của argbash: Tài liệu đã được cải thiện, một kịch bản argbash-init nhanh chóng đã được giới thiệu và bạn thậm chí có thể sử dụng argbash trực tuyến tại argbash.io/generate
bubla

29

Câu trả lời của tôi chủ yếu dựa trên câu trả lời của Bruno Bronosky , nhưng tôi đã nghiền nát hai cách thực hiện bash thuần túy của anh ấy thành một cách mà tôi sử dụng khá thường xuyên.

# As long as there is at least one more argument, keep looping
while [[ $# -gt 0 ]]; do
    key="$1"
    case "$key" in
        # This is a flag type option. Will catch either -f or --foo
        -f|--foo)
        FOO=1
        ;;
        # Also a flag type option. Will catch either -b or --bar
        -b|--bar)
        BAR=1
        ;;
        # This is an arg value type option. Will catch -o value or --output-file value
        -o|--output-file)
        shift # past the key and to the value
        OUTPUTFILE="$1"
        ;;
        # This is an arg=value type option. Will catch -o=value or --output-file=value
        -o=*|--output-file=*)
        # No need to shift here since the value is part of the same string
        OUTPUTFILE="${key#*=}"
        ;;
        *)
        # Do whatever you want with extra options
        echo "Unknown option '$key'"
        ;;
    esac
    # Shift after checking all the cases to get the next option
    shift
done

Điều này cho phép bạn có cả hai tùy chọn / giá trị được phân tách bằng dấu cách, cũng như các giá trị được xác định bằng nhau.

Vì vậy, bạn có thể chạy tập lệnh của mình bằng cách sử dụng:

./myscript --foo -b -o /fizz/file.txt

cũng như:

./myscript -f --bar -o=/fizz/file.txt

và cả hai nên có kết quả cuối cùng.

PROS:

  • Cho phép cả -arg = value và -arg value

  • Hoạt động với bất kỳ tên arg mà bạn có thể sử dụng trong bash

    • Có nghĩa là -a hoặc -arg hoặc --arg hoặc -arg hoặc bất cứ điều gì
  • Bash tinh khiết. Không cần phải học / sử dụng getopt hoặc getopts

Nhược điểm:

  • Không thể kết hợp args

    • Có nghĩa là không -abc. Bạn phải làm -a -b -c

Đây là những ưu / nhược điểm duy nhất tôi có thể nghĩ ra khỏi đỉnh đầu


15

Tôi nghĩ cái này đủ đơn giản để sử dụng:

#!/bin/bash
#

readopt='getopts $opts opt;rc=$?;[ $rc$opt == 0? ]&&exit 1;[ $rc == 0 ]||{ shift $[OPTIND-1];false; }'

opts=vfdo:

# Enumerating options
while eval $readopt
do
    echo OPT:$opt ${OPTARG+OPTARG:$OPTARG}
done

# Enumerating arguments
for arg
do
    echo ARG:$arg
done

Ví dụ về lời mời

./myscript -v -do /fizz/someOtherFile -f ./foo/bar/someFile
OPT:v 
OPT:d 
OPT:o OPTARG:/fizz/someOtherFile
OPT:f 
ARG:./foo/bar/someFile

1
Tôi đọc tất cả và cái này là cái tôi thích. Tôi không thích sử dụng -a=1như phong cách argc. Tôi thích đặt đầu tiên các tùy chọn chính và sau đó là các tùy chọn đặc biệt với khoảng cách đơn -o option. Tôi đang tìm cách đơn giản nhất so với tốt hơn để đọc argvs.
m3nda

Nó hoạt động rất tốt nhưng nếu bạn chuyển một đối số sang tùy chọn không phải là a thì tất cả các tùy chọn sau sẽ được coi là đối số. Bạn có thể kiểm tra dòng này ./myscript -v -d fail -o /fizz/someOtherFile -f ./foo/bar/someFilevới kịch bản của riêng bạn. Tùy chọn -d không được đặt là d:
m3nda

15

Mở rộng câu trả lời xuất sắc của @guneysus, đây là một tinh chỉnh cho phép người dùng sử dụng bất kỳ cú pháp nào họ thích, ví dụ:

command -x=myfilename.ext --another_switch 

đấu với

command -x myfilename.ext --another_switch

Đó là để nói rằng các bằng có thể được thay thế bằng khoảng trắng.

"Giải thích mờ" này có thể không theo ý thích của bạn, nhưng nếu bạn đang tạo các tập lệnh có thể hoán đổi cho nhau với các tiện ích khác (như trường hợp của tôi, phải hoạt động với ffmpeg), thì tính linh hoạt là hữu ích.

STD_IN=0

prefix=""
key=""
value=""
for keyValue in "$@"
do
  case "${prefix}${keyValue}" in
    -i=*|--input_filename=*)  key="-i";     value="${keyValue#*=}";; 
    -ss=*|--seek_from=*)      key="-ss";    value="${keyValue#*=}";;
    -t=*|--play_seconds=*)    key="-t";     value="${keyValue#*=}";;
    -|--stdin)                key="-";      value=1;;
    *)                                      value=$keyValue;;
  esac
  case $key in
    -i) MOVIE=$(resolveMovie "${value}");  prefix=""; key="";;
    -ss) SEEK_FROM="${value}";          prefix=""; key="";;
    -t)  PLAY_SECONDS="${value}";           prefix=""; key="";;
    -)   STD_IN=${value};                   prefix=""; key="";; 
    *)   prefix="${keyValue}=";;
  esac
done

13

Ví dụ này cho thấy cách sử dụng getoptevalHEREDOCshiftxử lý các thông số ngắn hạn và dài có và không có giá trị bắt buộc rằng sau. Ngoài ra, tuyên bố chuyển đổi / trường hợp ngắn gọn và dễ làm theo.

#!/usr/bin/env bash

# usage function
function usage()
{
   cat << HEREDOC

   Usage: $progname [--num NUM] [--time TIME_STR] [--verbose] [--dry-run]

   optional arguments:
     -h, --help           show this help message and exit
     -n, --num NUM        pass in a number
     -t, --time TIME_STR  pass in a time string
     -v, --verbose        increase the verbosity of the bash script
     --dry-run            do a dry run, dont change any files

HEREDOC
}  

# initialize variables
progname=$(basename $0)
verbose=0
dryrun=0
num_str=
time_str=

# use getopt and store the output into $OPTS
# note the use of -o for the short options, --long for the long name options
# and a : for any option that takes a parameter
OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; usage; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  # uncomment the next line to see how shift is working
  # echo "\$1:\"$1\" \$2:\"$2\""
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

if (( $verbose > 0 )); then

   # print out all the parameters we read in
   cat <<-EOM
   num=$num_str
   time=$time_str
   verbose=$verbose
   dryrun=$dryrun
EOM
fi

# The rest of your script below

Các dòng quan trọng nhất của tập lệnh ở trên là:

OPTS=$(getopt -o "hn:t:v" --long "help,num:,time:,verbose,dry-run" -n "$progname" -- "$@")
if [ $? != 0 ] ; then echo "Error in command line arguments." >&2 ; exit 1 ; fi
eval set -- "$OPTS"

while true; do
  case "$1" in
    -h | --help ) usage; exit; ;;
    -n | --num ) num_str="$2"; shift 2 ;;
    -t | --time ) time_str="$2"; shift 2 ;;
    --dry-run ) dryrun=1; shift ;;
    -v | --verbose ) verbose=$((verbose + 1)); shift ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

Ngắn gọn, có thể đọc được và xử lý mọi thứ (IMHO).

Hy vọng rằng sẽ giúp được ai đó.


1
Đây là một trong những câu trả lời tốt nhất.
Ông Polywhirl

11

Tôi cung cấp cho bạn Hàm parse_paramssẽ phân tích các tham số từ dòng lệnh.

  1. Nó là một giải pháp Bash thuần túy, không có tiện ích bổ sung.
  2. Không gây ô nhiễm phạm vi toàn cầu.
  3. Dễ dàng trả về cho bạn các biến đơn giản để sử dụng các biến, mà bạn có thể xây dựng logic hơn nữa.
  4. Số tiền của dấu gạch ngang trước params không quan trọng ( --alltương đương với -allbình đẳng all=all)

Kịch bản dưới đây là một bản trình diễn làm việc sao chép-dán. Xem show_usechức năng để hiểu làm thế nào để sử dụng parse_params.

Hạn chế:

  1. Không hỗ trợ các tham số được phân cách bằng dấu cách ( -d 1)
  2. Tên tham số sẽ mất dấu gạch ngang --any-param-anyparamtương đương
  3. eval $(parse_params "$@")phải được sử dụng bên trong hàm bash (nó sẽ không hoạt động trong phạm vi toàn cầu)

#!/bin/bash

# Universal Bash parameter parsing
# Parse equal sign separated params into named local variables
# Standalone named parameter value will equal its param name (--force creates variable $force=="force")
# Parses multi-valued named params into an array (--path=path1 --path=path2 creates ${path[*]} array)
# Puts un-named params as-is into ${ARGV[*]} array
# Additionally puts all named params as-is into ${ARGN[*]} array
# Additionally puts all standalone "option" params as-is into ${ARGO[*]} array
# @author Oleksii Chekulaiev
# @version v1.4.1 (Jul-27-2018)
parse_params ()
{
    local existing_named
    local ARGV=() # un-named params
    local ARGN=() # named params
    local ARGO=() # options (--params)
    echo "local ARGV=(); local ARGN=(); local ARGO=();"
    while [[ "$1" != "" ]]; do
        # Escape asterisk to prevent bash asterisk expansion, and quotes to prevent string breakage
        _escaped=${1/\*/\'\"*\"\'}
        _escaped=${_escaped//\'/\\\'}
        _escaped=${_escaped//\"/\\\"}
        # If equals delimited named parameter
        nonspace="[^[:space:]]"
        if [[ "$1" =~ ^${nonspace}${nonspace}*=..* ]]; then
            # Add to named parameters array
            echo "ARGN+=('$_escaped');"
            # key is part before first =
            local _key=$(echo "$1" | cut -d = -f 1)
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # val is everything after key and = (protect from param==value error)
            local _val="${1/$_key=}"
            # remove dashes from key name
            _key=${_key//\-}
            # skip when key is empty
            # search for existing parameter name
            if (echo "$existing_named" | grep "\b$_key\b" >/dev/null); then
                # if name already exists then it's a multi-value named parameter
                # re-declare it as an array if needed
                if ! (declare -p _key 2> /dev/null | grep -q 'declare \-a'); then
                    echo "$_key=(\"\$$_key\");"
                fi
                # append new value
                echo "$_key+=('$_val');"
            else
                # single-value named parameter
                echo "local $_key='$_val';"
                existing_named=" $_key"
            fi
        # If standalone named parameter
        elif [[ "$1" =~ ^\-${nonspace}+ ]]; then
            # remove dashes
            local _key=${1//\-}
            # Just add as non-named when key is empty or contains space
            if [[ "$_key" == "" || "$_key" =~ " " ]]; then
                echo "ARGV+=('$_escaped');"
                shift
                continue
            fi
            # Add to options array
            echo "ARGO+=('$_escaped');"
            echo "local $_key=\"$_key\";"
        # non-named parameter
        else
            # Escape asterisk to prevent bash asterisk expansion
            _escaped=${1/\*/\'\"*\"\'}
            echo "ARGV+=('$_escaped');"
        fi
        shift
    done
}

#--------------------------- DEMO OF THE USAGE -------------------------------

show_use ()
{
    eval $(parse_params "$@")
    # --
    echo "${ARGV[0]}" # print first unnamed param
    echo "${ARGV[1]}" # print second unnamed param
    echo "${ARGN[0]}" # print first named param
    echo "${ARG0[0]}" # print first option param (--force)
    echo "$anyparam"  # print --anyparam value
    echo "$k"         # print k=5 value
    echo "${multivalue[0]}" # print first value of multi-value
    echo "${multivalue[1]}" # print second value of multi-value
    [[ "$force" == "force" ]] && echo "\$force is set so let the force be with you"
}

show_use "param 1" --anyparam="my value" param2 k=5 --force --multi-value=test1 --multi-value=test2

Để sử dụng bản demo để phân tích các thông số đi vào tập lệnh bash bạn chỉ cần thực hiệnshow_use "$@"
Oleksii Chekulaiev

Về cơ bản tôi phát hiện ra rằng github.com/renatosilva/easyoptions cũng làm tương tự theo cách tương tự nhưng lớn hơn một chút so với chức năng này.
Oleksii Chekulaiev

10

EasyOptions không yêu cầu bất kỳ phân tích cú pháp nào:

## Options:
##   --verbose, -v  Verbose mode
##   --output=FILE  Output filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "output file is ${output}"
    echo "${arguments[@]}"
fi

Phải mất một phút để tôi nhận ra rằng các nhận xét ở đầu tập lệnh ví dụ của bạn đang được phân tích cú pháp để cung cấp chuỗi trợ giúp sử dụng mặc định, cũng như thông số kỹ thuật đối số. Đây là một giải pháp tuyệt vời và tôi xin lỗi vì nó chỉ nhận được 6 phiếu trong 2 năm. Có lẽ câu hỏi này quá lầy để mọi người chú ý.
Biến thái

Theo một nghĩa nào đó, giải pháp của bạn là tốt nhất (ngoài @ OleksiiChekulaiev không hỗ trợ cú pháp tùy chọn "tiêu chuẩn"). Điều này là do giải pháp của bạn chỉ yêu cầu người viết kịch bản chỉ định tên của từng tùy chọn một lần . Thực tế là các giải pháp khác yêu cầu nó phải được chỉ định 3 lần - trong cách sử dụng, trong mẫu 'trường hợp' và trong cài đặt của biến - đã liên tục làm tôi khó chịu. Ngay cả getopt cũng có vấn đề này. Tuy nhiên, mã của bạn chậm trên máy của tôi - 0,11 giây cho việc triển khai Bash, 0,28 giây cho Ruby. Versus 0,02s để phân tích cú pháp "trong trường hợp" rõ ràng.
Biến thái

Tôi muốn một phiên bản nhanh hơn, có thể được viết bằng C. Ngoài ra, một phiên bản tương thích với zsh. Có lẽ điều này xứng đáng có một câu hỏi riêng biệt ("Có cách nào để phân tích các đối số dòng lệnh trong các shell giống Bash chấp nhận cú pháp tùy chọn dài tiêu chuẩn và không yêu cầu phải nhập tên tùy chọn nhiều lần không?").
Biến thái

10

getopts hoạt động tuyệt vời nếu # 1 bạn đã cài đặt và # 2 bạn có ý định chạy nó trên cùng một nền tảng. OSX và Linux (ví dụ) hành xử khác nhau về mặt này.

Dưới đây là một giải pháp (không getopts) hỗ trợ các cờ bằng, không bằng và cờ boolean. Ví dụ: bạn có thể chạy tập lệnh của mình theo cách này:

./script --arg1=value1 --arg2 value2 --shouldClean

# parse the arguments.
COUNTER=0
ARGS=("$@")
while [ $COUNTER -lt $# ]
do
    arg=${ARGS[$COUNTER]}
    let COUNTER=COUNTER+1
    nextArg=${ARGS[$COUNTER]}

    if [[ $skipNext -eq 1 ]]; then
        echo "Skipping"
        skipNext=0
        continue
    fi

    argKey=""
    argVal=""
    if [[ "$arg" =~ ^\- ]]; then
        # if the format is: -key=value
        if [[ "$arg" =~ \= ]]; then
            argVal=$(echo "$arg" | cut -d'=' -f2)
            argKey=$(echo "$arg" | cut -d'=' -f1)
            skipNext=0

        # if the format is: -key value
        elif [[ ! "$nextArg" =~ ^\- ]]; then
            argKey="$arg"
            argVal="$nextArg"
            skipNext=1

        # if the format is: -key (a boolean flag)
        elif [[ "$nextArg" =~ ^\- ]] || [[ -z "$nextArg" ]]; then
            argKey="$arg"
            argVal=""
            skipNext=0
        fi
    # if the format has not flag, just a value.
    else
        argKey=""
        argVal="$arg"
        skipNext=0
    fi

    case "$argKey" in 
        --source-scmurl)
            SOURCE_URL="$argVal"
        ;;
        --dest-scmurl)
            DEST_URL="$argVal"
        ;;
        --version-num)
            VERSION_NUM="$argVal"
        ;;
        -c|--clean)
            CLEAN_BEFORE_START="1"
        ;;
        -h|--help|-help|--h)
            showUsage
            exit
        ;;
    esac
done

8

Đây là cách tôi thực hiện trong một hàm để tránh phá vỡ các getopts chạy cùng lúc ở đâu đó cao hơn trong stack:

function waitForWeb () {
   local OPTIND=1 OPTARG OPTION
   local host=localhost port=8080 proto=http
   while getopts "h:p:r:" OPTION; do
      case "$OPTION" in
      h)
         host="$OPTARG"
         ;;
      p)
         port="$OPTARG"
         ;;
      r)
         proto="$OPTARG"
         ;;
      esac
   done
...
}

8

Mở rộng câu trả lời của @ bruno-bronosky, tôi đã thêm một "tiền xử lý" để xử lý một số định dạng phổ biến:

  • Mở rộng --longopt=valthành--longopt val
  • Mở rộng -xyzthành-x -y -z
  • Hỗ trợ --để chỉ ra sự kết thúc của cờ
  • Hiển thị lỗi cho các tùy chọn không mong muốn
  • Công tắc tùy chọn nhỏ gọn và dễ đọc
#!/bin/bash

# Report usage
usage() {
  echo "Usage:"
  echo "$(basename $0) [options] [--] [file1, ...]"

  # Optionally exit with a status code
  if [ -n "$1" ]; then
    exit "$1"
  fi
}

invalid() {
  echo "ERROR: Unrecognized argument: $1" >&2
  usage 1
}

# Pre-process options to:
# - expand -xyz into -x -y -z
# - expand --longopt=arg into --longopt arg
ARGV=()
END_OF_OPT=
while [[ $# -gt 0 ]]; do
  arg="$1"; shift
  case "${END_OF_OPT}${arg}" in
    --) ARGV+=("$arg"); END_OF_OPT=1 ;;
    --*=*)ARGV+=("${arg%%=*}" "${arg#*=}") ;;
    --*) ARGV+=("$arg"); END_OF_OPT=1 ;;
    -*) for i in $(seq 2 ${#arg}); do ARGV+=("-${arg:i-1:1}"); done ;;
    *) ARGV+=("$arg") ;;
  esac
done

# Apply pre-processed options
set -- "${ARGV[@]}"

# Parse options
END_OF_OPT=
POSITIONAL=()
while [[ $# -gt 0 ]]; do
  case "${END_OF_OPT}${1}" in
    -h|--help)      usage 0 ;;
    -p|--password)  shift; PASSWORD="$1" ;;
    -u|--username)  shift; USERNAME="$1" ;;
    -n|--name)      shift; names+=("$1") ;;
    -q|--quiet)     QUIET=1 ;;
    -C|--copy)      COPY=1 ;;
    -N|--notify)    NOTIFY=1 ;;
    --stdin)        READ_STDIN=1 ;;
    --)             END_OF_OPT=1 ;;
    -*)             invalid "$1" ;;
    *)              POSITIONAL+=("$1") ;;
  esac
  shift
done

# Restore positional parameters
set -- "${POSITIONAL[@]}"

6

Có một số cách để phân tích cmdline args (ví dụ: GNU getopt (không di động) so với BSD (OSX) getopt so với getopts) - tất cả đều có vấn đề. Giải pháp này là

  • xách tay!
  • không phụ thuộc, chỉ dựa vào bash dựng sẵn
  • cho phép cả hai tùy chọn ngắn và dài
  • xử lý khoảng trắng giữa tùy chọn và đối số nhưng cũng có thể sử dụng =dấu phân cách
  • hỗ trợ kiểu tùy chọn ngắn nối -vxf
  • xử lý tùy chọn với các đối số tùy chọn (xem ví dụ) và
  • không yêu cầu phình mã so với các lựa chọn thay thế cho cùng một bộ tính năng. Tức là cô đọng, và do đó dễ duy trì hơn

Ví dụ: Bất kỳ

# flag
-f
--foo

# option with required argument
-b"Hello World"
-b "Hello World"
--bar "Hello World"
--bar="Hello World"

# option with optional argument
--baz
--baz="Optional Hello"

#!/usr/bin/env bash

usage() {
  cat - >&2 <<EOF
NAME
    program-name.sh - Brief description

SYNOPSIS
    program-name.sh [-h|--help]
    program-name.sh [-f|--foo]
                    [-b|--bar <arg>]
                    [--baz[=<arg>]]
                    [--]
                    FILE ...

REQUIRED ARGUMENTS
  FILE ...
          input files

OPTIONS
  -h, --help
          Prints this and exits

  -f, --foo
          A flag option

  -b, --bar <arg>
          Option requiring an argument <arg>

  --baz[=<arg>]
          Option that has an optional argument <arg>. If <arg>
          is not specified, defaults to 'DEFAULT'
  --     
          Specify end of options; useful if the first non option
          argument starts with a hyphen

EOF
}

fatal() {
    for i; do
        echo -e "${i}" >&2
    done
    exit 1
}

# For long option processing
next_arg() {
    if [[ $OPTARG == *=* ]]; then
        # for cases like '--opt=arg'
        OPTARG="${OPTARG#*=}"
    else
        # for cases like '--opt arg'
        OPTARG="${args[$OPTIND]}"
        OPTIND=$((OPTIND + 1))
    fi
}

# ':' means preceding option character expects one argument, except
# first ':' which make getopts run in silent mode. We handle errors with
# wildcard case catch. Long options are considered as the '-' character
optspec=":hfb:-:"
args=("" "$@")  # dummy first element so $1 and $args[1] are aligned
while getopts "$optspec" optchar; do
    case "$optchar" in
        h) usage; exit 0 ;;
        f) foo=1 ;;
        b) bar="$OPTARG" ;;
        -) # long option processing
            case "$OPTARG" in
                help)
                    usage; exit 0 ;;
                foo)
                    foo=1 ;;
                bar|bar=*) next_arg
                    bar="$OPTARG" ;;
                baz)
                    baz=DEFAULT ;;
                baz=*) next_arg
                    baz="$OPTARG" ;;
                -) break ;;
                *) fatal "Unknown option '--${OPTARG}'" "see '${0} --help' for usage" ;;
            esac
            ;;
        *) fatal "Unknown option: '-${OPTARG}'" "See '${0} --help' for usage" ;;
    esac
done

shift $((OPTIND-1))

if [ "$#" -eq 0 ]; then
    fatal "Expected at least one required argument FILE" \
    "See '${0} --help' for usage"
fi

echo "foo=$foo, bar=$bar, baz=$baz, files=${@}"

5

Tôi muốn cung cấp phiên bản phân tích tùy chọn của mình, cho phép như sau:

-s p1
--stage p1
-w somefolder
--workfolder somefolder
-sw p1 somefolder
-e=hello

Cũng cho phép điều này (có thể là không mong muốn):

-s--workfolder p1 somefolder
-se=hello p1
-swe=hello p1 somefolder

Bạn phải quyết định trước khi sử dụng if = có được sử dụng trên một tùy chọn hay không. Điều này là để giữ cho mã sạch (ish).

while [[ $# > 0 ]]
do
    key="$1"
    while [[ ${key+x} ]]
    do
        case $key in
            -s*|--stage)
                STAGE="$2"
                shift # option has parameter
                ;;
            -w*|--workfolder)
                workfolder="$2"
                shift # option has parameter
                ;;
            -e=*)
                EXAMPLE="${key#*=}"
                break # option has been fully handled
                ;;
            *)
                # unknown option
                echo Unknown option: $key #1>&2
                exit 10 # either this: my preferred way to handle unknown options
                break # or this: do this to signal the option has been handled (if exit isn't used)
                ;;
        esac
        # prepare for next option in this key, if any
        [[ "$key" = -? || "$key" == --* ]] && unset key || key="${key/#-?/-}"
    done
    shift # option(s) fully processed, proceed to next input argument
done

1
ý nghĩa của "+ x" trên $ {key + x} là gì?
Luca Davanzo

1
Đây là một thử nghiệm để xem liệu 'chìa khóa' có mặt hay không. Tiếp tục xuống tôi bỏ cài đặt khóa và điều này phá vỡ vòng lặp while.
galmok

5

Giải pháp bảo tồn các lập luận chưa được xử lý. Bao gồm các bản demo.

Đây là giải pháp của tôi. Nó rất linh hoạt và không giống như những người khác, không nên yêu cầu các gói bên ngoài và xử lý các đối số còn sót lại một cách sạch sẽ.

Cách sử dụng là: ./myscript -flag flagvariable -otherflag flagvar2

Tất cả bạn phải làm là chỉnh sửa dòng hợp lệ. Nó chuẩn bị một dấu gạch nối và tìm kiếm tất cả các đối số. Sau đó, nó định nghĩa đối số tiếp theo là tên cờ, vd

./myscript -flag flagvariable -otherflag flagvar2
echo $flag $otherflag
flagvariable flagvar2

Mã chính (phiên bản ngắn, dài dòng với các ví dụ tiếp theo, cũng là một phiên bản có lỗi):

#!/usr/bin/env bash
#shebang.io
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
    for flag in $validflags
    do
        sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers

Phiên bản dài dòng được xây dựng trong các bản demo tiếng vang:

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
echo "all args
$@"
validflags="rate time number"
count=1
for arg in $@
do
    match=0
    argval=$1
#   argval=$(echo $@ | cut -d ' ' -f$count)
    for flag in $validflags
    do
            sflag="-"$flag
        if [ "$argval" == "$sflag" ]
        then
            declare $flag=$2
            match=1
        fi
    done
        if [ "$match" == "1" ]
    then
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done

#Cleanup then restore the leftovers
echo "pre final clear args:
$@"
shift $#
echo "post final clear args:
$@"
set -- $leftovers
echo "all post set args:
$@"
echo arg1: $1 arg2: $2

echo leftovers: $leftovers
echo rate $rate time $time number $number

Cuối cùng, lỗi này sẽ xảy ra nếu một thông tin không hợp lệ được thông qua.

#!/usr/bin/env bash
#shebang.io
rate=30
time=30
number=30
validflags="rate time number"
count=1
for arg in $@
do
    argval=$1
    match=0
        if [ "${argval:0:1}" == "-" ]
    then
        for flag in $validflags
        do
                sflag="-"$flag
            if [ "$argval" == "$sflag" ]
            then
                declare $flag=$2
                match=1
            fi
        done
        if [ "$match" == "0" ]
        then
            echo "Bad argument: $argval"
            exit 1
        fi
        shift 2
    else
        leftovers=$(echo $leftovers $argval)
        shift
    fi
    count=$(($count+1))
done
#Cleanup then restore the leftovers
shift $#
set -- $leftovers
echo rate $rate time $time number $number
echo leftovers: $leftovers

Ưu điểm: Những gì nó làm, nó xử lý rất tốt. Nó bảo tồn các đối số không sử dụng mà rất nhiều giải pháp khác ở đây không có. Nó cũng cho phép các biến được gọi mà không được xác định bằng tay trong tập lệnh. Nó cũng cho phép chuẩn bị trước các biến nếu không có đối số tương ứng được đưa ra. (Xem ví dụ dài dòng).

Nhược điểm: Không thể phân tích một chuỗi arg phức tạp đơn lẻ, ví dụ -xcvf sẽ xử lý như một đối số duy nhất. Bạn có thể dễ dàng viết mã bổ sung vào phần bổ sung chức năng này.



3

Lưu ý rằng đó getopt(1)là một sai lầm sống ngắn từ AT & T.

getopt được tạo ra vào năm 1984 nhưng đã bị chôn vùi vào năm 1986 vì nó không thực sự có thể sử dụng được.

Một bằng chứng cho thực tế đã getoptrất lỗi thời là getopt(1)trang người đàn ông vẫn đề cập "$*"thay vì "$@", đã được thêm vào Bourne Shell vào năm 1986 cùng với getopts(1)vỏ được xây dựng để xử lý các đối số với khoảng trắng bên trong.

BTW: nếu bạn quan tâm đến việc phân tích các tùy chọn dài trong các tập lệnh shell, có thể bạn nên biết rằng việc getopt(3)triển khai từ libc (Solaris) và ksh93cả hai đã thêm một triển khai tùy chọn dài thống nhất hỗ trợ các tùy chọn dài làm bí danh cho các tùy chọn ngắn. Điều này gây ra ksh93Bourne Shellđể thực hiện một giao diện thống nhất cho các tùy chọn dài thông qua getopts.

Một ví dụ cho các tùy chọn dài được lấy từ trang man Bourne Shell:

getopts "f:(file)(input-file)o:(output-file)" OPTX "$@"

cho thấy các bí danh tùy chọn có thể được sử dụng trong cả Bourne Shell và ksh93.

Xem trang người đàn ông của Bourne Shell gần đây:

http://schillix.sourceforge.net/man/man1/bosh.1.html

và trang man cho getopt (3) từ OpenSolaris:

http://schillix.sourceforge.net/man/man3c/getopt.3c.html

và cuối cùng, trang man getopt (1) để xác minh $ * lỗi thời:

http://schillix.sourceforge.net/man/man1/getopt.1.html


3

Tôi đã viết một trình trợ giúp bash để viết một công cụ bash đẹp

nhà dự án: https://gitlab.mbedsys.org/mbedsys/bashopts

thí dụ:

#!/bin/bash -ei

# load the library
. bashopts.sh

# Enable backtrace dusplay on error
trap 'bashopts_exit_handle' ERR

# Initialize the library
bashopts_setup -n "$0" -d "This is myapp tool description displayed on help message" -s "$HOME/.config/myapprc"

# Declare the options
bashopts_declare -n first_name -l first -o f -d "First name" -t string -i -s -r
bashopts_declare -n last_name -l last -o l -d "Last name" -t string -i -s -r
bashopts_declare -n display_name -l display-name -t string -d "Display name" -e "\$first_name \$last_name"
bashopts_declare -n age -l number -d "Age" -t number
bashopts_declare -n email_list -t string -m add -l email -d "Email adress"

# Parse arguments
bashopts_parse_args "$@"

# Process argument
bashopts_process_args

sẽ giúp đỡ:

NAME:
    ./example.sh - This is myapp tool description displayed on help message

USAGE:
    [options and commands] [-- [extra args]]

OPTIONS:
    -h,--help                          Display this help
    -n,--non-interactive true          Non interactive mode - [$bashopts_non_interactive] (type:boolean, default:false)
    -f,--first "John"                  First name - [$first_name] (type:string, default:"")
    -l,--last "Smith"                  Last name - [$last_name] (type:string, default:"")
    --display-name "John Smith"        Display name - [$display_name] (type:string, default:"$first_name $last_name")
    --number 0                         Age - [$age] (type:number, default:0)
    --email                            Email adress - [$email_list] (type:string, default:"")

thưởng thức :)


Tôi nhận được điều này trên Mac OS X: `` `lib / bashopts.sh: dòng 138: khai báo: -A: tùy chọn không hợp lệ khai báo: cách sử dụng: khai báo [-afFirtx] [-p] [name [= value] ...] Lỗi trong lib / bashopts.sh: 138. 'khai báo -x -A bashopts_optprop_name' đã thoát với trạng thái 2 Gọi cây: 1: lib / control.sh: 4 nguồn (...) Thoát với trạng thái 1 `` `
Josh Wulf

Bạn cần Bash phiên bản 4 để sử dụng cái này. Trên Mac, phiên bản mặc định là 3. Bạn có thể sử dụng bia tại nhà để cài đặt bash 4.
Josh Wulf

3

Đây là cách tiếp cận của tôi - sử dụng regrec.

  • không có
  • nó xử lý khối các tham số ngắn -qwerty
  • nó xử lý các tham số ngắn -q -w -e
  • nó xử lý các tùy chọn dài --qwerty
  • bạn có thể chuyển thuộc tính cho tùy chọn ngắn hoặc dài (nếu bạn đang sử dụng khối tùy chọn ngắn, thuộc tính được đính kèm với tùy chọn cuối cùng)
  • bạn có thể sử dụng dấu cách hoặc =để cung cấp các thuộc tính, nhưng khớp thuộc tính cho đến khi gặp dấu gạch nối + dấu cách "dấu cách", v.v.--q=qwe ty qwe ty là một thuộc tính
  • nó xử lý hỗn hợp của tất cả các bên trên vì vậy -o a -op attr ibute --option=att ribu te --op-tion attribute --option att-ributelà hợp lệ

kịch bản:

#!/usr/bin/env sh

help_menu() {
  echo "Usage:

  ${0##*/} [-h][-l FILENAME][-d]

Options:

  -h, --help
    display this help and exit

  -l, --logfile=FILENAME
    filename

  -d, --debug
    enable debug
  "
}

parse_options() {
  case $opt in
    h|help)
      help_menu
      exit
     ;;
    l|logfile)
      logfile=${attr}
      ;;
    d|debug)
      debug=true
      ;;
    *)
      echo "Unknown option: ${opt}\nRun ${0##*/} -h for help.">&2
      exit 1
  esac
}
options=$@

until [ "$options" = "" ]; do
  if [[ $options =~ (^ *(--([a-zA-Z0-9-]+)|-([a-zA-Z0-9-]+))(( |=)(([\_\.\?\/\\a-zA-Z0-9]?[ -]?[\_\.\?a-zA-Z0-9]+)+))?(.*)|(.+)) ]]; then
    if [[ ${BASH_REMATCH[3]} ]]; then # for --option[=][attribute] or --option[=][attribute]
      opt=${BASH_REMATCH[3]}
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    elif [[ ${BASH_REMATCH[4]} ]]; then # for block options -qwert[=][attribute] or single short option -a[=][attribute]
      pile=${BASH_REMATCH[4]}
      while (( ${#pile} > 1 )); do
        opt=${pile:0:1}
        attr=""
        pile=${pile/${pile:0:1}/}
        parse_options
      done
      opt=$pile
      attr=${BASH_REMATCH[7]}
      options=${BASH_REMATCH[9]}
    else # leftovers that don't match
      opt=${BASH_REMATCH[10]}
      options=""
    fi
    parse_options
  fi
done

Như cái này. Có lẽ chỉ cần thêm -e param để lặp lại với dòng mới.
mauron85

3

Giả sử chúng ta tạo một tập lệnh shell có tên test_args.shnhư sau

#!/bin/sh
until [ $# -eq 0 ]
do
  name=${1:1}; shift;
  if [[ -z "$1" || $1 == -* ]] ; then eval "export $name=true"; else eval "export $name=$1"; shift; fi  
done
echo "year=$year month=$month day=$day flag=$flag"

Sau khi chúng tôi chạy lệnh sau:

sh test_args.sh  -year 2017 -flag  -month 12 -day 22 

Đầu ra sẽ là:

year=2017 month=12 day=22 flag=true

5
Điều này có cùng cách tiếp cận như câu trả lời của Nô-ê , nhưng có ít kiểm tra / biện pháp bảo vệ an toàn hơn. Điều này cho phép chúng tôi viết các đối số tùy ý vào môi trường của tập lệnh và tôi khá chắc chắn rằng việc bạn sử dụng eval ở đây có thể cho phép tiêm lệnh.
Will Barnwell

2

Sử dụng mô-đun "đối số" từ mô-đun bash

Thí dụ:

#!/bin/bash
. import.sh log arguments

NAME="world"

parse_arguments "-n|--name)NAME;S" -- "$@" || {
  error "Cannot parse command line."
  exit 1
}

info "Hello, $NAME!"

2

Trộn các đối số dựa trên vị trí và cờ

--param = arg (bằng giới hạn)

Tự do trộn cờ giữa các đối số vị trí:

./script.sh dumbo 127.0.0.1 --environment=production -q -d
./script.sh dumbo --environment=production 127.0.0.1 --quiet -d

có thể được thực hiện với một cách tiếp cận khá súc tích:

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   param=${!pointer}
   if [[ $param != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      case $param in
         # paramter-flags with arguments
         -e=*|--environment=*) environment="${param#*=}";;
                  --another=*) another="${param#*=}";;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + 1)):$#} \
         || set -- ${@:((pointer + 1)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

--param arg (giới hạn không gian)

Nó thường rõ ràng hơn để không trộn lẫn --flag=value--flag valuephong cách.

./script.sh dumbo 127.0.0.1 --environment production -q -d

Đây là một chút xí ngầu để đọc, nhưng vẫn còn hiệu lực

./script.sh dumbo --environment production 127.0.0.1 --quiet -d

Nguồn

# process flags
pointer=1
while [[ $pointer -le $# ]]; do
   if [[ ${!pointer} != "-"* ]]; then ((pointer++)) # not a parameter flag so advance pointer
   else
      param=${!pointer}
      ((pointer_plus = pointer + 1))
      slice_len=1

      case $param in
         # paramter-flags with arguments
         -e|--environment) environment=${!pointer_plus}; ((slice_len++));;
                --another) another=${!pointer_plus}; ((slice_len++));;

         # binary flags
         -q|--quiet) quiet=true;;
                 -d) debug=true;;
      esac

      # splice out pointer frame from positional list
      [[ $pointer -gt 1 ]] \
         && set -- ${@:1:((pointer - 1))} ${@:((pointer + $slice_len)):$#} \
         || set -- ${@:((pointer + $slice_len)):$#};
   fi
done

# positional remain
node_name=$1
ip_address=$2

2

Dưới đây là một getopts đạt được phân tích cú pháp với mã tối thiểu và cho phép bạn xác định những gì bạn muốn trích xuất trong một trường hợp bằng cách sử dụng eval với chuỗi con.

Về cơ bản eval "local key='val'"

function myrsync() {

        local backup=("${@}") args=(); while [[ $# -gt 0 ]]; do k="$1";
                case "$k" in
                    ---sourceuser|---sourceurl|---targetuser|---targeturl|---file|---exclude|---include)
                        eval "local ${k:3}='${2}'"; shift; shift    # Past two arguments
                    ;;
                    *)  # Unknown option  
                        args+=("$1"); shift;                        # Past argument only
                    ;;                                              
                esac                                                
        done; set -- "${backup[@]}"                                 # Restore $@


        echo "${sourceurl}"
}

Khai báo các biến là người địa phương thay vì toàn cầu như hầu hết các câu trả lời ở đây.

Được gọi là:

myrsync ---sourceurl http://abc.def.g ---sourceuser myuser ... 

$ {K: 3} về cơ bản là một chuỗi con để loại bỏ chuỗi đầu tiên ---khỏi khóa.


1

Điều này cũng có thể hữu ích để biết, bạn có thể đặt một giá trị và nếu ai đó cung cấp đầu vào, ghi đè mặc định bằng giá trị đó ..

myscript.sh -f ./serverlist.txt hoặc chỉ ./myscript.sh (và nó mặc định)

    #!/bin/bash
    # --- set the value, if there is inputs, override the defaults.

    HOME_FOLDER="${HOME}/owned_id_checker"
    SERVER_FILE_LIST="${HOME_FOLDER}/server_list.txt"

    while [[ $# > 1 ]]
    do
    key="$1"
    shift

    case $key in
        -i|--inputlist)
        SERVER_FILE_LIST="$1"
        shift
        ;;
    esac
    done


    echo "SERVER LIST   = ${SERVER_FILE_LIST}"

1

Một giải pháp khác không có getopt [s], POSIX, kiểu Unix cũ

Tương tự như giải pháp Bruno Bronosky đăng ở đây là một mà không sử dụng getopt(s).

Tính năng khác biệt chính của giải pháp của tôi là nó cho phép có các tùy chọn được nối với nhau giống như tar -xzf foo.tar.gzlà bằng tar -x -z -f foo.tar.gz. Và cũng giống như trong tar,ps v.v ... dấu gạch nối hàng đầu là tùy chọn cho một khối các tùy chọn ngắn (nhưng điều này có thể thay đổi dễ dàng). Các tùy chọn dài cũng được hỗ trợ (nhưng khi một khối bắt đầu bằng một thì hai dấu gạch nối hàng đầu được yêu cầu).

Mã với các tùy chọn ví dụ

#!/bin/sh

echo
echo "POSIX-compliant getopt(s)-free old-style-supporting option parser from phk@[se.unix]"
echo

print_usage() {
  echo "Usage:

  $0 {a|b|c} [ARG...]

Options:

  --aaa-0-args
  -a
    Option without arguments.

  --bbb-1-args ARG
  -b ARG
    Option with one argument.

  --ccc-2-args ARG1 ARG2
  -c ARG1 ARG2
    Option with two arguments.

" >&2
}

if [ $# -le 0 ]; then
  print_usage
  exit 1
fi

opt=
while :; do

  if [ $# -le 0 ]; then

    # no parameters remaining -> end option parsing
    break

  elif [ ! "$opt" ]; then

    # we are at the beginning of a fresh block
    # remove optional leading hyphen and strip trailing whitespaces
    opt=$(echo "$1" | sed 's/^-\?\([a-zA-Z0-9\?-]*\)/\1/')

  fi

  # get the first character -> check whether long option
  first_chr=$(echo "$opt" | awk '{print substr($1, 1, 1)}')
  [ "$first_chr" = - ] && long_option=T || long_option=F

  # note to write the options here with a leading hyphen less
  # also do not forget to end short options with a star
  case $opt in

    -)

      # end of options
      shift
      break
      ;;

    a*|-aaa-0-args)

      echo "Option AAA activated!"
      ;;

    b*|-bbb-1-args)

      if [ "$2" ]; then
        echo "Option BBB with argument '$2' activated!"
        shift
      else
        echo "BBB parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    c*|-ccc-2-args)

      if [ "$2" ] && [ "$3" ]; then
        echo "Option CCC with arguments '$2' and '$3' activated!"
        shift 2
      else
        echo "CCC parameters incomplete!" >&2
        print_usage
        exit 1
      fi
      ;;

    h*|\?*|-help)

      print_usage
      exit 0
      ;;

    *)

      if [ "$long_option" = T ]; then
        opt=$(echo "$opt" | awk '{print substr($1, 2)}')
      else
        opt=$first_chr
      fi
      printf 'Error: Unknown option: "%s"\n' "$opt" >&2
      print_usage
      exit 1
      ;;

  esac

  if [ "$long_option" = T ]; then

    # if we had a long option then we are going to get a new block next
    shift
    opt=

  else

    # if we had a short option then just move to the next character
    opt=$(echo "$opt" | awk '{print substr($1, 2)}')

    # if block is now empty then shift to the next one
    [ "$opt" ] || shift

  fi

done

echo "Doing something..."

exit 0

Đối với việc sử dụng ví dụ xin vui lòng xem các ví dụ dưới đây.

Vị trí của các tùy chọn với các đối số

Đối với giá trị của nó, các tùy chọn với các đối số không phải là cuối cùng (chỉ cần các tùy chọn dài). Vì vậy, trong khi ví dụ trong tar(ít nhất là trong một số triển khai), các ftùy chọn cần phải tồn tại bởi vì tên tệp theo sau ( tar xzf bar.tar.gzhoạt động nhưng tar xfz bar.tar.gzkhông) đây không phải là trường hợp ở đây (xem các ví dụ sau).

Nhiều tùy chọn với các đối số

Như một phần thưởng khác, các tham số tùy chọn được tiêu thụ theo thứ tự của các tùy chọn bởi các tham số với các tùy chọn bắt buộc. Chỉ cần nhìn vào đầu ra của tập lệnh của tôi ở đây với dòng lệnh abc X Y Z(hoặc -abc X Y Z):

Option AAA activated!
Option BBB with argument 'X' activated!
Option CCC with arguments 'Y' and 'Z' activated!

Các tùy chọn dài cũng được kết hợp

Ngoài ra, bạn cũng có thể có các tùy chọn dài trong khối tùy chọn cho rằng chúng xuất hiện lần cuối trong khối. Vì vậy, các dòng lệnh sau đây đều tương đương (bao gồm thứ tự các tùy chọn và đối số của nó đang được xử lý):

  • -cba Z Y X
  • cba Z Y X
  • -cb-aaa-0-args Z Y X
  • -c-bbb-1-args Z Y X -a
  • --ccc-2-args Z Y -ba X
  • c Z Y b X a
  • -c Z Y -b X -a
  • --ccc-2-args Z Y --bbb-1-args X --aaa-0-args

Tất cả những điều này dẫn đến:

Option CCC with arguments 'Z' and 'Y' activated!
Option BBB with argument 'X' activated!
Option AAA activated!
Doing something...

Không có trong giải pháp này

Đối số tùy chọn

Các tùy chọn với các đối số tùy chọn nên có thể với một chút công việc, ví dụ bằng cách nhìn về phía trước liệu có một khối không có dấu gạch nối hay không; người dùng sau đó sẽ cần đặt dấu gạch nối trước mỗi khối theo sau một khối có tham số có tham số tùy chọn. Có lẽ điều này quá phức tạp để giao tiếp với người dùng nên tốt hơn chỉ cần yêu cầu một dấu gạch nối hàng đầu trong trường hợp này.

Mọi thứ thậm chí còn phức tạp hơn với nhiều tham số có thể. Tôi sẽ khuyên bạn không nên làm cho các tùy chọn cố gắng trở nên thông minh bằng cách xác định liệu đối số có thể phù hợp với nó hay không (ví dụ: với một tùy chọn chỉ lấy một số làm đối số tùy chọn) vì điều này có thể phá vỡ trong tương lai.

Cá nhân tôi ủng hộ các tùy chọn bổ sung thay vì các đối số tùy chọn.

Đối số tùy chọn được giới thiệu với dấu bằng

Cũng giống như với các đối số tùy chọn Tôi không phải là người hâm mộ của điều này (BTW, có một chủ đề để thảo luận về ưu / nhược điểm của các kiểu tham số khác nhau không?) Nhưng nếu bạn muốn điều này, bạn có thể tự thực hiện nó giống như được thực hiện tại http: // mywiki.wooledge.org/BashFAQ/035#Manual_loop với một --long-with-arg=?*tuyên bố trường hợp và sau đó tước dấu bằng (đây là trang web BTW nói rằng việc tạo tham số có thể thực hiện được với một số nỗ lực nhưng "để lại [nó] như một bài tập cho người đọc "Điều đó khiến tôi hiểu họ nhưng tôi bắt đầu lại từ đầu).

Ghi chú khác

POSIX-compliant, hoạt động ngay cả trên các thiết lập Busybox xưa tôi đã phải đối phó với (với ví dụ cut, headgetoptsmất tích).

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.