0

So, I am trying to write a script to

1.) Create a new directory with the name the user has specified

2.) Download files from a remote location into the new directory

3.) Append a port to a file in the new directory

What I have now is

OPTIND=1 
while getopts "hpnm:" opt; do
case "$opt" in
    h)
        show_help
        exit 0
        shift
        ;;
    p)  port="*@"
        shift
        /home/servers/zombies/"$server_name"/server.properties >> port: "$@" 
        ;;
    n)  server_name="$*"
        shift
        mkdir /home/ecb/servers/zombies/"$*"
        ;;
    m)  map_name="$*"
        shift
        rsync -azvP username@host:/home/downloads/zombies/"$*" /home/servers/zombies/"$server_name"/
        ;;
esac
done

When I run the script it interprets "-n Test -m test -p 1234" as "Test test 1234". How would I separate the arguments?

1 Answer 1

1
  1. Don't shift. That will confuse getopts, since you are modifying the argument list while it is working.

  2. If an option takes an argument, put a colon after the character in the argument vector. In your case, -p, -n and -m all take arguments, not just -m. So your invocation should be:

    while getopts "hp:n:m:" opt; do
    
  3. To refer to the option argument, use $OPTARG. getops will put the argument in that variable (provided you told getopts that the option takes an argument).

  4. When getopts finishes, OPTIND will be the index of the first positional argument. At this point, if you want, you can shift away all of the consumed arguments:

    shift $((OPTIND-1))
    

    Or you can scan them using bash's subsequence notation:

    for arg in "${@:OPTIND}"; do
    
  5. It is usually not a good idea to act on options immediately. Rather, you would normally save each argument to a variable and process the variables when option parsing is finished. There are a couple of reasons:

    • You might detect an error after the first option has been recognized. In that case, you would normally want to produce an error message without altering the state of the world.

    • Normally, there is no specified order for options (which is why they are identified with option letters, rather than being positional). It's often the case that the actions are better performed in a specific order.

    • Options are, by definition, optional. But you don't know that an option is unspecified until you reach the end of the list. If the option has a default value, or if the non-specification of the option affects the behaviour of your script, you probably want to know about it before you start doing things.

  6. It is normally not necessary to initialize OPTIND, since bash does that automatically when it creates a new execution environment.

Sign up to request clarification or add additional context in comments.

5 Comments

So I have shifted it so that the arguments are inserted into the option, but I still haven't gotten it to separate the input.
I have removed shift, but it still outputs this: prntscr.com/6wq6jb (Note I know that the paths and commands are non-existent on this machine)
@Dhs92: Since I cannot see your script, the command you used, nor your file system, I can only guess what is going on. Did you insert the colons as per point 2? Did you change "$*" (which is almost never correct in any context) to "$OPTARG" as per point 3?
@Dhs92: Don't forget to initialize server_name, port and map_name to sensible defaults. -h does not take an argument (the : means that it will expect an argument, so ./script -h will be an error.) I think the last line should be echo port: .... Other than that, it works just fine for me.
Had to remove the arrays, then it started working properly. Thanks for helping me clean it up and such.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.