استخدام getopts لمعالجة خيارات سطر الأوامر الطويلة والقصيرة

StackOverflow https://stackoverflow.com/questions/402377

سؤال

أرغب في الحصول على نماذج طويلة وقصيرة من خيارات سطر الأوامر التي تم استدعاؤها باستخدام برنامج Shell النصي الخاص بي.

وأنا أعلم ذلك getopts يمكن استخدامها، ولكن كما هو الحال في لغة Perl، لم أتمكن من فعل الشيء نفسه مع Shell.

أي أفكار حول كيفية القيام بذلك، حتى أتمكن من استخدام خيارات مثل:

./shell.sh --copyfile abc.pl /tmp/
./shell.sh -c abc.pl /tmp/

في ما سبق، كلا الأمرين يعنيان نفس الشيء بالنسبة إلى الصدفة الخاصة بي، لكن باستخدام getopts, ، لم أتمكن من تنفيذ هذه؟

هل كانت مفيدة؟

المحلول

هناك ثلاثة تطبيقات يمكن أخذها في الاعتبار:

  • باش مدمج getopts.لا يدعم هذا أسماء الخيارات الطويلة ذات البادئة ذات الشرطة المزدوجة.وهو يدعم فقط خيارات الحرف الواحد.

  • تنفيذ BSD UNIX مستقل getopt الأمر (وهو ما يستخدمه MacOS).وهذا لا يدعم الخيارات الطويلة أيضًا.

  • تنفيذ جنو مستقل getopt.جنو getopt(3) (يستخدم بواسطة سطر الأوامر getopt(1) على Linux) يدعم تحليل الخيارات الطويلة.


تظهر بعض الإجابات الأخرى حلاً لاستخدام bash المدمج getopts لتقليد الخيارات الطويلة.يقوم هذا الحل في الواقع بإنشاء خيار قصير يكون حرفه "-".لذلك تحصل على "--" كعلم.ثم أي شيء يتبع ذلك يصبح OPTARG، ويمكنك اختبار OPTARG باستخدام ملف متداخل case.

هذا أمر ذكي، لكنه يأتي مع التحذيرات:

  • getopts لا يمكن فرض المواصفات الاختيارية.ولا يمكنه إرجاع الأخطاء إذا قدم المستخدم خيارًا غير صالح.يجب عليك التحقق من الأخطاء بنفسك أثناء تحليل OPTARG.
  • يتم استخدام OPTARG لاسم الخيار الطويل، مما يزيد من تعقيد الاستخدام عندما يكون للخيار الطويل نفسه وسيطة.وينتهي بك الأمر إلى الاضطرار إلى ترميز ذلك بنفسك كحالة إضافية.

لذلك، في حين أنه من الممكن كتابة المزيد من التعليمات البرمجية للتغلب على نقص الدعم للخيارات الطويلة، فإن هذا يتطلب الكثير من العمل ويبطل جزئيًا الغرض من استخدام محلل getopt لتبسيط التعليمات البرمجية الخاصة بك.

نصائح أخرى

getopt و getopts هم وحوش مختلفة، ويبدو أن الناس لديهم القليل من سوء الفهم لما يفعلونه. getopts هو أمر مضمن ل bash لمعالجة خيارات سطر الأوامر في حلقة وتعيين كل خيار وقيمة يتم العثور عليها بدوره إلى المتغيرات المضمنة، حتى تتمكن من معالجتها بشكل أكبر. getopt, ومع ذلك، فهو برنامج فائدة خارجي، وهو كذلك لا يقوم في الواقع بمعالجة خياراتك نيابةً عنك الطريقة التي على سبيل المثال.سحق getopts, ، بيرل Getopt الوحدة النمطية أو بايثون optparse/argparse وحدات تفعل ذلك.كل ذلك getopt ما يفعله هو تحديد الخيارات التي تم تمريرها - على سبيل المثال.قم بتحويلها إلى نموذج أكثر معيارية، بحيث يسهل على برنامج Shell النصي معالجتها.على سبيل المثال، تطبيق getopt يمكن تحويل ما يلي:

myscript -ab infile.txt -ooutfile.txt

في هذا:

myscript -a -b -o outfile.txt infile.txt

عليك أن تقوم بالمعالجة الفعلية بنفسك.ليس عليك أن تستخدم getopt على الإطلاق إذا قمت بإجراء قيود مختلفة على طريقة تحديد الخيارات:

  • ضع خيارًا واحدًا فقط لكل وسيطة؛
  • يتم وضع جميع الخيارات قبل أي معلمات موضعية (أيالحجج غير الخيارية)؛
  • للخيارات ذات القيم (على سبيل المثال -o أعلاه)، يجب أن تظهر القيمة كوسيطة منفصلة (بعد مسافة).

لماذا الاستخدام getopt بدلاً من getopts؟السبب الأساسي هو أن GNU فقط getopt يمنحك الدعم لخيارات سطر الأوامر ذات الأسماء الطويلة.1 (جنو getopt هو الإعداد الافتراضي في Linux.يأتي Mac OS X وFreeBSD مزودين ببرنامج أساسي وغير مفيد للغاية getopt, ولكن يمكن تثبيت نسخة جنو؛انظر أدناه.)

على سبيل المثال، إليك مثال لاستخدام GNU getopt, ، من نص خاص بي يسمى javawrap:

# NOTE: This requires GNU getopt.  On Mac OS X and FreeBSD, you have to install this
# separately; see below.
TEMP=`getopt -o vdm: --long verbose,debug,memory:,debugfile:,minheap:,maxheap: \
             -n 'javawrap' -- "$@"`

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

# Note the quotes around `$TEMP': they are essential!
eval set -- "$TEMP"

VERBOSE=false
DEBUG=false
MEMORY=
DEBUGFILE=
JAVA_MISC_OPT=
while true; do
  case "$1" in
    -v | --verbose ) VERBOSE=true; shift ;;
    -d | --debug ) DEBUG=true; shift ;;
    -m | --memory ) MEMORY="$2"; shift 2 ;;
    --debugfile ) DEBUGFILE="$2"; shift 2 ;;
    --minheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MinHeapFreeRatio=$2"; shift 2 ;;
    --maxheap )
      JAVA_MISC_OPT="$JAVA_MISC_OPT -XX:MaxHeapFreeRatio=$2"; shift 2 ;;
    -- ) shift; break ;;
    * ) break ;;
  esac
done

يتيح لك هذا تحديد خيارات مثل --verbose -dm4096 --minh=20 --maxhe 40 --debugfi="/Users/John Johnson/debug.txt" أو مشابه.أثر الدعوة إلى getopt هو تحديد الخيارات ل --verbose -d -m 4096 --minheap 20 --maxheap 40 --debugfile "/Users/John Johnson/debug.txt" حتى تتمكن من معالجتها بسهولة أكبر.الاقتباس حولها "$1" و "$2" مهم لأنه يضمن التعامل مع الوسائط التي تحتوي على مسافات بشكل صحيح.

إذا قمت بحذف الأسطر التسعة الأولى (كل شيء من خلال eval set سطر)، سوف رمز ما زال يعمل!ومع ذلك، سيكون الكود الخاص بك أكثر انتقائية فيما يتعلق بأنواع الخيارات التي يقبلها:وعلى وجه الخصوص، سيتعين عليك تحديد جميع الخيارات في النموذج "المتعارف عليه" الموضح أعلاه.بإستخدام getopt, ومع ذلك، يمكنك تجميع خيارات الحرف المفرد، واستخدام أشكال أقصر وغير غامضة من الخيارات الطويلة، واستخدام إما --file foo.txt أو --file=foo.txt النمط، استخدم إما -m 4096 أو -m4096 النمط، ومزج الخيارات وغير الخيارات بأي ترتيب، وما إلى ذلك. getopt يقوم أيضًا بإخراج رسالة خطأ في حالة العثور على خيارات غامضة أو غير معروفة.

