程式扎記: [Linux 常見問題] Bash - How do I parse command line arguments in bash?

標籤

2016年9月25日 星期日

[Linux 常見問題] Bash - How do I parse command line arguments in bash?

Source From Here 
Question 
Say, I have a script that gets called with this line: 
# ./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

or this one: 
# ./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile

What's the accepted way of parsing this such that in each case (or some combination of the two$v$f, and $d will all be set to true and $outFile will be equal to /fizz/someOtherFile ? 

How-To 

Preferred Method: Using straight bash without getopt[s] 
I originally answered the question as the OP asked. This Q/A is getting a lot of attention, so I should also offer the non-magic way to do this. I'm going to expand upon guneysus's answer to fix the nasty sed and include Tobias Kienzler's suggestion. Two of the most common ways to pass key value pair arguments are: 

Straight Bash Space Separated 
- myscript.sh 
  1. #!/bin/sh  
  2. # some arguments don't have a corresponding value to go with it such as  
  3. # in the --default example).  
  4. # note: if this is et to -gt 0 the /etc/hosts part is not recongnized  
  5.   
  6. while [[ $# -gt 1 ]]  
  7. do  
  8.   
  9. key="$1"  
  10.   
  11. case $key in  
  12.     -e|--extension)  
  13.     EXTENSION="$2"  
  14.     shift # past argument  
  15.     ;;  
  16.     -s|--searchpath)  
  17.     SEARCHPATH="$2"  
  18.     shift  
  19.     ;;  
  20.     -l|--lib)  
  21.     LIBPATH="$2"  
  22.     shift  
  23.     ;;  
  24.     --default)  
  25.     DEFAULT=YES  
  26.     ;;  
  27.     *)  
  28.     # Unknown option  
  29.     ;;  
  30. esac  
  31. shift # past argument or value  
  32. done  
  33.   
  34. echo "File extension=${EXTENSION}"  
  35. echo "Search path=${SEARCHPATH}"  
  36. echo "Library path=${LIBPATH}"  
  37.   
  38. if [[ -n $1 ]]; then  
  39.     echo "Last line of file specified as non-opt/last argument: $1"  
  40. fi  
Usage 
# ./myscript.sh -e conf -s /etc -l /usr/lib /etc/hosts 
File extension=conf
Search path=/etc
Library path=/usr/lib
Last line of file specified as non-opt/last argument: /etc/hosts

Straight Bash Equals Separated 
- myscript2.sh 
  1. #!/bin/sh  
  2. DEFAULT=NO  
  3. for i in "$@"  
  4. do  
  5. case $1 in  
  6.     -e=*|--extension=*)  
  7.     EXTENSION="${i#*=}"  
  8.     shift  # pass argument = value  
  9.     ;;  
  10.     -s=*|--search=*)  
  11.     SEARCHPATH="${i#*=}"  
  12.     shift  
  13.     ;;  
  14.     -l=*|--lib=*)  
  15.     LIBPATH="${i#*=}"  
  16.     shift  
  17.     ;;  
  18.     --default)  
  19.     DEFAULT=YES  
  20.     shift  
  21.     ;;  
  22.     *)  
  23.     # Unknown option  
  24.     ;;  
  25. esac  
  26. done  
  27.   
  28. echo "File extension=${EXTENSION}"  
  29. echo "Search path=${SEARCHPATH}"  
  30. echo "Library path=${LIBPATH}"  
  31. echo "Is Default? ${DEFAULT}"  
  32. if [[ -n $1 ]]; then  
  33.     echo "Last line of file specified as non-opt/last argument: $1"  
  34. fi  
Usage: 
$ ./myscript2.sh -e=conf -s=/etc -l=/usr/lib --default /etc/hosts
File extension=conf
Search path=/etc
Library path=/usr/lib
Is Default? YES
Last line of file specified as non-opt/last argument: /etc/hosts

Using getopt[s] 
from: http://mywiki.wooledge.org/BashFAQ/035#getopts 
Never use getopt(1). getopt cannot handle empty arguments strings, or arguments with embedded whitespace. Please forget that it ever existed. The POSIX shell (and others) offer getopts which is safe to use instead. Here is a simplistic getopts example: 
- myscript3.sh 
  1. #!/bin/sh  
  2. # A POSIX variable  
  3. OPTION=1 # Reset in case getopts has been used previous in the shell  
  4.   
  5. # Initialize our own variables:  
  6. output_file=""  
  7. verbose=0  
  8.   
  9. function show_help(){  
  10.     echo "Show help here!"  
  11. }  
  12.   
  13. while getopts "h?vf:" opt; do  
  14.     case "$opt" in  
  15.     h|\?)  
  16.         show_help  
  17.         exit 0  
  18.         ;;  
  19.     v) verbose=1  
  20.         ;;  
  21.     f) output_file=$OPTARG  
  22.         ;;  
  23.     esac  
  24. done  
  25.   
  26. shift $((OPTIND-1))  
  27.   
  28. "$1" == '--' ] && shift  
  29.   
  30. echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"  
  31.   
  32. # End of file  
Usage: 
$ ./myscript3.sh -h
Show help here!
$ ./myscript3.sh -v -f=/etc/test others
verbose=1, output_file='=/etc/test', Leftovers: others

The advantages of getopts are: 
1. It's portable, and will work in e.g. dash.
2. It can handle things like -vf filename in the expected Unix way, automatically.

The disadvantage of getopts is that it can only handle short options (-h, not --help) without trickery. There is a getopts tutorial which explains what all of the syntax and variables mean. In bash, there is also help getopts, which might be informative.

沒有留言:

張貼留言

網誌存檔