LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Software (https://www.linuxquestions.org/questions/linux-software-2/)
-   -   Problem With Arguments Test in Bash Script (https://www.linuxquestions.org/questions/linux-software-2/problem-with-arguments-test-in-bash-script-934862/)

dcparris 03-16-2012 06:51 PM

Problem With Arguments Test in Bash Script
 
I'm still a bit of a novice when it comes to bash scripting, and am having problems processing arguments from the command line. I tend to run my script as:
Code:

./myscript file1 file2 or ./myscript action file1 file2
If I run the script with no action - just the files - the script runs the default action.

I want to say:
(1) if one or more files are listed, with no action, then process with default action
(2) if an action is given, along with one or more files, then process the files with this action
(3) action, if given must be $1

Can anyone help?

My current incarnation looks like this:
Code:

if [ -f "$1" -o -d "$1" ]  # if this is a file or directory
    then
        filelist="$@"
        action1          # default action, process files
    elif [ ! -f "$1" -a ! -d "$1" ]  # if it is NOT a file AND not a directory, it's an action.
    then
        action="$1"
        filelist="$@"
        if [ $action == "action1" ]  # default action when called explicitly
            then
                action1              # calls the function to process the files
            elif [ $action == "action2" ]  # performs opposite action on files
            then
                action2
# the rest of these are just helpful actions, but do not actually process files
            elif [ $action == "action3" ]
            then
                action3
            elif [ $action == "action4" ]
            then
                action4
            elif [ $action == "action5" ]
            then
                action5
            elif [ $action == "action6" ]
            then
                action6
            else   
                echo "$action is invalid"
        fi
    else
        echo "Not an option"
fi

Two problems:
(1) Even though I have checked my arguments here, I still have to strip $1 from those functions that do process the files anyway.
(2) The action1 function works great, but the action2 function breaks, even though it processes the files in reverse from action1.

This part of action1 and action2 are exactly alike:
Code:

action()
{
for f in $filelist  # processes $1 (the action), along with the files.
do
    if [ -f "$f" -o -d "$f" ]  # this is to prevent processing $1

Is getopts a better approach?

Thanks!

druuna 03-17-2012 04:05 AM

Hi,

Quote:

Originally Posted by dcparris
(1) Even though I have checked my arguments here, I still have to strip $1 from those functions that do process the files anyway.

You can use shift to get rid of the first argument (simplified):
Code:

#!/bin/bash

if [ -f "$1" -o -d "$1" ]
then
  action="default action"
  filelist="$@"
else
  action="$1"
  shift          # strip first argument
  filelist="$@"
fi

echo $action
echo $filelist

exit 0

Quote:

2) The action1 function works great, but the action2 function breaks, even though it processes the files in reverse from action1.
Maybe this will work (simplified):
Code:

#!/bin/bash

doAction() {
  Action="$1"    # store $1 (= action) in human readable variable
  Files="$2"    # store $2 (= file(s)) in human readable variable
  # the next part is a case/esac construct
  case ${Action} in
    action1) echo "do action 1 on $Files";;
    action2) echo "do action 2 on $Files";;
    * ) echo "Action ($Action) is not valid....";;
  esac
}

if [ -f "$1" -o -d "$1" ]
then
  action="action1"
  filelist="$@"
else
  action="$1"
  shift
  filelist="$@"
fi

doAction "$action" "$filelist"  # start function using action and file(s)

exit 0

Quote:

Is getopts a better approach?
Not necessarily better, you stll need to tell getops what is correct and what is not. With a limited amount of options I tend not to use getops (but that is a personal choice).

Hope this helps.

dcparris 03-19-2012 04:03 AM

Many Thanks! Shift works. I thought I was not understanding the use of shift, since I could not get action2 to work for the life of me. Through process of elimination, I now realize the problem lay elsewhere (namely inside action2), specifically the file/directory test. But I have learned a lot about hunting bugs - and processing command line arguments.

salasi 03-19-2012 10:01 AM

Quote:

Originally Posted by dcparris (Post 4628785)
Is getopts a better approach?

I never really know, short of actually writing it out both ways (which seems a bit excessive). Getopts can result in cleaner-looking code, if you structure the bit that actually deals with the options well, but 'cleaner' isn't always a synonym for 'better'. The getopts approach hides some of the complexity in getopts out of the way of anyone looking at the script, which feels like an advantage, but unless you understand exactly what getopts does, is it really such an advantage?

I'd have to say that its too close to call, in general, but that you might find that one approach suits your style more than the other.

dcparris 03-19-2012 10:59 AM

Thanks for that Salasi, as is frequently true, I suppose it's a matter of there being more than one way to skin a cat.

salasi 03-19-2012 11:06 AM

Stupid answer:

http://aplawrence.com/Unix/getopts.html
http://www.shelldorado.com/goodcoding/cmdargs.html
http://www.bash-hackers.org/wiki/dok...topts_tutorial
http://bashcurescancer.com/the-60-se...-tutorial.html
http://stackoverflow.com/questions/4...d-line-options

(It is a stupid answer, because the only thing here is links, and all can be found by the use of a search engine. I still hope that they are valuable, though, because you don't have to sort through all sorts of rubbish to get there.)

catkin 03-19-2012 11:06 AM

I like getopts because it:
  • is easier to write initially when you have a pro-forma skeleton.
  • deals with no space between the option letters and their arguments.
  • encourages complete error trapping.
  • is easier to extend when the script is further developed.
FWIW here is my pro-forma/boilerplate.
Code:

        lf=$'\n'

        # Set defaults that may be overriden by command line parsing
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        # Parse command line
        # ~~~~~~~~~~~~~~~~~~
        emsg=''
        while getopts dhVo: opt 2>/dev/null
        do
                case $opt in
                        d )
                                debug='YES'
                                ;;
                        h )
                                usage -v
                                exit 0
                                ;;
                        V )
                                echo "$prgnam version $prg_ver"
                                exit 0
                                ;;
                        o )
                                o_optarg="$OPTARG"
                                ;;
                        * )
                                emsg="${lf}Invalid option '$opt'"
                esac
        done

        # Test for extra arguments
        # ~~~~~~~~~~~~~~~~~~~~~~~~
        shift $(( $OPTIND-1 ))
        if [[ $* != '' ]]; then
                emsg="${lf}Invalid extra argument(s) '$*'"
        fi

        # Test for mandatory options not set
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        if [[ ${o_optarg:-} = '' ]]; then
                emsg="${lf}Mandatory option -o not given"
        fi

        # Report any errors
        # ~~~~~~~~~~~~~~~~~
        if [[ $emsg != '' ]]; then
                echo "$emsg" >&2
                usage
                exit 1
        fi

        echo "DEBUG: o_optarg is '$o_optarg'"


chrism01 03-19-2012 08:00 PM

I agree that if you have varying nums (& types) of params, then getopts is the way to go

pan64 03-20-2012 07:07 AM

I agree, getopts is a good idea, but it does not support that kind of syntax


All times are GMT -5. The time now is 08:49 PM.