37

It is pretty clear that with shell scripting this sort of thing can be accomplished in a huge number of ways (more than most programming languages) because of all the different variable expansion methods and programs like test and [ and [[, etc.

Right now I'm just looking for

DIR=$1 or .

Meaning, my DIR variable should contain either what is specified in the first arg or the current directory.

What is the difference between this and DIR=${1-.}?

I find the hyphen syntax confusing, and seek more readable syntax.

Why can't I do this?

DIR="$1" || '.'

I'm guessing this means "if $1 is empty, the assignment still works (DIR becomes empty), so the invalid command '.' never gets executed."

Steven Lu
  • 41,389
  • 58
  • 210
  • 364
  • 1
    I found my answer to what the colon means: http://wiki.bash-hackers.org/syntax/pe#use_a_default_value – Steven Lu Apr 18 '13 at 05:07
  • 4
    Embrace the language. Not all languages have the same idioms. `DIR=${1:-.}` is a perfectly natural way to express this logic in any POSIX-compatible shell. – chepner Apr 18 '13 at 12:26
  • 1
    I find the downvote to be entirely justified. I don't like the way I wrote this question. I was frustrated at the time, I guess. But ever since I posted this, I've never had any trouble remembering the `${VAR:-default}` syntax. I'm going to edit my question to make it less petty. I'm sure you weren't the downvoter @chepner but you are absolutely right! – Steven Lu Feb 02 '17 at 19:29
  • I never downvoted (or if I did, I subsequently retracted it). – chepner Feb 02 '17 at 20:24

3 Answers3

70

I see several questions here.

  1. “Can I write something that actually reflects this logic”

    Yes. There are a few ways you can do it. Here's one:

    if [[ "$1" != "" ]]; then
        DIR="$1"
    else
        DIR=.
    fi
    
  2. “What is the difference between this and DIR=${1-.}?”

    The syntax ${1-.} expands to . if $1 is unset, but expands like $1 if $1 is set—even if $1 is set to the empty string.

    The syntax ${1:-.} expands to . if $1 is unset or is set to the empty string. It expands like $1 only if $1 is set to something other than the empty string.

  3. “Why can't I do this? DIR="$1" || '.'

    Because this is bash, not perl or ruby or some other language. (Pardon my snideness.)

    In bash, || separates entire commands (technically it separates pipelines). It doesn't separate expressions.

    So DIR="$1" || '.' means “execute DIR="$1", and if that exits with a non-zero exit code, execute '.'”.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • just a nitpick. Your first example won't work under `set -u`, when no argument is given. Perhaps https://stackoverflow.com/a/18448624/1190453 is a better example. – danbst Jan 22 '20 at 21:06
5

How about this:

DIR=.
if [ $# -gt 0 ]; then
  DIR=$1
fi

$# is the number of arguments given to the script, and -gt means "greater than", so you basically set DIR to the default value, and if the user has specified an argument, then you set DIR to that instead.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • 1
    This is not horrible, though four lines definitely seems too verbose for my liking. I'd rather just use the hyphen var-expansion since I already know what it means at this point. – Steven Lu Apr 18 '13 at 05:05
1

I use a simple helper function to make such assignments look cleaner. The function below accepts any number of arguments, but returns the first one that's not the empty string.

default_value() {
    # Return the first non-empty argument
    while [[ "$1" == "" ]] && [[ "$#" -gt "0" ]]; do
        shift
    done
    echo $1
}
x=$(default_value "$1" 0)
icosabit
  • 36
  • 1