5

I was wondering if there's a shorthand for this kind of stuff.

Currently I can do.

var_empty=; [ -n "$var" ] || var_empty=1; #intermediary variable
echo "REPL_if_var_empty_otherwise_empty=${var_empty:+REPL}"

Is this doable without the intermediary?

I tried

sh -c 'readonly SAME=SAME; var=; echo test0=${var:-SAME} test1=${SAME:+REPL}; echo REPL_if_var_empty_otherwise_empty=${${var:-SAME}:+REPL}'

but this results in a bad substitution error in the last echo (test0=SAME test1=REPL). Why is that? Is there another way?

5
  • If I understand it correctly you want var_empty=; [[ -n $var_empty ]] || var_empty="${var_empty:=REPLY}". In your case "${var_empty:+REPL}" nothing is set if var_empty is null or unset. Maybe this will help you more mywiki.wooledge.org/BashGuide/Parameters Nov 2, 2018 at 10:27
  • 1
    Maybe I don't understand what you're after, but what about: test "$var" && var= || var=was_empty. There isn't any ${foo?bar:baz} shortcut.
    – user313992
    Nov 2, 2018 at 10:47
  • @mosvy I'll accept that as an answer. I perversely love asking "is-there-a" questions, but perhaps a no in an answer will help future wonderers. Anyway, I think the fact that my attempt failed is kind of interesting as far as the shell grammar is concerned. Nov 2, 2018 at 11:06
  • 1
    With zsh, you could do ${${var:-empty$var}%$var} Nov 2, 2018 at 11:19
  • @StéphaneChazelas Nice to know! zsh is doing it right. Nov 2, 2018 at 11:20

2 Answers 2

7

In bash, ksh or zsh in ksh emulation, you could do:

r=empty;output=${r[${#var}]}

In zsh:

output=${${var:-empty$var}%$var}

Otherwise, you can always do

output=;[ "$var" ]||output=empty
5
  • You need a unset r for the first option. Otherwise a previous defined r like: r=(1 2 3) will still be used. Or, shorter: r=(empty).
    – user232326
    Nov 3, 2018 at 6:50
  • Yes, though arrays are not inherited from the environment, so it's just a matter of using a variable that is not otherwise used earlier in the script. Not sure it's worth mentioning . r=(empty) wouldn't work with ksh88. While we're at nitpicking, in bash or mksh, unset wouldn't work either in some contexts as it doesn't unset but peels off a layer of local scope. In bash, you also need unset -v r as otherwise it may unset a r function. The answer also assumes the nounset option has not been set. Nov 3, 2018 at 9:06
  • Who is still using ksh88? But the local scope is what gets used down the line, isn't it? True, better to use unset -v r. The nounset will not be a problem if var has been set before, but yes, that is a point. Which may be solved with var=${var-}, that will change var only if unset, but will remain empty. Or some other workaround.
    – user232326
    Nov 3, 2018 at 9:20
  • @isaac, ksh88 is still the default ksh on some commercial Unices (and often the best shell you get there by defaul). r=(1 2); f() { local r=x; g; }; g() { unset r; echo "${r[1]}"; }; f outputs 2 in bash, mksh (and yash, except you need typeset and array indices start at 1). The problem with nounset is with ${r[${#var}]}, we rely on it being unset for values of ${#var} other than 0 so it expands to the empty string. Nov 3, 2018 at 9:30
  • Please make it 'g(){ local r; ……' There is no need to unset a var from the upstream scope (without local). And yes, I understand the reason (and problem) with nounset and ${#var}, but thanks for your input.
    – user232326
    Nov 3, 2018 at 9:50
0

Not sure if I follow what you mean by "without the intermediary" - if you mean "do it all on one line" or "don't even make variable assignments, do it all directly", but here are methods for each interpretation:

Method 1a - With variable assignments:

$ testVar=; [[ "${testVar}" ]] && testVar="" || testVar="was empty"; echo $testVar

Method 1b - With variable assignments:

testVar=; testVar=$( [ $testVar ] || echo "was empty"); echo $testVar

Method 2 - No assignments:

$ testVar=; [[ "${testVar}" ]] && echo "" || echo "was empty"

Output:

was empty

Bonuses:

Generalization - Pick the pieces you need - no variables required at all:

echo testValue | { echo -n "Original value: "; tee >(read -n 1 && echo "wasn't empty" || echo "was empty"); }

Replace echo testValue with the raw command you would be using to assign your variable.

Generalization - using awk:

echo -n | awk 'BEGIN{print "Original value: "}; d=1; END{if (!d) {print "was empty"} else {print "was not empty"} }'

Again, pick and choose the cases you need (for the OP's question, the original value isn't needed, but that's probably a less-common use-case). If you need to start from a variable, echo it. If you need to assign the result to a variable, use command substitution (wrap the whole thing in $( ... ).

Note: To my knowledge, sed won't be of any help in these cases, because it is 100% line-oriented, and the "empty case" is zero-lines by-definition. Therefore, sed will exit before it has a chance to process anything you tell it to. Also, I'm not very familiar with Perl, but I couldn't figure it out -- though I'm guessing somebody good with Perl could. Bash's features like {var:=param} et al. aren't particularly helpful for the OP's case, but I'll mention them for completeness. I wish they operated on raw strings instead of just named string variables, for the same reason as the OP (I like to avoid intermediates if I don't need them later).

You must log in to answer this question.

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