ملحوظة:هناك في الواقع اثنان مختلفة تماما إصدارات getopt, ، أساسي getopt و جنو getopt, ، مع ميزات مختلفة واصطلاحات اتصال مختلفة.2 أساسي getopt مكسور تمامًا:فهو لا لا يتعامل مع الخيارات الطويلة فحسب، بل لا يمكنه أيضًا التعامل مع المسافات المضمنة داخل الوسائط أو الوسائط الفارغة، بينما getopts يفعل هذا الحق.لن يعمل الكود أعلاه بشكل أساسي getopt.جنو getopt يتم تثبيته بشكل افتراضي على Linux، ولكن في نظامي التشغيل Mac OS X وFreeBSD يجب تثبيته بشكل منفصل.في نظام التشغيل Mac OS X، قم بتثبيت MacPorts (http://www.macports.org) ثم افعل sudo port install getopt لتثبيت جنو getopt (عادة في /opt/local/bin)، والتأكد من ذلك /opt/local/bin في مسار الصدفة الخاص بك أمامك /usr/bin.على FreeBSD، قم بتثبيت misc/getopt.

دليل سريع لتعديل رمز المثال لبرنامجك الخاص:من بين الأسطر القليلة الأولى، كل شيء "نموذجي" يجب أن يظل كما هو، باستثناء السطر الذي يستدعي getopt.يجب عليك تغيير اسم البرنامج بعد ذلك -n, ، حدد خيارات قصيرة بعد ذلك -o, ، وخيارات طويلة بعد ذلك --long.ضع نقطتين بعد الخيارات التي تأخذ قيمة.

أخيرًا، إذا رأيت الكود الذي يحتوي على set بدلاً من eval set, لقد تم كتابته لـ BSD getopt.يجب عليك تغييره لاستخدام eval set النمط، الذي يعمل بشكل جيد مع كلا الإصدارين من getopt, بينما السهل set لا يعمل بشكل صحيح مع جنو getopt.

1في الحقيقة، getopts في ksh93 يدعم الخيارات ذات الأسماء الطويلة، ولكن لا يتم استخدام هذه الصدفة كثيرًا bash.في zsh, ، يستخدم zparseopts للحصول على هذه الوظيفة.

2من الناحية الفنية، "جنو getopt" هي تسمية خاطئة؛تمت كتابة هذا الإصدار بالفعل لنظام Linux بدلاً من مشروع GNU.ومع ذلك، فهو يتبع جميع اتفاقيات جنو، ومصطلح "GNU getopt" يستخدم بشكل شائع (على سبيل المثال.على فري بي إس دي).

وظيفة getopts باش مدمج يمكن استخدامها لتحليل خيارات طويلة عن طريق وضع طابع شرطة متبوعا بنقطتين في optspec:

#!/usr/bin/env bash 
optspec=":hv-:"
while getopts "$optspec" optchar; do
    case "${optchar}" in
        -)
            case "${OPTARG}" in
                loglevel)
                    val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
                    echo "Parsing option: '--${OPTARG}', value: '${val}'" >&2;
                    ;;
                loglevel=*)
                    val=${OPTARG#*=}
                    opt=${OPTARG%=$val}
                    echo "Parsing option: '--${opt}', value: '${val}'" >&2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h)
            echo "usage: $0 [-v] [--loglevel[=]<value>]" >&2
            exit 2
            ;;
        v)
            echo "Parsing option: '-${optchar}'" >&2
            ;;
        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done

وبعد نسخ إلى اسم الملف القابل للتنفيذ = getopts_test.sh في دليل العمل الحالي ، يمكن للمرء أن تنتج الانتاج مثل

$ ./getopts_test.sh
$ ./getopts_test.sh -f
Non-option argument: '-f'
$ ./getopts_test.sh -h
usage: code/getopts_test.sh [-v] [--loglevel[=]<value>]
$ ./getopts_test.sh --help
$ ./getopts_test.sh -v
Parsing option: '-v'
$ ./getopts_test.sh --very-bad
$ ./getopts_test.sh --loglevel
Parsing option: '--loglevel', value: ''
$ ./getopts_test.sh --loglevel 11
Parsing option: '--loglevel', value: '11'
$ ./getopts_test.sh --loglevel=11
Parsing option: '--loglevel', value: '11'

وgetopts ومن الواضح أن لا يؤدي OPTERR فحص ولا خيار حجة تحليل للخيارات طويلة. يظهر جزء السيناريو أعلاه كيف يمكن القيام بذلك يدويا. يعمل المبدأ الأساسي أيضا في وعاء ديبيان ألمكويست ( "اندفاعة"). لاحظ حالة خاصة:

getopts -- "-:"  ## without the option terminator "-- " bash complains about "-:"
getopts "-:"     ## this works in the Debian Almquist shell ("dash")

ملاحظة أنه، كما GreyCat من أكثر من نقطة في http://mywiki.wooledge.org/BashFAQ بها، هذه الخدعة تستغل سلوك غير قياسي من قذيفة الذي يسمح خيار الحجة (أي اسم الملف في "اسم الملف -f") إلى أن تكون متصلا إلى الخيار (كما في "-ffilename"). و مستوى POSIX يقول يجب أن يكون هناك مسافة بينهما، والتي في حالة "- longoption". من شأنه أن ينهي خيار تحليل وتحويل جميع longoptions إلى حجج غير خيار

والمدمج في قيادة getopts لا يزال، AFAIK، تقتصر على خيارات حرف واحد فقط.

وهناك (أو تستخدم ليكون) وgetopt برنامج خارجي من شأنه أن يعيد تنظيم مجموعة من الخيارات من النوع الذي كان من الأسهل تحليل. هل يمكن أن تتكيف مع هذا التصميم للتعامل مع خيارات طويلة جدا. استخدام مثال:

aflag=no
bflag=no
flist=""
set -- $(getopt abf: "$@")
while [ $# -gt 0 ]
do
    case "$1" in
    (-a) aflag=yes;;
    (-b) bflag=yes;;
    (-f) flist="$flist $2"; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*)  break;;
    esac
    shift
done

# Process remaining non-option arguments
...

هل يمكن استخدام نظام مماثل مع أمر getoptlong.

لاحظ أن نقطة الضعف الأساسية مع برنامج getopt الخارجي هو صعوبة التعامل مع الحجج مع المساحات في نفوسهم، والحفاظ على تلك المساحات بدقة. هذا هو السبب في أن getopts المدمج في متفوقة، وإن كانت محدودة بسبب حقيقة أنه يتعامل مع خيارات فقط حرف واحد.

وفيما يلي مثال يستخدم في الواقع getopt مع خيارات طويلة:

aflag=no
bflag=no
cargument=none

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc: -l along,blong,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag="yes" ;;
    -b|--blong) bflag="yes" ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

يمكن تحليل الخيارات الطويلة بالمعيار getopts مدمج كـ "وسائط" لـ - "خيار"

هذا هو غلاف POSIX الأصلي والمحمول - لا حاجة إلى برامج خارجية أو عمليات تصادم.

يطبق هذا الدليل الخيارات الطويلة كوسائط لـ - الخيار، لذلك --alpha يرى من قبل getopts مثل - مع الحجة alpha و --bravo=foo يعتبر - مع الحجة bravo=foo.يمكن حصاد الحجة الحقيقية باستبدال بسيط: ${OPTARG#*=}.

في هذا المثال، -b (وشكلها الطويل، --bravo) لديه خيار إلزامي (لاحظ إعادة البناء اليدوي لفرض ذلك على النموذج الطويل).تأتي الخيارات غير المنطقية للوسيطات الطويلة بعد علامات المساواة، على سبيل المثال. --bravo=foo (سيكون من الصعب تنفيذ محددات المسافة للخيارات الطويلة).

لأن هذا يستخدم getopts, ، هذا الحل يدعم الاستخدام مثل cmd -ac --bravo=foo -d FILE (الذي يحتوي على خيارات مدمجة -a و -c ويتداخل مع الخيارات الطويلة مع الخيارات القياسية) بينما معظم الإجابات الأخرى هنا إما تكافح أو تفشل في القيام بذلك.

while getopts ab:c-: arg; do
  case $arg in
    a )  ARG_A=true ;;
    b )  ARG_B="$OPTARG" ;;
    c )  ARG_C=true ;;
    - )  LONG_OPTARG="${OPTARG#*=}"
         case $OPTARG in
           alpha    )  ARG_A=true ;;
           bravo=?* )  ARG_B="$LONG_OPTARG" ;;
           bravo*   )  echo "No arg for --$OPTARG option" >&2; exit 2 ;;
           charlie  )  ARG_C=true ;;
           alpha* | charlie* )
                       echo "No arg allowed for --$OPTARG option" >&2; exit 2 ;;
           '' )        break ;; # "--" terminates argument processing
           * )         echo "Illegal option --$OPTARG" >&2; exit 2 ;;
         esac ;;
    \? ) exit 2 ;;  # getopts already reported the illegal option
  esac
done
shift $((OPTIND-1)) # remove parsed options and args from $@ list

عندما تكون الوسيطة شرطة (-)، فهو يحتوي على مكونين آخرين:اسم العلم و(اختياريًا) وسيطته.أقوم بتحديد هذه الطريقة القياسية لأي أمر، مع علامة التساوي الأولى (=). $LONG_OPTARG وبالتالي فهو مجرد محتوى $OPTARG بدون اسم العلم أو علامة يساوي.

الداخلية case ينفذ الخيارات الطويلة يدويًا، لذا فهو يحتاج إلى بعض التدبير المنزلي:

  • bravo=? اعواد الكبريت --bravo=foo لكن لا --bravo= (ملحوظة: case يتوقف بعد المباراة الأولى)
  • bravo* يتبع، مع ملاحظة الوسيطة المطلوبة المفقودة في --bravo و --bravo=
  • alpha* | charlie* يلتقط الحجج المقدمة للخيارات التي لا تدعمها
  • '' موجود لدعم الخيارات غير التي تبدأ بشرطات
  • * يلتقط جميع الخيارات الطويلة الأخرى ويعيد إنشاء الخطأ الذي ألقاه getopts لخيار غير صالح

لا تحتاج بالضرورة إلى كل عناصر التدبير المنزلي هذه.على سبيل المثال، ربما تريد --bravo أن يكون لديك خياري حجة (التي -b لا يمكن دعمه بسبب وجود قيود في getopts).مجرد إزالة =? وحالة الفشل ذات الصلة ثم اتصل ${ARG_B:=$DEFAULT_ARG_B} في المرة الأولى التي تستخدمها $ARG_B.


