0

I have a bash script which processes an input file with optional arguments. The script looks like this

#!/bin/bash while getopts a:b:i: option do case "${option}" in a) arg1=${OPTARG};; b) arg2=${OPTARG};; i) file=${OPTARG};; esac done [ -z "$file" ] && { echo "No input file specified" ; exit; } carry out some stuff 

The script runs fine, but I need to specify the input file like so

sh script.sh -a arg1 -b arg2 -i filename 

I would prefer to be able to call the script without the -i option, like so

sh script.sh -a arg1 -b arg2 filename 

while still having the error message when no input file is specified. Is there a way to do this?

2
  • And what should happen if the script is called as script.sh -i file1 -i file2 file3 file4?CommentedOct 29, 2020 at 15:02
  • @Stéphane-Chazelas Hm, I don't care really (it's for my personal use only). It should just complain (and more importantly, do nothing) if I write something like sh script.sh -a arg1 or sh script.sh -a arg1 -b arg2.
    – 220284
    CommentedOct 29, 2020 at 15:07

2 Answers 2

1
#!/bin/sh - # Beware variables can be inherited from the environment. So # it's important to start with a clean slate if you're going to # dereference variables while not being guaranteed that they'll # be assigned to: unset -v file arg1 arg2 # no need to initialise OPTIND here as it's the first and only # use of getopts in this script and sh should already guarantee it's # initialised. while getopts a:b:i: option do case "${option}" in (a) arg1=${OPTARG};; (b) arg2=${OPTARG};; (i) file=${OPTARG};; (*) exit 1;; esac done shift "$((OPTIND - 1))" # now "$@" contains the rest of the arguments if [ -z "${file+set}" ]; then if [ "$#" -eq 0 ]; then echo >&2 "No input file specified" exit 1 else file=$1 # first non-option argument shift fi fi if [ "$#" -gt 0 ]; then echo There are more arguments: printf ' - "%s"\n' "$@" fi 

I changed the bash to sh as there's nothing bash-specific in that code.

3
0

I would like to expand on Stéphane Chazelas' excellent answer in order to use bashism more on the question style, namely [ -z "$file" ] && { echo "No input file specified" ; exit; } -- instead of using ifs.

#!/bin/bash unset -v file arg1 arg2 while getopts "a:b:i:" option; do case "$option" in a ) arg1="$OPTARG";; b ) arg2="$OPTARG";; i ) file="$OPTARG";; * ) exit 1;; esac done shift "$((OPTIND - 1))" [ -z "$file" ] && [ "$#" -eq 0 ] && { echo "No input file" >&2; exit 1; } [ -z "$file" ] && [ "$#" -gt 0 ] && { file="$1"; shift; } echo "DEBUG: arg 1 is $arg1" echo "DEBUG: arg 2 is $arg2" echo "DEBUG: file is $file" [ "$#" -gt 0 ] && { echo "More arguments"; printf " - %s\n" "$@"; } 
2
  • That's helpful too. Thanks!
    – 220284
    CommentedNov 12, 2020 at 9:32
  • Not sure what you mean there. && / || are not bashisms. They were already present in the Bourne shell in the 70s. They're also present in csh which make them more portable than if whose syntax is different between Bourne-like shells and csh. Using them in place of if constructs is often considered bad practice though. Note that [ -z "$file" ] would also return true if the script is called with -i '' (likely not a problem in practice)CommentedNov 29, 2021 at 15:28

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.