0

What is the easiest (and possibly one-liner) way to check for the absence of a specific argument in a bash script, disregarding the argument order ?

I would like to assign at the beginning of the script a "boolean" variable named REAL_RUN with true or false based on the absence or presence of an argument --dry-run included among all the script arguments. Something like this:

REAL_RUN=... # <-- what to put here ?
if [ "$REAL_RUN" = true ] ; then
   # do something
fi

I expect REAL_RUN to be assigned true with the following cases:

./run.sh
./run.sh foo bar
./run.sh foo --dry-run-with-unexpected-suffix bar
./run.sh foo ------dry-run bar

Instead, with the following examples REAL_RUN must be set to false

./run.sh --dry-run
./run.sh foo --dry-run
./run.sh --dry-run bar
./run.sh foo --dry-run bar
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Aldo
  • 550
  • 3
  • 13

3 Answers3

1

case is portable to POSIX sh. It can be a one-liner, though conventionally, the statement is divided over multiple physical lines.

case " $@ " in *\ --dry-run\ *) REAL_RUN=false;; *) REAL_RUN=true;; esac

or for legibility

# Put spaces around "$@" to make the later logic simpler
case " $@ " in
  # If --dry run exists with spaces on both sides,
  *\ --dry-run\ *)
    # Set REAL_RUN to false
    REAL_RUN=false;;
  # Otherwise,
  *)
    # ... it's true.
    REAL_RUN=true;;
esac

Some people like to put the special token ;; on a line of its own but in a simple case like this, that seems excessive.

This is slightly inexact in that it fails to distinguish between spaces between arguments and quoted spaces. Somebody could write command " --dry-run " and it would trigger the condition, even though strictly speaking, this should be interpreted as a static string argument which starts and ends with a literal space, and not an option at all. (To prevent this, probably loop over "$@" and check for a literal argument:

REAL_RUN=true
for arg; do    # shorthand for 'for arg in "$@"; do'
    case $arg in
      --dry-run) REAL_RUN=false;;
    esac
done

but this definitely isn't a one-line any longer.)

tripleee
  • 175,061
  • 34
  • 275
  • 318
0

You can use this regex matching in BASH:

[[ $# -eq 0 || ! $* =~ (^| )--dry-run( |$) ]] &&
REAL_RUN=true || REAL_RUN=false;

echo "REAL_RUN=$REAL_RUN"
anubhava
  • 761,203
  • 64
  • 569
  • 643
-1

You could create a function like :

contains () {
  local e match="$1"
  shift
  for e; do [[ "$e" == "$match" ]] && return 0 ; done
  return 1
}

And then use it by passing the array already coming from the system :

[[ `contains "apple" "$@"` -eq 0 ]] && echo "Is present" || echo "Is not present"

Regards!

Matias Barrios
  • 4,674
  • 3
  • 22
  • 49
  • The command substitution captures the function's standard output, not its return code (and you should aveid the obsolescent backticks syntax). The proper way to use this is much simpler: `contains "apple" "$@" && echo "Is present" || echo "Is not present"` – tripleee Jun 11 '18 at 15:36
  • @tripleee one question , why is the use of backticks obsolete? I always understood the backticks like a substitution happening in the same process while the $() forks into a new process. Is that the case? – Matias Barrios Jun 11 '18 at 15:40
  • No, they are basically equivalent, though some details of their parsing differs. See https://stackoverflow.com/questions/9449778/what-is-the-benefit-of-using-instead-of-backticks-in-shell-scripts – tripleee Jun 11 '18 at 15:41
  • @tripleee ok. got it now. Thanks! – Matias Barrios Jun 11 '18 at 15:50