0

I have a bash script that I need to take in a user name with a flag, and then I want to be able to look for -r or -w to indicate whether this should be a read or write.

Currently I am using get opts, but this requires that an actual argument be passed to -r and -w.

How do I test if just -r or -w is there without passing something to those flags.

Currently my script looks like this:

#!/bin/bash
while getopts :u:r:w: opt; do
   case $opt in
     u ) user="$OPTARG"                       ;;
     r ) my_read=1                            ;;
     w ) my_write=1                           ;;
    \? ) echo "${0##*/}" [ -erw ]; exit 1     ;;
  esac
done

if [[ ${my_write} -eq 1 ]] ; then
    echo "write"
fi

if [[ ${my_read} -eq 1 ]] ; then
    echo "read"
fi
Cyrus
  • 84,225
  • 14
  • 89
  • 153
William Ross
  • 3,568
  • 7
  • 42
  • 73
  • 6
    If you don't want your options to take arguments, don't use the `:` sigil after them. Your current code is *explicitly instructing* `-r` and `-w` to put the following word in `OPTARG`. – Charles Duffy Nov 01 '17 at 15:31
  • Ah yea, I see that was an issue now. How can I change the if statements to check if that flag was typed at the command line? – William Ross Nov 01 '17 at 15:34
  • see e.g. https://stackoverflow.com/a/16496491/297323 `-z` is your friend – Fredrik Pihl Nov 01 '17 at 15:35
  • `if [[ $my_write ]]` is terse and idiomatic. `if (( my_write ))` isn't bad form either, since you're using a positive numeric value as a sigil for presence. – Charles Duffy Nov 01 '17 at 15:38
  • 2
    ...so, I assume you want `getopts u:rw opt` -- not clear on what the leading `:` was doing either. – Charles Duffy Nov 01 '17 at 15:40

2 Answers2

4

As noted in the comments, a colon (:) indicates the preceding option character requires an argument. Just remove the colons:

#!/bin/bash
while getopts u:rw opt; do
   case $opt in
     u ) user="$OPTARG"                       ;;
     r ) my_read=1                            ;;
     w ) my_write=1                           ;;
    \? ) echo "${0##*/} [ -erw ]" >&2; exit 1 ;;
  esac
done
shift $((OPTIND-1))

if [[ "${my_write}" -eq 1 ]] ; then
    echo "write"
fi

if [[ "${my_read}" -eq 1 ]] ; then
    echo "read"
fi

Other changes made: quotes on final case moved to include square brackets, output to standard error (>&2) to avoid getting piped inappropriately, the shift line was added so your argument list ($@ and $1, etc) have the getopts-parsed options removed, and quotes were placed around tests because otherwise the shell can complain about being passed empty tests (it'll see [[ -eq 1]] if either variable is undefined, which will happen if either -r or -w is not passed, and that is invalid while [[ "" -eq 1 ]] will simply evaluate as false).

Adam Katz
  • 14,455
  • 5
  • 68
  • 83
1

Just get them as parameters not options:

while [ -n "$1" ]; do
case "$1" in
    -r) echo "read";;
    -w) echo "write";;
esac
shift
done
anomaaly
  • 833
  • 4
  • 15
  • 2
    For someone interested in `getopts`-free parsing in more detail, [BashFAQ #35](http://mywiki.wooledge.org/BashFAQ/035) is a good reference. – Charles Duffy Nov 01 '17 at 15:42