ฉันจะเพิ่มวิธีช่วยเหลือให้กับเชลล์สคริปต์ได้อย่างไร


128

ฉันจะตรวจสอบได้อย่างไรว่า-hแอตทริบิวต์ถูกส่งไปยังเชลล์สคริปต์หรือไม่ ฉันต้องการแสดงข้อความช่วยเหลือเมื่อผู้ใช้โทรmyscript.sh -hมา


ตัวอย่างที่เกี่ยวข้อง: stackoverflow.com/questions/14008125/…
Anton Tarasenko

คำตอบ:


173

นี่คือตัวอย่างสำหรับ bash:

usage="$(basename "$0") [-h] [-s n] -- program to calculate the answer to life, the universe and everything

where:
    -h  show this help text
    -s  set the seed value (default: 42)"

seed=42
while getopts ':hs:' option; do
  case "$option" in
    h) echo "$usage"
       exit
       ;;
    s) seed=$OPTARG
       ;;
    :) printf "missing argument for -%s\n" "$OPTARG" >&2
       echo "$usage" >&2
       exit 1
       ;;
   \?) printf "illegal option: -%s\n" "$OPTARG" >&2
       echo "$usage" >&2
       exit 1
       ;;
  esac
done
shift $((OPTIND - 1))

ในการใช้สิ่งนี้ภายในฟังก์ชัน:

  • ใช้"$FUNCNAME"แทน$(basename "$0")
  • แอดlocal OPTIND OPTARGก่อนโทรgetopts

1
ฉันกำลังลองสิ่งนี้ภายในฟังก์ชัน แต่เมื่อฉันพยายามเรียกใช้ฟังก์ชันนี้ฉันได้รับข้อผิดพลาด "basename: invalid option - 'b'" ดูเหมือนว่ามันกำลังพยายามส่ง "-bash" ไปbasenameด้วยเครื่องหมายขีดนำ
Morgan Estes

5
imside การใช้งานฟังก์ชั่นไม่ได้"$FUNCNAME" "$0"นอกจากนี้ยังเพิ่มlocal OPTIND OPTARG
glenn jackman

ขอบคุณ FUNCNAMEโรงงาน ฉันมีฟังก์ชั่นทั้งหมดของฉันอยู่ในไฟล์เดียวดังนั้นนี่จึงเหมาะอย่างยิ่งสำหรับการขยายให้เป็นสิ่งที่มีประโยชน์สำหรับผู้อื่น
Morgan Estes

5
@sigur ให้แน่ใจว่าคุณพูด"$usage" ทุกที่ที่คุณใช้
glenn jackman

1
มีไว้shift $((OPTIND - 1))เพื่ออะไร?
hpaknia

45

อาร์กิวเมนต์แรกของเชลล์สคริปต์พร้อมใช้งานเป็นตัวแปร$1ดังนั้นการนำไปใช้งานที่ง่ายที่สุดคือ

if [ "$1" == "-h" ]; then
  echo "Usage: `basename $0` [somestuff]"
  exit 0
fi

แต่สิ่งที่ anubhava พูด


ขอบคุณ @MarkBooth แก้ไขการพิมพ์ผิด (รวมถึงการปรับปรุงโดยการใส่เครื่องหมายคำพูด)
ก.ย.

คุณควรสร้างนิสัยในการตัด if ใน [[... ]] สำหรับเงื่อนไขเพื่อหลีกเลี่ยงการแยกวิเคราะห์ตัวแปรที่ไม่ถูกต้องที่มา: github.com/bahamas10/bash-style-guide#bashisms
JREAM

2
ใช่แม้ว่า OP จะไม่ได้ระบุ bash และ[เป็นเวอร์ชันที่รองรับ POSIX
ก.ย.

หมายเหตุ - สำหรับใช้ภายในfunction:คุณควรแทนที่exit 0ด้วยreturnถ้าคุณไม่ต้องการยุติเชลล์ของคุณหลังจากเรียกใช้ฟังก์ชันของคุณ (เคยทำมาแล้ว😂)
Illuminator

29

นี่คือส่วนหนึ่งที่ฉันใช้เพื่อเริ่มเซิร์ฟเวอร์ VNC

#!/bin/bash
start() {
echo "Starting vnc server with $resolution on Display $display"
#your execute command here mine is below
#vncserver :$display -geometry $resolution
}

stop() {
echo "Killing vncserver on display $display"
#vncserver -kill :$display
}

#########################
# The command line help #
#########################
display_help() {
    echo "Usage: $0 [option...] {start|stop|restart}" >&2
    echo
    echo "   -r, --resolution           run with the given resolution WxH"
    echo "   -d, --display              Set on which display to host on "
    echo
    # echo some stuff here for the -a or --add-options 
    exit 1
}

################################
# Check if parameters options  #
# are given on the commandline #
################################
while :
do
    case "$1" in
      -r | --resolution)
          if [ $# -ne 0 ]; then
            resolution="$2"   # You may want to check validity of $2
          fi
          shift 2
          ;;
      -h | --help)
          display_help  # Call your function
          exit 0
          ;;
      -d | --display)
          display="$2"
           shift 2
           ;;

      -a | --add-options)
          # do something here call function
          # and write it in your help function display_help()
           shift 2
           ;;

      --) # End of all options
          shift
          break
          ;;
      -*)
          echo "Error: Unknown option: $1" >&2
          ## or call function display_help
          exit 1 
          ;;
      *)  # No more options
          break
          ;;
    esac
