96

I'm looking a way to build conditional assignments in bash:

In Java it looks like this:

int variable= (condition) ? 1 : 0;
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Neuquino
  • 11,580
  • 20
  • 62
  • 76

8 Answers8

89

If you want a way to define defaults in a shell script, use code like this:

: ${VAR:="default"}

Yes, the line begins with ':'. I use this in shell scripts so I can override variables in ENV, or use the default.

This is related because this is my most common use case for that kind of logic. ;]

Demosthenex
  • 4,343
  • 2
  • 26
  • 22
  • 5
    This is a valuable technique, but the only condition supported with the ':=' notation is 'set to "default" if value is unset or empty'. The more general notation in the question is supported directly in bash. – Jonathan Leffler Mar 14 '10 at 04:52
  • 2
    I agree completely, I gave it a disclaimer for being slightly off topic. – Demosthenex Mar 14 '10 at 05:16
  • 3
    Omitting the colon before equal tests only for the variable to be unset and doesn't change it if it's null. – Dennis Williamson Mar 19 '10 at 16:35
  • 1
    Related useful explanation about starting with a colon and the parameter expansion in [this answer](https://stackoverflow.com/a/307735/1180551). – GcL Feb 06 '20 at 16:37
83

As per Jonathan's comment:

variable=$(( 1 == 1 ? 1 : 0 ))  

EDIT:

I revised the original answer which just echo'd the value of the condition operator, it didn't actually show any assignment.

Community
  • 1
  • 1
Anthony Forloney
  • 90,123
  • 14
  • 117
  • 115
  • 1
    Nit-pick: that isn't an assignment - it is just a conditional expression. However, `variable=$(( 1 == 1 ? 1 : 0 ))` works as long as there are no spaces around the first '=' sign; you can omit all the spaces after that and it also works - but think about readability. – Jonathan Leffler Mar 14 '10 at 04:54
  • 3
    You can have complete freedom of spacing (and drop the dollar sign) if you move the opening double parentheses all the way to the left. `(( variable = 1 == 1 ? 1 : 0 ))` or `(( variable = (1 == 1) ? 1 : 0 ))` But it could be argued that it's more readable as given in the answer. – Dennis Williamson Mar 19 '10 at 16:29
  • 52
    Also, it should be noted that this operator only works for arithmetic operations in Bash. – Dennis Williamson Mar 19 '10 at 16:36
  • 18
    What if the variable I want to set is a string? – Giovanni Botta May 22 '15 at 16:10
  • 43
    This does not work for strings: echo $(( 1 == 1 ? 'hello' : 'world' )) – 4aRk Kn1gh7 Dec 07 '15 at 06:28
48
myvar="default" && [[ <some_condition_is_true> ]]  && myvar="non-default"

real examples:

DELIM="" && [[ "$APP_ENV_RESOLVED" != "" ]] && DELIM=$INNER_DELIM

The condition can be "(( ... ))" as well:

filepath=/proc/drbd && (( $# > 0 )) && filepath=$1
Kevin Little
  • 12,436
  • 5
  • 39
  • 47
45

I wanted to do a conditional assignment with strings and I ended up with:

VAR=$([ "$MYVALUE" = "value" ] && echo "YES" || echo "NO")

Which is:

VAR=$([ condition ] && value_when_true || value_when_false)
Lee Goddard
  • 10,680
  • 4
  • 46
  • 63
Pierre Maoui
  • 5,976
  • 2
  • 27
  • 28
37

In addition to the other more general answers (particularly as per Jonathan's comment and Kevin's more general answer [which also supports strings]) I'd like to add the following two solutions:


setting the variable to either 0 or 1 based on the condition:

(as the question's example suggests.)

The general form would read

(condition); variable=$?;

where $variable results in being either 0 or 1 and condition can be any valid conditional expression.

E.g. checking a variable ...

[[ $variableToCheck == "$othervariable, string or number to match" ]]
variable=$?

... or checking a file's existence ...

[ -f "$filepath" ]
fileExists=$?

... or checking the nummerical value of $myNumber:

(( myNumber >= 1000000000 ))
is_huge_number=$?


The advantages of this solution is that

  • it supports arbitrary conditional expressions, including strings
    (which are not supported in arithmetic expressions of Jonathan's solution)
  • that variable gets declared in any case, unlike in griffon's answer:
    [ -z "$variable" ] && variable="defaultValue"
    Which would matter in case you want to nameref it later on (e.g. from within a function).


Please note: In Bash, the special variable $? always contains the exit code of the previously executed statement (or statement block; see the man bash for more details). As such, a positive result is generally represented by the value 0, not 1 (See my comment below, thanks Assimilater for pointing it out). Thus, if the condition is true (e.g [[2 eq 2]]) then $?=0.

If instead you need 0 or 1 in your variable (e.g. to print or do mathematical calculations) then you need to employ boolean negation using a leading exclamation mark (as pointed out by GypsySpellweaver in the comments below): ( ! condition ); variable=$? or ! ( condition ); variable=$?. (However, readability in terms of what happens might be a bit less obvious.)

Another possible solution by Jonathan would be variable=$(( 1 == 1 ? 1 : 0 )) - which, however, is creating a subshell.

If you want to avoid the creation of a subshel, keep good readability or have arbitrary conditions, use one of the following solutions.


setting the variable to arbitrary values:

as it is done in most other answers, it could adapted as follows:

(condition) \
    && variable=true \
    || variable=false

e.g as in

[[ $variableToCheck == "$othervariable, string or number to match" ]] \
    && variable="$valueIfTrue" \
    || variable="$valueIfFalse"

or to get 1 in a positive check, and 0 upon failure (like in the question's example):

[[ $variableToCheck == "$othervariable, string or number to match" ]] \
    && variable=1 \
    || variable=0

(for the last example, - as already mentioned in the notes above - the same can be achieved with boolean negation using a leading exclamation mark:

[[ ! $variableToCheck == "$othervariable, string or number to match" ]]
variable=$?


The advantages of this solution is that

  • it might be considered a bit better readable than Kevin's answer
    myvar="default" && [[ <some_condition_is_true> ]] && myvar="non-default", and
  • the $valueIfTrue is conditionally evaluated only if needed,
    which would matter in case you'd do something
    • with side-effect, like
      • variable=$((i++)), or
      • { variable=$1; shift; }
    • high computation, like
      • variable=$(find / -type f -name ListOfFilesWithThisNameOnMySystem)
  • is a bit shorter than ghostdog74's answer
    (which, however is great if you have multiple conditions!)
  • does not open a subshell as in Pierre's answer
  • and as above:
    • it supports arbitrary conditional expressions, including strings
      (which are not supported in arithmetic expressions of Jonathan's solution)
    • that variable gets declared in any case, unlike in griffon's answer:
      [ -z "$variable" ] && variable="defaultValue"
      Which would matter in case you want to nameref it later on (e.g. from within a function).
Martin Rüegg
  • 815
  • 9
  • 14
  • The result of your first solution is a little confusing to me. A conditional that evaluates to true results in a value of 0; where a conditional that evaluates to false results in a value of 1 – Assimilater Sep 13 '16 at 06:23
  • Maybe I did it wrong though. I test with `[ "$1" == "--all" ] ; allperiods=$? ; echo $allperiods` – Assimilater Sep 13 '16 at 06:25
  • 1
    @Assimilater, I agree, that 0 and 1 might be confusing. It stems from the fact that an exit code of a program or statement is usually 0 when everything went ok, and an arbitrary code (usually between 1 and 254) for errors. Thus `executable && executesWhenOk || executesWhenNotOk` and in our scenario the test is the "executable" (or statement, to be precise), and the special variable `$?` contains the result code of the last statement. hope that helps clarifying. – Martin Rüegg Sep 14 '16 at 12:56
  • That helps :) (You might consider adding it to the answer ;) ) – Assimilater Sep 15 '16 at 04:31
  • This is the best solution, especially as the accepted answer only works for arithmetic expressions. It is also more readable @kevin's and shorter than the verbose, but otherwise excellent and more-general `case` solution by @ghostdog74. – Brent Faust Feb 16 '19 at 00:43
  • 1
    The "normal" Boolean logic which programmers are adapted to, of `1` is `true` and `0` is `false`, can easily be achieved in this case with the _NOT_ `!` operator applied to the expression. Either inside, or outside the 'expression'. `( ! condition ); variable=$?` or `! ( condition ); variable=$?` Such that `[ ! -f "$filepath" ]; fileExists=$?` works as a programmer would be accustomed to: 1 file exists, and 0 file is missing. – Chindraba Feb 22 '19 at 07:10
  • HI @GypsySpellweaver, thanks for your comment. Yes, sure, that's a possibility. However, be aware that if later you use `$fileExists` in a further test, you have to negate it again every single time. – Martin Rüegg Feb 22 '19 at 08:21
  • Agree. That's why I mentioned, do it only if you need it for e.g. mathematical calculations. - I have added your solution to the post. Needs to be moderated before it will appear though. Thanks again! – Martin Rüegg Feb 22 '19 at 08:46
  • If I use this with 2 assignments, it behaves oddly. `[[ some condition ]] && IS_IDENTITY=0 || IS_IDENTITY=1 [[ some other condition ]] && IS_CORE=0 || IS_CORE=1` If the first condition is true and the 2nd false, `IS_CORE` will always be set to 1, and `IS_IDENTITY` will be 0 – Douglas Gaskell Oct 13 '22 at 05:43
  • @DouglasGaskell, if I understand you correctly, `IS__IDENTITY`should be set based on the evaluation of `[[ some condition ]]`, while `IS_CORE` should be set based on `[[ some other condition ]]`. In that case, you need to add a semicolon `;` between the two statements (i.e. `[[ some condition ]] && IS_IDENTITY=0 || IS_IDENTITY=1; [[ some other condition ]] && IS_CORE=0 || IS_CORE=1`) or put them on two separate lines! Without that, the `IS_CORE=1` is called after a positive `some condition` *and* positive setting of `IS_IDENTITY=0` (which yields true). The code fails if `some condition` =false – Martin Rüegg Oct 28 '22 at 10:59
14

Big ups to @Demosthenex and especially @Dennis Williamson for the shortest and easiest solution I've seen. Leave it to bash to require a bunch of parentheses for a simple ternary assignment. Ahh, the 60s! And to put it all together in an example...

echo $BASHRULES;             # not defined
                             # no output
: ${BASHRULES:="SCHOOL"}     # assign the variable
echo $BASHRULES              # check it
SCHOOL                       # correct answer
: ${BASHRULES="FOREVER?"}    # notice the slightly different syntax for the conditional assignment
echo $BASHRULES              # let's see what happened!
SCHOOL                       # unchanged! (it was already defined)

I wrote that a long time ago.. these days I'd probably get more excited over a solution like...

PLATFORM=iphonesimulator
OTHERSDK=iphone && [[ $PLATFORM=~os ]]      \
                &&     OTHERSDK+=simulator  \
                ||     OTHERSDK+=os

$OTHERSDKiphoneos

Alex Gray
  • 16,007
  • 9
  • 96
  • 118
  • 2
    Thank you, Mr. @alex gray. This is an excellent syntactic arrow that everyone should have in their `bash` quiver… for the curious and/or unadventurous who discover it here, this sort of conditional assignment plays nice with `export` statements, too – which you put that between the colon prefix and the dollar-sign statement-y part, e.g. `: export ${YO_DOGG:="global environment default"}` – fish2000 Nov 05 '13 at 11:13
  • 1
    Your second solution, actually represents the second solution of my [answer](http://stackoverflow.com/a/35890017/3102305) and was added only [after](http://stackoverflow.com/posts/15182923/revisions) I've posted it. A short reference would have been nice. Please also note, since `iphonesimulator` does not contain the string `os`, above solution would end up with `OTHERSDK=iphonesimulator`. – Martin Rüegg Sep 15 '16 at 08:28
  • I like this short answer. Some of the others were too long. – MarkHu Aug 04 '17 at 01:32
  • The Bourne shell was created in the late 1970s, and Bash in the late 1980s. – tripleee May 02 '22 at 12:20
6

another way using a case/switch

case "$variable" in
  condition) result=1 ;;
          *) result=0 ;;
esac
Audwin Oyong
  • 2,247
  • 3
  • 15
  • 32
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
5

If you want to assign a value unless variable is empty use this:

[ -z "$variable" ] && variable="defaultValue"

You can put as well, each other condition on the []

griffon vulture
  • 6,594
  • 6
  • 36
  • 57