لقبول الخيارات الطويلة ذات الوسائط المفصولة بمسافات، ستحتاج إلى (آمن بشكل لائق) eval:

           bravo=?* )  ARG_B="$LONG_OPTARG" ;;
           bravo )     eval "ARG_B=\"\$$OPTIND\""
                       if [ -z "$ARG_B" ]; then
                         echo "No arg for --$OPTARG option" >&2; exit 2
                       fi
                       OPTIND=$((OPTIND+1)) ;;
           bravo*   )  echo "No arg for --$OPTARG option" >&2; exit 2 ;;

تمت إضافة هذا الشرط الإضافي مباشرة بعد = إصدار المهمة، يدير eval لتفسير قيمة المعلمة التالية للخيار الذي يتم تفسيره في ذلك الوقت.يمكن لـ Bash القيام بذلك بشكل أكثر نظافة باستخدام التوسع غير المباشر, ARG_B="${!OPTIND}" ويمكن لـ Zsh القيام بذلك باستخدام ملف توسيع المعلمة P علَم, ARGB="${(P)OPTIND}", ، لكن ال eval مطلوب أن يكون POSIX محمولًا بالكامل. $OPTIND هو رقم يشير إلى وسيطة Shell التالية، على سبيل المثال $2 (معنى $1 يكون --bravo).ال eval ومن ثم سيتم تفسيرها ARG_B="$2" ولأن هذا موجود بين علامتي اقتباس، فهو آمن من إساءة الاستخدام (لم أتمكن من العثور على طريقة لخداعه للقيام بشيء غير مناسب).

إن ضمان قيمة غير فارغة أمر غير تافه ويتطلب التحقق منه فعليًا، لذلك لدينا شرطية ونولد خطأ فادحًا في هذه الحالة.إذا سمحت له أن يكون فارغًا، فستحتاج إلى جعل الزيادة مشروطة $OPTIND وإلا ستدخل في حلقة لا نهائية. [ $# -gt $OPTIND ] && OPTIND=$((OPTIND+1)) ينبغي أن تفعل.

الجزء الأخير من هذا الشرط بالإضافة إلى الزيادات $OPTIND لاستيعاب وسيطة الخيار بشكل صحيح والانتقال إلى الخيار التالي.

نلقي نظرة على shFlags وهي مكتبة شل محمولة (بمعنى:sh، bash، Dash، ksh، zsh على Linux، Solaris، وما إلى ذلك).

فهو يجعل إضافة علامات جديدة أمرًا بسيطًا مثل إضافة سطر واحد إلى البرنامج النصي الخاص بك، ويوفر وظيفة استخدام يتم إنشاؤها تلقائيًا.

هنا بسيط Hello, world! استخدام shFlag:

#!/bin/sh

# source shflags from current directory
. ./shflags

# define a 'name' command-line string flag
DEFINE_string 'name' 'world' 'name to say hello to' 'n'

# parse the command-line
FLAGS "$@" || exit 1
eval set -- "${FLAGS_ARGV}"

# say hello
echo "Hello, ${FLAGS_name}!"

بالنسبة لأنظمة التشغيل التي تحتوي على getopt المحسّن الذي يدعم الخيارات الطويلة (على سبيل المثال.لينكس)، يمكنك القيام بما يلي:

$ ./hello_world.sh --name Kate
Hello, Kate!

بالنسبة للباقي، يجب عليك استخدام الخيار القصير:

$ ./hello_world.sh -n Kate
Hello, Kate!

تعد إضافة علامة جديدة أمرًا بسيطًا مثل إضافة علامة جديدة DEFINE_ call.

استخدام getopts مع الخيارات والحجج قصيرة/طويلة


يعمل مع جميع المجموعات، على سبيل المثال:

  • foobar -f --bar
  • foobar --foo -b
  • foobar -bf --bar --foobar
  • foobar -fbFBAshorty --bar -FB --arguments=longhorn
  • foobar -fA "نص قصير" -B --arguments="نص قرون طويلة"
  • bash foobar -F --barfoo
  • sh foobar -B --foobar - ...
  • باش ./foobar -F --bar

بعض التصريحات لهذا المثال

Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

كيف ستبدو وظيفة الاستخدام

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
  $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                  -f   --foo            Set foo to yes    ($bart)
                  -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

getops مع أعلام طويلة/قصيرة بالإضافة إلى وسيطات طويلة

while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
             --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

انتاج |

##################################################################
echo "----------------------------------------------------------"
echo "RESULT short-foo      : $sfoo                                    long-foo      : $lfoo"
echo "RESULT short-bar      : $sbar                                    long-bar      : $lbar"
echo "RESULT short-foobar   : $sfoobar                                 long-foobar   : $lfoobar"
echo "RESULT short-barfoo   : $sbarfoo                                 long-barfoo   : $lbarfoo"
echo "RESULT short-arguments: $sarguments  with Argument = \"$sARG\"   long-arguments: $larguments and $lARG"

الجمع بين ما سبق في نص متماسك

#!/bin/bash
# foobar: getopts with short and long options AND arguments

function _cleanup ()
{
  unset -f _usage _cleanup ; return 0
}

## Clear out nested functions on exit
trap _cleanup INT EXIT RETURN

###### some declarations for this example ######
Options=$@
Optnum=$#
sfoo='no '
sbar='no '
sfoobar='no '
sbarfoo='no '
sarguments='no '
sARG=empty
lfoo='no '
lbar='no '
lfoobar='no '
lbarfoo='no '
larguments='no '
lARG=empty

function _usage() 
{
  ###### U S A G E : Help and ERROR ######
  cat <<EOF
   foobar $Options
   $*
          Usage: foobar <[options]>
          Options:
                  -b   --bar            Set bar to yes    ($foo)
                    -f   --foo            Set foo to yes    ($bart)
                      -h   --help           Show this message
                  -A   --arguments=...  Set arguments to yes ($arguments) AND get ARGUMENT ($ARG)
                  -B   --barfoo         Set barfoo to yes ($barfoo)
                  -F   --foobar         Set foobar to yes ($foobar)
EOF
}

[ $# = 0 ] && _usage "  >>>>>>>> no options given "

##################################################################    
#######  "getopts" with: short options  AND  long options  #######
#######            AND  short/long arguments               #######
while getopts ':bfh-A:BF' OPTION ; do
  case "$OPTION" in
    b  ) sbar=yes                       ;;
    f  ) sfoo=yes                       ;;
    h  ) _usage                         ;;   
    A  ) sarguments=yes;sARG="$OPTARG"  ;;
    B  ) sbarfoo=yes                    ;;
    F  ) sfoobar=yes                    ;;
    -  ) [ $OPTIND -ge 1 ] && optind=$(expr $OPTIND - 1 ) || optind=$OPTIND
         eval OPTION="\$$optind"
         OPTARG=$(echo $OPTION | cut -d'=' -f2)
         OPTION=$(echo $OPTION | cut -d'=' -f1)
         case $OPTION in
             --foo       ) lfoo=yes                       ;;
             --bar       ) lbar=yes                       ;;
             --foobar    ) lfoobar=yes                    ;;
             --barfoo    ) lbarfoo=yes                    ;;
             --help      ) _usage                         ;;
               --arguments ) larguments=yes;lARG="$OPTARG"  ;; 
             * )  _usage " Long: >>>>>>>> invalid options (long) " ;;
         esac
       OPTIND=1
       shift
      ;;
    ? )  _usage "Short: >>>>>>>> invalid options (short) "  ;;
  esac
done

وهناك طريقة أخرى ...

# translate long options to short
for arg
do
    delim=""
    case "$arg" in
       --help) args="${args}-h ";;
       --verbose) args="${args}-v ";;
       --config) args="${args}-c ";;
       # pass through anything else
       *) [[ "${arg:0:1}" == "-" ]] || delim="\""
           args="${args}${delim}${arg}${delim} ";;
    esac
done
# reset the translated args
eval set -- $args
# now we can process with getopt
while getopts ":hvc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        c)  source $OPTARG ;;
        \?) usage ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage
        ;;
    esac
done

والنوع الأول من حلها بهذه الطريقة:

# A string with command options
options=$@

# An array with all the arguments
arguments=($options)

# Loop index
index=0

for argument in $options
  do
    # Incrementing index
    index=`expr $index + 1`

    # The conditions
    case $argument in
      -a) echo "key $argument value ${arguments[index]}" ;;
      -abc) echo "key $argument value ${arguments[index]}" ;;
    esac
  done

exit;

وانا كونها غبية أو شيء؟ getopt وgetopts هي مربكة للغاية.

في حال كنت لا تريد الاعتماد getopt، يمكنك القيام بذلك:

while test $# -gt 0
do
  case $1 in

  # Normal option processing
    -h | --help)
      # usage and help
      ;;
    -v | --version)
      # version info
      ;;
  # ...

  # Special cases
    --)
      break
      ;;
    --*)
      # error unknown (long) option $1
      ;;
    -?)
      # error unknown (short) option $1
      ;;

  # FUN STUFF HERE:
  # Split apart combined short options
    -*)
      split=$1
      shift
      set -- $(echo "$split" | cut -c 2- | sed 's/./-& /g') "$@"
      continue
      ;;

  # Done with options
    *)
      break
      ;;
  esac

  # for testing purposes:
  echo "$1"

  shift
done

وبطبيعة الحال، ثم لا يمكنك استخدام خيارات نمط طويلة مع اندفاعة واحدة. وإذا كنت ترغب في إضافة إصدارات تقصير (مثل --verbos بدلا من --verbose)، فأنت بحاجة إلى إضافة هذه يدويا.