done

###################### 
# Check if parameter #
# is set too execute #
######################
case "$1" in
  start)
    start # calling function start()
    ;;
  stop)
    stop # calling function stop()
    ;;
  restart)
    stop  # calling function stop()
    start # calling function start()
    ;;
  *)
#    echo "Usage: $0 {start|stop|restart}" >&2
     display_help

     exit 1
     ;;
esac

มันแปลกนิดหน่อยที่ฉันวาง start stop start ไว้แยกกัน แต่มันควรจะใช้งานได้


ถ้าคุณให้ตัวเลือกว่างเป็น -d; มันจะวนรอบไม่สิ้นสุดเหรอ?
zerobane

เหตุใดคุณจึงออกจาก 1 ในฟังก์ชันตัวช่วย
jeantimex

17

สำหรับโซลูชันตัวเลือกเดียวที่รวดเร็วให้ใช้ if

หากคุณมีเพียงตัวเลือกเดียวในการตรวจสอบและจะเป็นตัวเลือกแรกเสมอ ( $1) วิธีแก้ปัญหาที่ง่ายที่สุดคือการifทดสอบ ( [) ตัวอย่างเช่น:

if [ "$1" == "-h" ] ; then
    echo "Usage: `basename $0` [-h]"
    exit 0
fi

โปรดทราบว่าสำหรับความเข้ากันได้ของ posix =จะใช้งานได้เช่นเดียวกับ==.

อ้างทำไม$1?

เหตุผลที่$1จำเป็นต้องใส่เครื่องหมายคำพูดก็คือถ้าไม่มี$1เชลล์จะพยายามรันif [ == "-h" ]และล้มเหลวเนื่องจาก==ได้รับอาร์กิวเมนต์เดียวเมื่อคาดหวังสองข้อเท่านั้น:

$ [ == "-h" ]
bash: [: ==: unary operator expected

สำหรับการใช้งานที่ซับซ้อนมากขึ้นgetoptหรือgetopts

ในฐานะที่เป็นปัญหา จาก คนอื่น ๆgetoptsถ้าคุณมีมากกว่าเป็นตัวเลือกที่ง่ายเดียวหรือต้องการตัวเลือกของคุณเพื่อยอมรับข้อโต้แย้งแล้วคุณแน่นอนควรไปสำหรับความซับซ้อนเป็นพิเศษในการใช้

การอ้างอิงอย่างรวดเร็วผมชอบ60 getopts สองกวดวิชา

คุณอาจต้องการพิจารณาgetoptโปรแกรมแทนเชลล์ในgetoptsตัว อนุญาตให้ใช้ตัวเลือกแบบยาวและตัวเลือกหลังอาร์กิวเมนต์ที่ไม่ใช่ตัวเลือก (เช่นfoo a b c --verboseแทนที่จะเป็นเพียงfoo -v a b c) คำตอบนี้ Stackoverflowอธิบายวิธีการใช้ getoptGNU

jeffbyrnesกล่าวว่าลิงก์เดิมเสียชีวิตไปแล้ว แต่โชคดีที่ทางเครื่องกลับได้เก็บถาวรไว้


ขอบคุณ! ฉันมีความสุขกับการใช้ getopt มาเป็นเวลาหนึ่งปีแล้ว แต่ฉันจะดู getopt ด้วย
tttppp

1
น่าเศร้าที่ลิงก์ไปยังบทช่วยสอน Getopts 60 วินาทีนั้นตายแล้ว ดูเหมือนว่า bashcurescancer.com จะไม่มีอีกแล้ว นี่คือการเชื่อมโยงไปยังรุ่นของเครื่อง Wayback
jeffbyrnes


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.