I have a script where I've implemented switches using getopts. However, I'm having trouble referencing the next argument.
My script is for backporting a backup of our website on a local development environment. I've added a -p
switch to run some post-deploy steps. Here's my syntax:
backport -p /path/to/website_backup.sql.gz
So, before the switch, I was testing that a file was specified, and that it's a proper file. Since a filename path was the only argument, I could assume that it was necessary, and also that it would be the first argument ($1
).
if [[ $# -eq 0 ]] ; then
echo 'Specifcy the sql file to backport.';
exit 0;
fi
if [[ ! -f "$1" ]]; then
echo "$1 is not a valid file.";
exit 0;
fi
I found this answer which demonstrated an example of how to use getopts to parse switch arguments:
while getopts "p" opt; do
case $opt in
p) p_post_deploy=true ;; # Handle -a
esac
done
Of course, using $1
as the argument didn't work after implementing getopts. Without the switch it was fine. But, when I added the switch, it was, of course, the first argument, which my script was testing for being a file.
$ backport -p /path/to/website_backup.sql.gz
-p is not a valid file.
So, with the introduction of switches, I can't rely on any argument appearing at particular position in the command. Hard-coding $2
won't work, because the filename argument won't be the second argument if there is no switch. I want a solution that will allow me to accept arguments after switches, and allow me to introduce new switches in the future while only updating the code to handle the new switches themselves (and not have to re-shuffle later arguments that may be moved after the introduction of a new switch).
I looked at the answers to the unix.stackexchange question that instructed me how to use getopts to parse switches. One of the answers mentioned $*
as a variable representing the remaining arguments.
if [[ "$*" -eq 0 ]] ; then
echo 'Specifcy the sql file to backport.';
exit 0;
fi
However, when I try to use it, I'm not expressing syntax correctly, and I get a parse error.
~/scripts/backport: line 14: [[: /d/Downloads/database.sql.gz: syntax error: operand expected (error token is "/d/Downloads/database.sql.gz")
How do I test the filename argument after getops?
Here is the script in its current version:
$ cat backport
#!/bin/bash
set -e
while getopts "p" opt; do
case $opt in
p) p_post_deploy=true ;;
esac
done
shift $(($OPTIND - 1))
# testing what this variable looks like
printf "Remaining arguments are: %s\n" "$*"
if [[ "$*" -eq 0 ]] ; then
echo 'Specifcy the sql file to backport.';
exit 0;
fi
if [[ ! -f "$*" ]]; then
echo "$* is not a valid file.";
exit 0;
fi
drush @local.dev sql-drop -y ;
zcat $1 | drush @local.dev sqlc ;
drush @local.dev cr;
if [ ! -z "$p_post_deploy" ] ; then
echo "Running post-deploy..."
SCRIPT_PATH=$(dirname "$BASH_SOURCE")
source "$SCRIPT_PATH/post-deploy"
post_deploy
fi
getopts
getopts
, and what error did you encounter, when you were still trying to use$1
(ie. before you added those"$*"
, which are causing other problems)?shift "$(( OPTIND - 1 ))"
, the value of$1
will be the first argument after any option, not-p
(this is why you do thatshift
, after all)OPTIND
is. TheOPTIND
variable holds the position of the first command line argument that is not an option. Shifting by this number, minus 1, always leaves the non-options. No need to update that statement ever.