ولكن إذا كنت تبحث للحصول على وظيفة getopts جنبا إلى جنب مع خيارات طويلة، وهذا هو وسيلة بسيطة للقيام بذلك.

وأنا أيضا وضع هذا المقتطف في جوهر .

ووgetopts المدمج في لا تستطيع أن تفعل هذا. هناك خارجي <ط> getopt (1) برنامج التي يمكن أن تفعل هذا، ولكن تحصل فقط على لينكس من <ط> UTIL لينكس الحزمة. لأنه يأتي مع برنامج نصي سبيل المثال <ط> getopt-parse.bash .

وهناك أيضا getopts_long كما هو مكتوب وظيفة قذيفة.

#!/bin/bash
while getopts "abc:d:" flag
do
  case $flag in
    a) echo "[getopts:$OPTIND]==> -$flag";;
    b) echo "[getopts:$OPTIND]==> -$flag";;
    c) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
    d) echo "[getopts:$OPTIND]==> -$flag $OPTARG";;
  esac
done

shift $((OPTIND-1))
echo "[otheropts]==> $@"

exit

#!/bin/bash
until [ -z "$1" ]; do
  case $1 in
    "--dlong")
      shift
      if [ "${1:1:0}" != "-" ]
      then
        echo "==> dlong $1"
        shift
      fi;;
    *) echo "==> other $1"; shift;;
  esac
done
exit

في ksh93، getopts لا يعتمد أسماء طويلة ...

while getopts "f(file):s(server):" flag
do
    echo "$flag" $OPTIND $OPTARG
done

وأو هكذا قالوا ان الدروس لقد وجدت. انها محاولة ونرى.

واختراع بعد إصدار آخر من عجلة ...

وهذه الوظيفة هي (أمل) POSIX متوافق مع بورن عادي قذيفة بديلا عن GNU getopt. وهو يدعم خيارات قصيرة / طويلة والتي لا يمكن أن يقبل إلزامية / اختياري / بدون وسائط، والطريقة التي يتم تحديد خيارات مطابق تقريبا لGNU getopt، لذلك التحويل تافهة.

وبالطبع هذا لا يزال جزءا كبيرا من التعليمات البرمجية لتسقط في السيناريو، ولكن من حوالي نصف الخطوط وظيفة قذيفة getopt_long معروفة، وربما يكون من الأفضل في الحالات التي كنت ترغب فقط في استبدال الاستخدامات getopt GNU القائمة .

وهذا هو قانون جديد جدا، لذلك YMMV (وبالتأكيد واسمحوا لي أن أعرف إذا كان هذا هو في الواقع لا POSIX متوافق مع لأي سبب من الأسباب - كانت قابلية النية منذ البداية، ولكن ليس لدي اختبار POSIX مفيدة البيئة).

والمدونة واستخدام المثال التالي:

#!/bin/sh
# posix_getopt shell function
# Author: Phil S.
# Version: 1.0
# Created: 2016-07-05
# URL: http://stackoverflow.com/a/37087374/324105

# POSIX-compatible argument quoting and parameter save/restore
# http://www.etalabs.net/sh_tricks.html
# Usage:
# parameters=$(save "$@") # save the original parameters.
# eval "set -- ${parameters}" # restore the saved parameters.
save () {
    local param
    for param; do
        printf %s\\n "$param" \
            | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"
    done
    printf %s\\n " "
}

# Exit with status $1 after displaying error message $2.
exiterr () {
    printf %s\\n "$2" >&2
    exit $1
}

