4

I'm limited to Bash.
I want to echo one of two strings, depending on whever a parameter is set, or not.
Let's say

if $ARG is notset|empty then print X
if $ARG is set&nonempty then print V

I'm totally not interested in $ARG contents. Just if it is provided&filled or not.

I know I can get this outcome by IF/CASE/etc. But I'll use this construct quite a few times, and I want to keep it concise. I know I can do it in whatever way and wrap it into a function, but I'm curious if it can be made in some smart way i.e. with parameter expansion?

I've read through https://tldp.org/LDP/abs/html/parameter-substitution.html and a few similar pages, and I noticed that there are two similar, seemingly complementary operations:

default value:      ${ARG:-X}
alternative value:  ${ARG:+V}

This is where the question title comes from by the way. :- returns X when empty, :+ returns V when nonempty.

A simple:

echo ${ARG:+V}${ARG:-X}

does almost what I want. Prints out X when $ARG is empty or not set, but in any other case, say ARG=foo, it prints out Vfoo. That's because :+ and :- aren't really complementary in the way I want ;) To get it working with simple concatenation like ${}${} I'd need an 'inverse' :+ that outputs 'default' when empty, or outputs nothing (not: the tested variable).

Seems I'd need some monster compund command like ${ARG:+V:-X} (which doesn't work, since it's :+ with V:-X as the alt_value), that does this in a one go, chooses between two given values according to the state of the parameter, and doesn't really use the actual text that parameter holds.

I naively tried nesting

echo ${${ARG:+V}:-X}

which would do exactly what I needed, and it didn't work in Bash, since it's parameter expansion, and the inner ${ARG:+V} isn't a parameter, so the outer ${} complains. (~ Can command substitution be nested in variable substitution?)

I thought I could try fixing up Vfoo by cutting it down to first character ${xxx:0:1}, but then, this would require nesting again ${${ARG:+V}${ARG:-X}:0:1}, so no-go.

Or a helper variable, and in this case it's fine:

MINGW64 ~
$ f () { T=${1:+V}${1:-X} ; echo ${T:0:1} ; } ;

$ f "a"
V

$ f ""
X

$ f
X

This is as close as I could get, and even has the behavior I wanted.
Feels a bit dirty, since it actually constructs arbitrarily-long string T just to get its first char. Pure waste.
Also I still would like to squash it into a single expression, without a temporary variable T.

Did I miss something in Bash? Can I somehow get this 'choice' behavior in a better way with parameter expansions, or any other built-in tricks?

3
  • 1
    Are you looking for if [[ -z "$ARG" ]]; then echo X; else echo Y; fi?
    – muru
    Oct 6, 2022 at 10:29
  • @muru in terms of behavior, yes. But not in terms of writing. I didn't want to use explicit if/then/else or case, since they are so much more to type. I wrote that in 4th para. I focused on parameter expansion specifically because it offers some succint ops like $(X:-Y) that are relatively easy to type, less chars, less typos, especially when typed ad-hoc on commandline. And since there already are :- and :+, which are so close, it feels like I overlooked something Oct 6, 2022 at 10:32
  • 1
    Considering what you have in f, this isn't all that worse. It could be shortened to something like [ -z "ARG" ] && echo X || echo Y
    – muru
    Oct 6, 2022 at 10:54

1 Answer 1

2

You can do something like:

either=(
  'value for unset or empty'
  'value for set and non-empty'
)
printf '%s\n' "${either[!!${#VAR}]}"

(disable history expansion (set +o histexpand) if trying from an interactive bash invocation).

${#VAR} expands to the number of characters in the value of $VAR, !! is double negation, !!${#VAR} as an arithmetic expression yields 0 if the variable is empty or unset and 1 otherwise.

With zsh, you can do:

print -r -- ${${var:+value for set and non-empty}:-value for unset or empty}
1
  • Arrays! Thank you very much. Yep, I already read that with zsh I can nest these expressions, but it has to be bash. But I forgot that I could just index the array with bool result. It still needs a temp variable for the array, but at least it's now something trivial like ST=(X V) . Thanks! Oct 6, 2022 at 12:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .