How to create a bash script that takes arguments?

后端 未结 5 1546
一整个雨季
一整个雨季 2021-01-31 10:33

I already know about getopts, and this is fine, but it is annoying that you have to have a flag even for mandatory arguments.

Ideally, I\'d like to be able to have a scr

5条回答
  •  长发绾君心
    2021-01-31 11:23

    You're are suffering from illusions; using getopts does not require mandatory arguments prefixed by a flag letter. I tried to find a suitable example from my corpus of scripts; this is a semi-decent approximation. It is called rcsunco and is used to cancel a checkout from RCS. I haven't modified it in a while, I see; I use it quite often (because I haven't migrated from RCS completely, yet).

    #!/bin/sh
    #
    #   "@(#)$Id: rcsunco.sh,v 2.1 2002/08/03 07:41:00 jleffler Exp $"
    #
    #   Cancel RCS checkout
    
    # -V print version number
    # -n do not remove or rename checked out file (like SCCS unget) (default)
    # -r remove checked out file (default)
    # -k keep checked out file as $file.keep
    # -g checkout (unlocked) file after clean-up
    # -q quiet checkout
    
    : ${RCS:=rcs}
    : ${CO:=co}
    
    remove=yes
    keep=no
    get=no
    quiet=
    
    while getopts gknqrV opt
    do
        case $opt in
        V)  echo "`basename $0 .sh`: RCSUNCO Version $Revision: 2.1 $ ($Date: 2002/08/03 07:41:00 $)" |
            rcsmunger
            exit 0;;
        g)  get=yes;;
        k)  keep=yes;;
        n)  remove=no;;
        q)  quiet=-q;;
        r)  remove=yes;;
        *)  echo "Usage: `basename $0 .sh` [-{n|g}][-{r|k}] file [...]" 1>&2
            exit 1;;
        esac
    done
    
    shift $(($OPTIND-1))
    
    for file in $*
    do
        rfile=$(rfile $file)
        xfile=$(xfile $rfile)
        if $RCS -u $rfile
        then
            if [ $keep = yes ]
            then
                if [ -f $xfile ]
                then
                    mv $xfile $xfile.keep
                    echo "$xfile saved in $xfile.keep"
                fi
            elif [ $remove = yes ]
            then rm -f $xfile
            fi
            if [ $get = yes ] && [ $remove = yes -o $keep = yes ]
            then $CO $quiet $rfile
            fi
        fi
    done
    

    It's only a semi-decent approximation; the script quietly does nothing if you don't supply any file names after the optional arguments. However, if you need to, you can check that the mandatory arguments are present after the 'shift'. Another script of mine does have mandatory arguments. It contains:

    ...
    shift $(($OPTIND - 1))
    
    case $# in
    2)  case $1 in
        install)    MODE=Installation;;
        uninstall)  MODE=Uninstallation;;
        *)          usage;;
        esac;;
    *)  usage;;
    esac
    

    So, that command (jlss) can take optional arguments such as -d $HOME, but requires either install or uninstall followed by the name of something to install. The basic mode of use is:

    jlss install program
    

    But the optional mode is:

    jlss -d $HOME -u me -g mine -x -p install program
    

    I didn't show all of jlss because it has about 12 options - it isn't as compact as rcsunco.


    If you were dealing with mandatory arguments before option arguments, then you'd have to do a bit more work:

    • You'd pick up the mandatory arguments, shifting them out of the way.
    • Then you process the optional arguments with the flags.
    • Finally, if appropriate, you handle the extra 'file name' arguments.

    If you are dealing with mandatory arguments interspersed with option arguments (both before and after the mandatory ones), then you have still more work to do. This is used by many VCS systems; CVS and GIT both have the facility:

    git -global option command [-sub option] [...]
    

    Here, you run one getopts loop to get the global options; pick up the mandatory arguments; and run a second getopts loop to get the sub-options (and maybe run a final loop over the 'file name' arguments).

    Isn't life fun?

提交回复
热议问题