# POSIX-compatible command line option parsing.
# This function supports long options and optional arguments, and is
# a (largely-compatible) drop-in replacement for GNU getopt.
#
# Instead of:
# opts=$(getopt -o "$shortopts" -l "$longopts" -- "$@")
# eval set -- ${opts}
#
# We instead use:
# opts=$(posix_getopt "$shortopts" "$longopts" "$@")
# eval "set -- ${opts}"
posix_getopt () { # args: "$shortopts" "$longopts" "$@"
    local shortopts longopts \
          arg argtype getopt nonopt opt optchar optword suffix

    shortopts="$1"
    longopts="$2"
    shift 2

    getopt=
    nonopt=
    while [ $# -gt 0 ]; do
        opt=
        arg=
        argtype=
        case "$1" in
            # '--' means don't parse the remaining options
            ( -- ) {
                getopt="${getopt}$(save "$@")"
                shift $#
                break
            };;
            # process short option
            ( -[!-]* ) {         # -x[foo]
                suffix=${1#-?}   # foo
                opt=${1%$suffix} # -x
                optchar=${opt#-} # x
                case "${shortopts}" in
                    ( *${optchar}::* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *${optchar}:* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 "$1 requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 "$1 requires an argument";;
                            esac
                        fi
                    };;
                    ( *${optchar}* ) { # no argument
                        argtype=none
                        arg=
                        shift
                        # Handle multiple no-argument parameters combined as
                        # -xyz instead of -x -y -z. If we have just shifted
                        # parameter -xyz, we now replace it with -yz (which
                        # will be processed in the next iteration).
                        if [ -n "${suffix}" ]; then
                            eval "set -- $(save "-${suffix}")$(save "$@")"
                        fi
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # process long option
            ( --?* ) {            # --xarg[=foo]
                suffix=${1#*=}    # foo (unless there was no =)
                if [ "${suffix}" = "$1" ]; then
                    suffix=
                fi
                opt=${1%=$suffix} # --xarg
                optword=${opt#--} # xarg
                case ",${longopts}," in
                    ( *,${optword}::,* ) { # optional argument
                        argtype=optional
                        arg="${suffix}"
                        shift
                    };;
                    ( *,${optword}:,* ) { # required argument
                        argtype=required
                        if [ -n "${suffix}" ]; then
                            arg="${suffix}"
                            shift
                        else
                            case "$2" in
                                ( -* ) exiterr 1 \
                                       "--${optword} requires an argument";;
                                ( ?* ) arg="$2"; shift 2;;
                                (  * ) exiterr 1 \
                                       "--${optword} requires an argument";;
                            esac
                        fi
                    };;
                    ( *,${optword},* ) { # no argument
                        if [ -n "${suffix}" ]; then
                            exiterr 1 "--${optword} does not take an argument"
                        fi
                        argtype=none
                        arg=
                        shift
                    };;
                    ( * ) exiterr 1 "Unknown option $1";;
                esac
            };;
            # any other parameters starting with -
            ( -* ) exiterr 1 "Unknown option $1";;
            # remember non-option parameters
            ( * ) nonopt="${nonopt}$(save "$1")"; shift;;
        esac

        if [ -n "${opt}" ]; then
            getopt="${getopt}$(save "$opt")"
            case "${argtype}" in
                ( optional|required ) {
                    getopt="${getopt}$(save "$arg")"
                };;
            esac
        fi
    done

    # Generate function output, suitable for:
    # eval "set -- $(posix_getopt ...)"
    printf %s "${getopt}"
    if [ -n "${nonopt}" ]; then
        printf %s "$(save "--")${nonopt}"
    fi
}

والاستخدام مثال:

# Process command line options
shortopts="hvd:c::s::L:D"
longopts="help,version,directory:,client::,server::,load:,delete"
#opts=$(getopt -o "$shortopts" -l "$longopts" -n "$(basename $0)" -- "$@")
opts=$(posix_getopt "$shortopts" "$longopts" "$@")
if [ $? -eq 0 ]; then
    #eval set -- ${opts}
    eval "set -- ${opts}"
    while [ $# -gt 0 ]; do
        case "$1" in
            ( --                ) shift; break;;
            ( -h|--help         ) help=1; shift; break;;
            ( -v|--version      ) version_help=1; shift; break;;
            ( -d|--directory    ) dir=$2; shift 2;;
            ( -c|--client       ) useclient=1; client=$2; shift 2;;
            ( -s|--server       ) startserver=1; server_name=$2; shift 2;;
            ( -L|--load         ) load=$2; shift 2;;
            ( -D|--delete       ) delete=1; shift;;
        esac
    done
else
    shorthelp=1 # getopt returned (and reported) an error.
fi

أنا أكتب فقط نصوص برمجية شل بين الحين والآخر وأتوقف عن الممارسة، لذا فإن أي تعليقات موضع تقدير.

باستخدام الإستراتيجية التي اقترحها @Arvid Requate، لاحظنا بعض أخطاء المستخدم.المستخدم الذي ينسى تضمين قيمة، سيتم عن طريق الخطأ معاملة اسم الخيار التالي كقيمة:

./getopts_test.sh --loglevel= --toc=TRUE

سيؤدي إلى ظهور قيمة "loglevel" على أنها "--toc=TRUE".يمكن تجنب هذا.

لقد قمت بتكييف بعض الأفكار حول التحقق من خطأ المستخدم لـ CLI من http://mwiki.wooledge.org/BashFAQ/035 مناقشة التحليل اليدويلقد قمت بإدراج التحقق من الأخطاء في التعامل مع الوسيطتين "-" و"--".

ثم بدأت العبث ببناء الجملة، لذا فإن أي أخطاء هنا هي خطأي تمامًا، وليس خطأ المؤلفين الأصليين.

يساعد أسلوبي المستخدمين الذين يفضلون الدخول في صفقة شراء مع أو بدون علامة المساواة.وهذا يعني أنه يجب أن يكون له نفس الاستجابة لـ "--loglevel 9" كـ "--loglevel=9".في طريقة --/space، ليس من الممكن معرفة ما إذا كان المستخدم قد نسي إحدى الوسيطات على وجه اليقين، لذلك هناك حاجة إلى بعض التخمين.

  1. إذا كان لدى المستخدم تنسيق علامة الطول/المساواة (--opt=)، فستؤدي المسافة بعد = إلى حدوث خطأ لأنه لم يتم توفير وسيطة.
  2. إذا كان لدى المستخدم وسيطات طويلة/مسافة (--opt)، فسيتسبب هذا البرنامج النصي في فشل إذا لم تتبع أي وسيطة (نهاية الأمر) أو إذا كانت الوسيطة تبدأ بشرطة)

في حالة البدء في هذا الأمر، هناك فرق مثير للاهتمام بين التنسيقين "--opt=value" و"--opt value".باستخدام علامة المساواة، يُنظر إلى وسيطة سطر الأوامر على أنها "opt=value" والعمل الذي يجب التعامل معه هو تحليل السلسلة، للفصل عند "=".على النقيض من ذلك، مع "--opt value"، فإن اسم الوسيطة هو "opt" ولدينا التحدي المتمثل في الحصول على القيمة التالية المتوفرة في سطر الأوامر.هذا هو المكان الذي استخدم فيه @Arvid Requate ${!OPTIND}، المرجع غير المباشر.ما زلت لا أفهم ذلك، حسنًا، على الإطلاق، ويبدو أن التعليقات في BashFAQ تحذر من هذا الأسلوب (http://mywiki.wooledge.org/BashFAQ/006).راجع للشغل، لا أعتقد أن تعليقات الناشر السابقة حول أهمية OPTIND=$(( $OPTIND + 1 )) صحيحة.أعني أنني لا أرى أي ضرر في حذفها.

في الإصدار الأحدث من هذا البرنامج النصي، تعني العلامة -v النسخة المطبوعة المطولة.

احفظه في ملف يسمى "cli-5.sh"، واجعله قابلاً للتنفيذ، وسيعمل أي من هذه الملفات أو يفشل بالطريقة المطلوبة

./cli-5.sh  -v --loglevel=44 --toc  TRUE
./cli-5.sh  -v --loglevel=44 --toc=TRUE
./cli-5.sh --loglevel 7
./cli-5.sh --loglevel=8
./cli-5.sh -l9

./cli-5.sh  --toc FALSE --loglevel=77
./cli-5.sh  --toc=FALSE --loglevel=77

./cli-5.sh   -l99 -t yyy
./cli-5.sh   -l 99 -t yyy

فيما يلي مثال لإخراج التحقق من الأخطاء على intpu الخاص بالمستخدم

$ ./cli-5.sh  --toc --loglevel=77
ERROR: toc value must not have dash at beginning
$ ./cli-5.sh  --toc= --loglevel=77
ERROR: value for toc undefined

يجب أن تفكر في تشغيل -v، لأنه يطبع الأجزاء الداخلية لـ OPTIND وOPTARG

#/usr/bin/env bash

## Paul Johnson
## 20171016
##

## Combines ideas from
## https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
## by @Arvid Requate, and http://mwiki.wooledge.org/BashFAQ/035

# What I don't understand yet: 
# In @Arvid REquate's answer, we have 
# val="${!OPTIND}"; OPTIND=$(( $OPTIND + 1 ))
# this works, but I don't understand it!


die() {
    printf '%s\n' "$1" >&2
    exit 1
}

printparse(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'Parse: %s%s%s\n' "$1" "$2" "$3" >&2;
    fi
}

showme(){
    if [ ${VERBOSE} -gt 0 ]; then
        printf 'VERBOSE: %s\n' "$1" >&2;
    fi
}


VERBOSE=0
loglevel=0
toc="TRUE"

optspec=":vhl:t:-:"
while getopts "$optspec" OPTCHAR; do

    showme "OPTARG:  ${OPTARG[*]}"
    showme "OPTIND:  ${OPTIND[*]}"
    case "${OPTCHAR}" in
        -)
            case "${OPTARG}" in
                loglevel) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) # CAUTION! no effect?
                    printparse "--${OPTARG}" "  " "${val}"
                    loglevel="${val}"
                    shift
                    ;;
                loglevel=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        printparse "--${opt}" "=" "${val}"
                        loglevel="${val}"
                        ## shift CAUTION don't shift this, fails othewise
                    else
                        die "ERROR: $opt value must be supplied"
                    fi
                    ;;
                toc) #argument has no equal sign
                    opt=${OPTARG}
                    val="${!OPTIND}"
                    ## check value. If negative, assume user forgot value
                    showme "OPTIND is {$OPTIND} {!OPTIND} has value \"${!OPTIND}\""
                    if [[ "$val" == -* ]]; then
                        die "ERROR: $opt value must not have dash at beginning"
                    fi
                    ## OPTIND=$(( $OPTIND + 1 )) #??
                    printparse "--${opt}" " " "${val}"
                    toc="${val}"
                    shift
                    ;;
                toc=*) #argument has equal sign
                    opt=${OPTARG%=*}
                    val=${OPTARG#*=}
                    if [ "${OPTARG#*=}" ]; then
                        toc=${val}
                        printparse "--$opt" " -> " "$toc"
                        ##shift ## NO! dont shift this
                    else
                        die "ERROR: value for $opt undefined"
                    fi
                    ;;

                help)
                    echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
                    exit 2
                    ;;
                *)
                    if [ "$OPTERR" = 1 ] && [ "${optspec:0:1}" != ":" ]; then
                        echo "Unknown option --${OPTARG}" >&2
                    fi
                    ;;
            esac;;
        h|-\?|--help)
            ## must rewrite this for all of the arguments
            echo "usage: $0 [-v] [--loglevel[=]<value>] [--toc[=]<TRUE,FALSE>]" >&2
            exit 2
            ;;
        l)
            loglevel=${OPTARG}
            printparse "-l" " "  "${loglevel}"
            ;;
        t)
            toc=${OPTARG}
            ;;
        v)
            VERBOSE=1
            ;;

        *)
            if [ "$OPTERR" != 1 ] || [ "${optspec:0:1}" = ":" ]; then
                echo "Non-option argument: '-${OPTARG}'" >&2
            fi
            ;;
    esac
done



echo "
After Parsing values
"
echo "loglevel  $loglevel" 
echo "toc  $toc"

وهنا يمكنك العثور على قليل من الطرق المختلفة للتحليل خيار معقد في باش: http://mywiki.wooledge.org/ComplexOptionParsing

وفعلت خلق واحدة بعد، وأعتقد أنها فكرة جيدة، لأنه الحد الأدنى كود والخيارات على حد سواء طويلة وقصيرة العمل. وثمة خيار طويلة يمكن أن يكون أيضا حجج متعددة مع هذا النهج.

#!/bin/bash
# Uses bash extensions.  Not portable as written.

declare -A longoptspec
longoptspec=( [loglevel]=1 ) #use associative array to declare how many arguments a long option expects, in this case we declare that loglevel expects/has one argument, long options that aren't listed i n this way will have zero arguments by default
optspec=":h-:"
while getopts "$optspec" opt; do
while true; do
    case "${opt}" in
        -) #OPTARG is name-of-long-option or name-of-long-option=value
            if [[ "${OPTARG}" =~ .*=.* ]] #with this --key=value format only one argument is possible
            then
                opt=${OPTARG/=*/}
                OPTARG=${OPTARG#*=}
                ((OPTIND--))    
            else #with this --key value1 value2 format multiple arguments are possible
                opt="$OPTARG"
                OPTARG=(${@:OPTIND:$((longoptspec[$opt]))})
            fi
            ((OPTIND+=longoptspec[$opt]))
            continue #now that opt/OPTARG are set we can process them as if getopts would've given us long options
            ;;
        loglevel)
          loglevel=$OPTARG
            ;;
        h|help)
            echo "usage: $0 [--loglevel[=]<value>]" >&2
            exit 2
            ;;
    esac
break; done
done

# End of file

ولقد تم العمل على هذا الموضوع لفترة طويلة ... وجعل مكتبتي الخاصة التي سوف تحتاج إلى مصدر بالخط الرئيسي. انظر libopt4shell و <لأ href = "http://liealgebra.free.fr/cd2mpc" يختلط = " نوفولو "> cd2mpc للحصول على مثال. نأمل أن يساعد!

وحلا تحسين:

# translate long options to short
# Note: This enable long options but disable "--?*" in $OPTARG, or disable long options after  "--" in option fields.
for ((i=1;$#;i++)) ; do
    case "$1" in
        --)
            # [ ${args[$((i-1))]} == ... ] || EndOpt=1 ;;& # DIRTY: we still can handle some execptions...
            EndOpt=1 ;;&
        --version) ((EndOpt)) && args[$i]="$1"  || args[$i]="-V";;
        # default case : short option use the first char of the long option:
        --?*) ((EndOpt)) && args[$i]="$1"  || args[$i]="-${1:2:1}";;
        # pass through anything else:
        *) args[$i]="$1" ;;
    esac
    shift
done
# reset the translated args
set -- "${args[@]}"

function usage {
echo "Usage: $0 [options] files" >&2
    exit $1
}

# now we can process with getopt
while getopts ":hvVc:" opt; do
    case $opt in
        h)  usage ;;
        v)  VERBOSE=true ;;
        V)  echo $Version ; exit ;;
        c)  source $OPTARG ;;
        \?) echo "unrecognized option: -$opt" ; usage -1 ;;
        :)
        echo "option -$OPTARG requires an argument"
        usage -1
        ;;
    esac
done

shift $((OPTIND-1))
[[ "$1" == "--" ]] && shift

وربما يكون أسهل لاستخدام KSH، لمجرد الجزء getopts، إذا كان في حاجة خيارات سطر الأوامر طويلة، لأنها يمكن أن تكون أسهل القيام به هناك.

# Working Getopts Long => KSH

#! /bin/ksh
# Getopts Long
USAGE="s(showconfig)"
USAGE+="c:(createdb)"
USAGE+="l:(createlistener)"
USAGE+="g:(generatescripts)"
USAGE+="r:(removedb)"
USAGE+="x:(removelistener)"
USAGE+="t:(createtemplate)"
USAGE+="h(help)"

while getopts "$USAGE" optchar ; do
    case $optchar in
    s)  echo "Displaying Configuration" ;;
        c)  echo "Creating Database $OPTARG" ;;
    l)  echo "Creating Listener LISTENER_$OPTARG" ;;
    g)  echo "Generating Scripts for Database $OPTARG" ;;
    r)  echo "Removing Database $OPTARG" ;;
    x)  echo "Removing Listener LISTENER_$OPTARG" ;;
    t)  echo "Creating Database Template" ;;
    h)  echo "Help" ;;
    esac
done

وأنا لم يكن لديك ما يكفي من ممثل وحتى الآن لم يعلق أو التصويت حل له، ولكن الشركات الصغيرة والمتوسطة الجواب عملت بشكل جيد للغاية ل أنا. وكانت القضية الوحيدة جريت إلى أن الحجج في نهاية المطاف ملفوفة في وعلامات الاقتباس المفردة (ما لدي شريط بها).

وأود أيضا أن أضيف بعض الأعراف سبيل المثال والنص HELP. أنا وشملت بلدي نسخة موسعة قليلا هنا:

#!/bin/bash

# getopt example
# from: https://stackoverflow.com/questions/402377/using-getopts-in-bash-shell-script-to-get-long-and-short-command-line-options
HELP_TEXT=\
"   USAGE:\n
    Accepts - and -- flags, can specify options that require a value, and can be in any order. A double-hyphen (--) will stop processing options.\n\n

    Accepts the following forms:\n\n

    getopt-example.sh -a -b -c value-for-c some-arg\n
    getopt-example.sh -c value-for-c -a -b some-arg\n
    getopt-example.sh -abc some-arg\n
    getopt-example.sh --along --blong --clong value-for-c -a -b -c some-arg\n
    getopt-example.sh some-arg --clong value-for-c\n
    getopt-example.sh
"

aflag=false
bflag=false
cargument=""

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o abc:h\? -l along,blong,help,clong: -- "$@")
then
    # something went wrong, getopt will put out an error message for us
    exit 1
fi

set -- $options

while [ $# -gt 0 ]
do
    case $1 in
    -a|--along) aflag=true ;;
    -b|--blong) bflag=true ;;
    # for options with required arguments, an additional shift is required
    -c|--clong) cargument="$2" ; shift;;
    -h|--help|-\?) echo -e $HELP_TEXT; exit;;
    (--) shift; break;;
    (-*) echo "$0: error - unrecognized option $1" 1>&2; exit 1;;
    (*) break;;
    esac
    shift
done

# to remove the single quotes around arguments, pipe the output into:
# | sed -e "s/^'\\|'$//g"  (just leading/trailing) or | sed -e "s/'//g"  (all)

echo aflag=${aflag}
echo bflag=${bflag}
echo cargument=${cargument}

while [ $# -gt 0 ]
do
    echo arg=$1
    shift

    if [[ $aflag == true ]]; then
        echo a is true
    fi

done

كنت أرغب في الحصول على شيء بدون تبعيات خارجية، مع دعم صارم لـ bash (-u)، وكنت بحاجة إليه للعمل حتى على إصدارات bash الأقدم.يعالج هذا أنواعًا مختلفة من المعلمات:

  • بولات قصيرة (-ح)
  • خيارات قصيرة (-i "image.jpg")
  • بولز طويلة (--مساعدة)
  • يساوي الخيارات (--file = "filename.ext")
  • خيارات المسافة (--ملف "filename.ext")
  • منطقيات متسلسلة (-hvm)

ما عليك سوى إدراج ما يلي في الجزء العلوي من البرنامج النصي الخاص بك:

# Check if a list of params contains a specific param
# usage: if _param_variant "h|?|help p|path f|file long-thing t|test-thing" "file" ; then ...
# the global variable $key is updated to the long notation (last entry in the pipe delineated list, if applicable)
_param_variant() {
  for param in $1 ; do
    local variants=${param//\|/ }
    for variant in $variants ; do
      if [[ "$variant" = "$2" ]] ; then
        # Update the key to match the long version
        local arr=(${param//\|/ })
        let last=${#arr[@]}-1
        key="${arr[$last]}"
        return 0
      fi
    done
  done
  return 1
}

# Get input parameters in short or long notation, with no dependencies beyond bash
# usage:
#     # First, set your defaults
#     param_help=false
#     param_path="."
#     param_file=false
#     param_image=false
#     param_image_lossy=true
#     # Define allowed parameters
#     allowed_params="h|?|help p|path f|file i|image image-lossy"
#     # Get parameters from the arguments provided
#     _get_params $*
#
# Parameters will be converted into safe variable names like:
#     param_help,
#     param_path,
#     param_file,
#     param_image,
#     param_image_lossy
#
# Parameters without a value like "-h" or "--help" will be treated as
# boolean, and will be set as param_help=true
#
# Parameters can accept values in the various typical ways:
#     -i "path/goes/here"
#     --image "path/goes/here"
#     --image="path/goes/here"
#     --image=path/goes/here
# These would all result in effectively the same thing:
#     param_image="path/goes/here"
#
# Concatinated short parameters (boolean) are also supported
#     -vhm is the same as -v -h -m
_get_params(){

  local param_pair
  local key
  local value
  local shift_count

  while : ; do
    # Ensure we have a valid param. Allows this to work even in -u mode.
    if [[ $# == 0 || -z $1 ]] ; then
      break
    fi

    # Split the argument if it contains "="
    param_pair=(${1//=/ })
    # Remove preceeding dashes
    key="${param_pair[0]#--}"

    # Check for concatinated boolean short parameters.
    local nodash="${key#-}"
    local breakout=false
    if [[ "$nodash" != "$key" && ${#nodash} -gt 1 ]]; then
      # Extrapolate multiple boolean keys in single dash notation. ie. "-vmh" should translate to: "-v -m -h"
      local short_param_count=${#nodash}
      let new_arg_count=$#+$short_param_count-1
      local new_args=""
      # $str_pos is the current position in the short param string $nodash
      for (( str_pos=0; str_pos<new_arg_count; str_pos++ )); do
        # The first character becomes the current key
        if [ $str_pos -eq 0 ] ; then
          key="${nodash:$str_pos:1}"
          breakout=true
        fi
        # $arg_pos is the current position in the constructed arguments list
        let arg_pos=$str_pos+1
        if [ $arg_pos -gt $short_param_count ] ; then
          # handle other arguments
          let orignal_arg_number=$arg_pos-$short_param_count+1
          local new_arg="${!orignal_arg_number}"
        else
          # break out our one argument into new ones
          local new_arg="-${nodash:$str_pos:1}"
        fi
        new_args="$new_args \"$new_arg\""
      done
      # remove the preceding space and set the new arguments
      eval set -- "${new_args# }"
    fi
    if ! $breakout ; then
      key="$nodash"
    fi

    # By default we expect to shift one argument at a time
    shift_count=1
    if [ "${#param_pair[@]}" -gt "1" ] ; then
      # This is a param with equals notation
      value="${param_pair[1]}"
    else
      # This is either a boolean param and there is no value,
      # or the value is the next command line argument
      # Assume the value is a boolean true, unless the next argument is found to be a value.
      value=true
      if [[ $# -gt 1 && -n "$2" ]]; then
        local nodash="${2#-}"
        if [ "$nodash" = "$2" ]; then
          # The next argument has NO preceding dash so it is a value
          value="$2"
          shift_count=2
        fi
      fi
    fi

    # Check that the param being passed is one of the allowed params
    if _param_variant "$allowed_params" "$key" ; then
      # --key-name will now become param_key_name
      eval param_${key//-/_}="$value"
    else
      printf 'WARNING: Unknown option (ignored): %s\n' "$1" >&2
    fi
    shift $shift_count
  done
}

واستخدامها مثل هذا:

# Assign defaults for parameters
param_help=false
param_path=$(pwd)
param_file=false
param_image=true
param_image_lossy=true
param_image_lossy_quality=85

# Define the params we will allow
allowed_params="h|?|help p|path f|file i|image image-lossy image-lossy-quality"

# Get the params from arguments provided
_get_params $*

والجواب المقبول يقوم بعمل لطيفة جدا من لافتا إلى جميع أوجه القصور في باش المدمج في getopts. الجواب ينتهي:

<اقتباس فقرة>   

وذلك في حين أنه من الممكن لكتابة رمز أكثر للتغلب على عدم وجود دعم لخيارات طويلة، وهذا هو الكثير من العمل والهزائم جزئيا الغرض من استخدام محلل getopt لتبسيط التعليمات البرمجية.

وعلى الرغم من أنني توافق من حيث المبدأ مع هذا البيان، أشعر أن عدد المرات التي نحن جميعا تنفذ هذه الميزة في مختلف البرامج النصية تبرر وضع قليلا من الجهد في خلق "موحد"، حل اختباره بشكل جيد.

وعلى هذا النحو، لقد "ترقية" باش بنيت في getopts من خلال تنفيذ <وأ href = "https://github.com/UmkaDK/getopts_long/blob/master/lib/getopts_long.bash" يختلط = "نوفولو noreferrer "> getopts_long في باش النقي، مع عدم الاعتماد على الخارج. استخدام وظيفة هو 100٪ متوافق مع getopts المضمنة.

وقبل بما في ذلك getopts_long (والتي يتم استضافتها على جيثب على https://github.com/UmkaDK/getopts_long) في السيناريو، فإن الجواب على السؤال الأصلي يمكن تنفيذها ببساطة على النحو التالي:

source "${PATH_TO}/getopts_long.bash"

while getopts_long ':c: copyfile:' OPTKEY; do
    case ${OPTKEY} in
        'c'|'copyfile')
            echo 'file supplied -- ${OPTARG}'
            ;;
        '?')
            echo "INVALID OPTION -- ${OPTARG}" >&2
            exit 1
            ;;
        ':')
            echo "MISSING ARGUMENT for option -- ${OPTARG}" >&2
            exit 1
            ;;
        *)
            echo "Misconfigured OPTSPEC or uncaught option -- ${OPTKEY}" >&2
            exit 1
            ;;
    esac
done

shift $(( OPTIND - 1 ))
[[ "${1}" == "--" ]] && shift

من أجل البقاء متوافقًا مع الأنظمة الأساسية، وتجنب الاعتماد على الملفات التنفيذية الخارجية، قمت بنقل بعض التعليمات البرمجية من لغة أخرى.

أجد أنه سهل الاستخدام للغاية، إليك مثال:

ArgParser::addArg "[h]elp"    false    "This list"
ArgParser::addArg "[q]uiet"   false    "Supress output"
ArgParser::addArg "[s]leep"   1        "Seconds to sleep"
ArgParser::addArg "v"         1        "Verbose mode"

ArgParser::parse "$@"

ArgParser::isset help && ArgParser::showArgs

ArgParser::isset "quiet" \
   && echo "Quiet!" \
   || echo "Noisy!"

local __sleep
ArgParser::tryAndGetArg sleep into __sleep \
   && echo "Sleep for $__sleep seconds" \
   || echo "No value passed for sleep"

# This way is often more convienient, but is a little slower
echo "Sleep set to: $( ArgParser::getArg sleep )"

إن BASH المطلوب أطول قليلاً مما يمكن أن يكون، لكنني أردت تجنب الاعتماد على المصفوفات الترابطية الخاصة بـ BASH 4.يمكنك أيضًا تنزيل هذا مباشرة من http://nt4.com/bash/argparser.inc.sh

#!/usr/bin/env bash

# Updates to this script may be found at
# http://nt4.com/bash/argparser.inc.sh

# Example of runtime usage:
# mnc.sh --nc -q Caprica.S0*mkv *.avi *.mp3 --more-options here --host centos8.host.com

# Example of use in script (see bottom)
# Just include this file in yours, or use
# source argparser.inc.sh

unset EXPLODED
declare -a EXPLODED
function explode 
{
    local c=$# 
    (( c < 2 )) && 
    {
        echo function "$0" is missing parameters 
        return 1
    }

    local delimiter="$1"
    local string="$2"
    local limit=${3-99}

    local tmp_delim=$'\x07'
    local delin=${string//$delimiter/$tmp_delim}
    local oldifs="$IFS"

    IFS="$tmp_delim"
    EXPLODED=($delin)
    IFS="$oldifs"
}


# See: http://fvue.nl/wiki/Bash:_Passing_variables_by_reference
# Usage: local "$1" && upvar $1 "value(s)"
upvar() {
    if unset -v "$1"; then           # Unset & validate varname
        if (( $# == 2 )); then
            eval $1=\"\$2\"          # Return single value
        else
            eval $1=\(\"\${@:2}\"\)  # Return array
        fi
    fi
}

function decho
{
    :
}

function ArgParser::check
{
    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        matched=0
        explode "|" "${__argparser__arglist[$i]}"
        if [ "${#1}" -eq 1 ]
        then
            if [ "${1}" == "${EXPLODED[0]}" ]
            then
                decho "Matched $1 with ${EXPLODED[0]}"
                matched=1

                break
            fi
        else
            if [ "${1}" == "${EXPLODED[1]}" ]
            then
                decho "Matched $1 with ${EXPLODED[1]}"
                matched=1

                break
            fi
        fi
    done
    (( matched == 0 )) && return 2
    # decho "Key $key has default argument of ${EXPLODED[3]}"
    if [ "${EXPLODED[3]}" == "false" ]
    then
        return 0
    else
        return 1
    fi
}

function ArgParser::set
{
    key=$3
    value="${1:-true}"
    declare -g __argpassed__$key="$value"
}

function ArgParser::parse
{

    unset __argparser__argv
    __argparser__argv=()
    # echo parsing: "$@"

    while [ -n "$1" ]
    do
        # echo "Processing $1"
        if [ "${1:0:2}" == '--' ]
        then
            key=${1:2}
            value=$2
        elif [ "${1:0:1}" == '-' ]
        then
            key=${1:1}               # Strip off leading -
            value=$2
        else
            decho "Not argument or option: '$1'" >& 2
            __argparser__argv+=( "$1" )
            shift
            continue
        fi
        # parameter=${tmp%%=*}     # Extract name.
        # value=${tmp##*=}         # Extract value.
        decho "Key: '$key', value: '$value'"
        # eval $parameter=$value
        ArgParser::check $key
        el=$?
        # echo "Check returned $el for $key"
        [ $el -eq  2 ] && decho "No match for option '$1'" >&2 # && __argparser__argv+=( "$1" )
        [ $el -eq  0 ] && decho "Matched option '${EXPLODED[2]}' with no arguments"        >&2 && ArgParser::set true "${EXPLODED[@]}"
        [ $el -eq  1 ] && decho "Matched option '${EXPLODED[2]}' with an argument of '$2'"   >&2 && ArgParser::set "$2" "${EXPLODED[@]}" && shift
        shift
    done
}

function ArgParser::isset
{
    declare -p "__argpassed__$1" > /dev/null 2>&1 && return 0
    return 1
}

function ArgParser::getArg
{
    # This one would be a bit silly, since we can only return non-integer arguments ineffeciently
    varname="__argpassed__$1"
    echo "${!varname}"
}

##
# usage: tryAndGetArg <argname> into <varname>
# returns: 0 on success, 1 on failure
function ArgParser::tryAndGetArg
{
    local __varname="__argpassed__$1"
    local __value="${!__varname}"
    test -z "$__value" && return 1
    local "$3" && upvar $3 "$__value"
    return 0
}

function ArgParser::__construct
{
    unset __argparser__arglist
    # declare -a __argparser__arglist
}

##
# @brief add command line argument
# @param 1 short and/or long, eg: [s]hort
# @param 2 default value
# @param 3 description
##
function ArgParser::addArg
{
    # check for short arg within long arg
    if [[ "$1" =~ \[(.)\] ]]
    then
        short=${BASH_REMATCH[1]}
        long=${1/\[$short\]/$short}
    else
        long=$1
    fi
    if [ "${#long}" -eq 1 ]
    then
        short=$long
        long=''
    fi
    decho short: "$short"
    decho long: "$long"
    __argparser__arglist+=("$short|$long|$1|$2|$3")
}

## 
# @brief show available command line arguments
##
function ArgParser::showArgs
{
    # declare -p | grep argparser
    printf "Usage: %s [OPTION...]\n\n" "$( basename "${BASH_SOURCE[0]}" )"
    printf "Defaults for the options are specified in brackets.\n\n";

    __args=${#__argparser__arglist[@]}
    for (( i=0; i<__args; i++ ))
    do
        local shortname=
        local fullname=
        local default=
        local description=
        local comma=

        explode "|" "${__argparser__arglist[$i]}"

        shortname="${EXPLODED[0]:+-${EXPLODED[0]}}" # String Substitution Guide: 
        fullname="${EXPLODED[1]:+--${EXPLODED[1]}}" # http://tldp.org/LDP/abs/html/parameter-substitution.html
        test -n "$shortname" \
            && test -n "$fullname" \
            && comma=","

        default="${EXPLODED[3]}"
        case $default in
            false )
                default=
                ;;
            "" )
                default=
                ;;
            * )
                default="[$default]"
        esac

        description="${EXPLODED[4]}"

        printf "  %2s%1s %-19s %s %s\n" "$shortname" "$comma" "$fullname" "$description" "$default"
    done
}

function ArgParser::test
{
    # Arguments with a default of 'false' do not take paramaters (note: default
    # values are not applied in this release)

    ArgParser::addArg "[h]elp"      false       "This list"
    ArgParser::addArg "[q]uiet" false       "Supress output"
    ArgParser::addArg "[s]leep" 1           "Seconds to sleep"
    ArgParser::addArg "v"           1           "Verbose mode"

    ArgParser::parse "$@"

    ArgParser::isset help && ArgParser::showArgs

    ArgParser::isset "quiet" \
        && echo "Quiet!" \
        || echo "Noisy!"

    local __sleep
    ArgParser::tryAndGetArg sleep into __sleep \
        && echo "Sleep for $__sleep seconds" \
        || echo "No value passed for sleep"

    # This way is often more convienient, but is a little slower
    echo "Sleep set to: $( ArgParser::getArg sleep )"

    echo "Remaining command line: ${__argparser__argv[@]}"

}

if [ "$( basename "$0" )" == "argparser.inc.sh" ]
then
    ArgParser::test "$@"
fi

إذا لها فريدة من نوعها، والمطابقة، الأحرف الأولى كما الخيارات قصيرة، لذلك على سبيل المثال جميع الخيارات الخاصة بك طويلة

./slamm --chaos 23 --plenty test -quiet

وهو نفسه ك

./slamm -c 23 -p test -q

ويمكنك استخدام هذا <م> قبل getopts لإعادة كتابة $ سائط:

# change long options to short options

for arg; do 
    [[ "${arg:0:1}" == "-" ]] && delim="" || delim="\""
    if [ "${arg:0:2}" == "--" ]; 
       then args="${args} -${arg:2:1}" 
       else args="${args} ${delim}${arg}${delim}"
    fi
done

# reset the incoming args
eval set -- $args

# proceed as usual
while getopts ":b:la:h" OPTION; do
    .....

وشكرا لmtvee للإلهام؛ -)

مدمج getopts فقط تحليل الخيارات القصيرة (باستثناء KSH93) ، ولكن لا يزال بإمكانك إضافة خطوط قليلة من البرمجة النصية لجعل GetOpts مقابض طويلة.

هنا جزء من الكود الموجود في http://www.uxora.com/unix/shell-script/22-handle-long-options-with-getopts

  #== set short options ==#
SCRIPT_OPTS=':fbF:B:-:h'
  #== set long options associated with short one ==#
typeset -A ARRAY_OPTS
ARRAY_OPTS=(
    [foo]=f
    [bar]=b
    [foobar]=F
    [barfoo]=B
    [help]=h
    [man]=h
)

  #== parse options ==#
while getopts ${SCRIPT_OPTS} OPTION ; do
    #== translate long options to short ==#
    if [[ "x$OPTION" == "x-" ]]; then
        LONG_OPTION=$OPTARG
        LONG_OPTARG=$(echo $LONG_OPTION | grep "=" | cut -d'=' -f2)
        LONG_OPTIND=-1
        [[ "x$LONG_OPTARG" = "x" ]] && LONG_OPTIND=$OPTIND || LONG_OPTION=$(echo $OPTARG | cut -d'=' -f1)
        [[ $LONG_OPTIND -ne -1 ]] && eval LONG_OPTARG="\$$LONG_OPTIND"
        OPTION=${ARRAY_OPTS[$LONG_OPTION]}
        [[ "x$OPTION" = "x" ]] &&  OPTION="?" OPTARG="-$LONG_OPTION"

        if [[ $( echo "${SCRIPT_OPTS}" | grep -c "${OPTION}:" ) -eq 1 ]]; then
            if [[ "x${LONG_OPTARG}" = "x" ]] || [[ "${LONG_OPTARG}" = -* ]]; then 
                OPTION=":" OPTARG="-$LONG_OPTION"
            else
                OPTARG="$LONG_OPTARG";
                if [[ $LONG_OPTIND -ne -1 ]]; then
                    [[ $OPTIND -le $Optnum ]] && OPTIND=$(( $OPTIND+1 ))
                    shift $OPTIND
                    OPTIND=1
                fi
            fi
        fi
    fi

    #== options follow by another option instead of argument ==#
    if [[ "x${OPTION}" != "x:" ]] && [[ "x${OPTION}" != "x?" ]] && [[ "${OPTARG}" = -* ]]; then 
        OPTARG="$OPTION" OPTION=":"
    fi

    #== manage options ==#
    case "$OPTION" in
        f  ) foo=1 bar=0                    ;;
        b  ) foo=0 bar=1                    ;;
        B  ) barfoo=${OPTARG}               ;;
        F  ) foobar=1 && foobar_name=${OPTARG} ;;
        h ) usagefull && exit 0 ;;
        : ) echo "${SCRIPT_NAME}: -$OPTARG: option requires an argument" >&2 && usage >&2 && exit 99 ;;
        ? ) echo "${SCRIPT_NAME}: -$OPTARG: unknown option" >&2 && usage >&2 && exit 99 ;;
    esac
done
shift $((${OPTIND} - 1))

هنا اختبار:

# Short options test
$ ./foobar_any_getopts.sh -bF "Hello world" -B 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello world
files=file1 file2

# Long and short options test
$ ./foobar_any_getopts.sh --bar -F Hello --barfoo 6 file1 file2
foo=0 bar=1
barfoo=6
foobar=1 foobar_name=Hello
files=file1 file2

خلاف ذلك في الآونة الأخيرة كورن شل كش93, getopts يمكنه تحليل الخيارات الطويلة بشكل طبيعي وحتى عرض صفحة الدليل على حدٍ سواء.(يرى http://www.uxora.com/unix/shell-script/20-getopts-with-man-page-and-long-options)

وث المدمج في OS X (BSD) getopt لا يدعم خيارات طويلة، ولكن النسخة GNU لا: brew install gnu-getopt. ثم، شيئا مشابها ل: cp /usr/local/Cellar/gnu-getopt/1.1.6/bin/getopt /usr/local/bin/gnu-getopt

EasyOptions يعالج الخيارات القصيرة والطويلة:

## Options:
##   --verbose, -v   Verbose mode
##   --logfile=NAME  Log filename

source easyoptions || exit

if test -n "${verbose}"; then
    echo "log file: ${logfile}"
    echo "arguments: ${arguments[@]}"
fi

وA DIY بسيطة للحصول على وسائط فقط اسمه طويل:

استخدم:

$ ./test-args.sh --a1 a1 --a2 "a 2" --a3 --a4= --a5=a5 --a6="a 6"
a1 = "a1"
a2 = "a 2"
a3 = "TRUE"
a4 = ""
a5 = "a5"
a6 = "a 6"
a7 = ""

وسيناريو:

#!/bin/bash

function main() {
    ARGS=`getArgs "$@"`

    a1=`echo "$ARGS" | getNamedArg a1`
    a2=`echo "$ARGS" | getNamedArg a2`
    a3=`echo "$ARGS" | getNamedArg a3`
    a4=`echo "$ARGS" | getNamedArg a4`
    a5=`echo "$ARGS" | getNamedArg a5`
    a6=`echo "$ARGS" | getNamedArg a6`
    a7=`echo "$ARGS" | getNamedArg a7`

    echo "a1 = \"$a1\""
    echo "a2 = \"$a2\""
    echo "a3 = \"$a3\""
    echo "a4 = \"$a4\""
    echo "a5 = \"$a5\""
    echo "a6 = \"$a6\""
    echo "a7 = \"$a7\""

    exit 0
}


function getArgs() {
    for arg in "$@"; do
        echo "$arg"
    done
}


function getNamedArg() {
    ARG_NAME=$1

    sed --regexp-extended --quiet --expression="
        s/^--$ARG_NAME=(.*)\$/\1/p  # Get arguments in format '--arg=value': [s]ubstitute '--arg=value' by 'value', and [p]rint
        /^--$ARG_NAME\$/ {          # Get arguments in format '--arg value' ou '--arg'
            n                       # - [n]ext, because in this format, if value exists, it will be the next argument
            /^--/! p                # - If next doesn't starts with '--', it is the value of the actual argument
            /^--/ {                 # - If next do starts with '--', it is the next argument and the actual argument is a boolean one
                # Then just repla[c]ed by TRUE
                c TRUE
            }
        }
    "
}


main "$@"

وإذا بكل بساطة هذه هي الطريقة التي تريد الاتصال النصي

myscript.sh --input1 "ABC" --input2 "PQR" --input2 "XYZ"

وبعد ذلك يمكنك اتباع هذه أبسط طريقة لتحقيق ذلك مع مساعدة من getopt و--longoptions

وحاول هذا، نأمل أن يكون هذا هو المفيد

# Read command line options
ARGUMENT_LIST=(
    "input1"
    "input2"
    "input3"
)



# read arguments
opts=$(getopt \
    --longoptions "$(printf "%s:," "${ARGUMENT_LIST[@]}")" \
    --name "$(basename "$0")" \
    --options "" \
    -- "$@"
)


echo $opts

eval set --$opts

while true; do
    case "$1" in
    --input1)  
        shift
        empId=$1
        ;;
    --input2)  
        shift
        fromDate=$1
        ;;
    --input3)  
        shift
        toDate=$1
        ;;
      --)
        shift
        break
        ;;
    esac
    shift
done
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top