# Creating SH function: 'defined_empty'



## Seeker (Feb 2, 2011)

This works!

```
# var exists
if [ "${var-NON_EXISTING}" != 'NON_EXISTING' ]; then
    # and is empty
    [ ! "$var" ] && echo "Var \$var exists and is empty"
fi
```

Problem is, when I wana put it, in a function, which takes one arg, that is a name of a variable, to be checked.
This part of code, inside a function, always fails:

```
... "${$@-NON_EXISTING}" ...
```
As well as:

```
... "${"$@"-NON_EXISTING}" ...
```


```
... "${`echo $@`-NON_EXISTING}" ...
```


```
... "${"`echo $@`"-NON_EXISTING}" ...
```
Anyone?


----------



## DutchDaemon (Feb 2, 2011)

To test for non-existing (empty) variables I usually use something like:


```
if [ x${var} = "x" ]
```
 or 
	
	



```
if [ x${var} != "x" ]
```
 depending on how you frame your condition, of course.


----------



## SirDice (Feb 2, 2011)

Yes, that's how I normally do it too. But I think Seeker is passing the variable's name as an argument to the function and he's trying to test the value of the passed variable in the function.

This might be helpful: http://www.grymoire.com/Unix/Sh.html#uh-92


----------



## gordon@ (Feb 6, 2011)

Or something like


```
check-isset() {
  if [ -z "$1" ]; then
    return 1
  else
    return 0
  fi
}

check-isset $foo
```

I have to ask though, since you'll have to use check-isset inside of an if block anyway, why not just do:


```
if [ -z "$foo" ]; then
  echo "foo is not set"
fi
```


----------



## Seeker (Feb 6, 2011)

Guys!
*2 conditions* have to be met, for function to return 'true' (0)
1) Variable must exist (defined)
*AND*
2) Variable mustn't have any value -> NULL string (empty) -> *var=*

Anything else, returns 'false'.


```
# var exists
if [ "${var-NON_EXISTING}" != 'NON_EXISTING' ]; then
    # and is empty
    [ ! "$var" ] && echo "Var \$var exists and is empty"
fi
```
... achieves this_!_

Problem is putting it, inside of a function, as I need variable variable, for this, as var's name is passed to function to check is it defined *AND* empty.

ATM I've figured I can use *eval* to deal with variable variables


----------



## phoenix (Feb 6, 2011)

Does using $1 (instead of $var) inside the function work?

When the function is called as *defined_empty $myvar*, $myvar is passed in as the first positional argument, aka $1.

Although I think that passes a copy of $myvar, and not a reference to it, so you are testing whether or not $1 is defined/empty, and not $myvar.


----------



## gordon@ (Feb 7, 2011)

Seeker said:
			
		

> Guys!
> *2 conditions* have to be met, for function to return 'true' (0)
> 1) Variable must exist (defined)
> *AND*
> 2) Variable mustn't have any value -> NULL string (empty) -> *var=*



I guess I'll step back and ask, why are you doing this? I've been shell programming a long time and have never needed to know this.


----------



## gordon@ (Feb 7, 2011)

Seeker said:
			
		

> ```
> # var exists
> if [ "${var-NON_EXISTING}" != 'NON_EXISTING' ]; then
> # and is empty
> ...



You have a bug in your code if this is what you are using. It should be:


```
if [ "${var:-NON_EXISTING}" != 'NON_EXISTING' ]; then
    [ -z "$var" ] && echo 'Var $var exists and is empty'
fi
```

Which doesn't work by the way. I tested it with an unset variable, a variable existent but empty, and a set variable; it never prints anything.

As far as I know, there is no way to detect a variable exists short of doing something like:


```
if set | grep -q '^var=$'; then
    echo 'Var $var exists and is empty'
fi
```

But again, I would still ask what you are trying to accomplish.


----------



## Seeker (Feb 7, 2011)

You've declared a bug in my code, just by looking at it and without an attempt, to test it.
Then, you've just edited it and *then* tested it, for a first time.
In that case, I completely agree, with you:


> ... Which doesn't work by the way. I tested it with an unset variable, a variable existent but empty, and a set variable; it never prints anything.


And reason for that is, that your "fix", injected bugs in my code.


So, if you test *my unedited code*, you'll see that only:

```
var=
```
will indeed produce output, while

```
#var=
```
and

```
var=4657
```
*won't output anything.*

Colon colored in red is your bug, which makes a major difference here:

```
if [ "${var[color="Red"][B]:[/B][/color]-NON_EXISTING}" != 'NON_EXISTING' ]; then
```
Symbol *-* checks only for undefined/unset vars, but once you add:
Symbol *:* then you are additionally checking for null value.


Anyway, you've asked, why am I doing this.
I need it for use in *in_sum* function, which checks if script is executing in a single user mode and it does that by checking $HOST variable (defined and empty).
I also have a script which always setups my SUM, just a way I like it and of all env vars, only $HOST remains intact. (i.e; HOME doesn't exists in SUM, but my script sets it afterwards, etc ...)

Once I hit ctrl+d, to enter multi user mode or reboot, then $HOST variable becomes non empty.


----------



## gordon@ (Feb 7, 2011)

You're right about the hyphen thing. It's completely undocumented for both sh and bash. Since I was going off of the manpages, I figured you meant :- since it's sorta similar. Silly me using the documentation.

Personally, I'd probably use the set method described as it gives you both in a single statement at the expense of a single fork.


----------



## wblock@ (Feb 7, 2011)

Seeker said:
			
		

> I need it for use in *in_sum* function, which checks if script is executing in a single user mode and it does that by checking $HOST variable (defined and empty).



Seems like there would be an easier way to check that, like a sysctl or even just

```
if pgrep -q getty ; then
  echo "multi-user"
else
  echo "single user"
fi
```

Could probably use a lot of ordinary processes instead of getty, just the first thing that came to mind.


----------



## Seeker (Feb 8, 2011)

Yes, but my script starts up a lot of processes in SUM, so I can't rely on that.

@gordon - Yep! Your code is shorter and simpler.
But this is a mission critical function, so it must be as independent as possible(depends on itself -> /bin/sh), as SUM is associated with bad things that happened to sys, so ask god what is damaged/available and what isn't.


----------



## gordon@ (Feb 8, 2011)

Seeker said:
			
		

> @gordon - Yep! Your code is shorter and simpler.
> But this is a mission critical function, so it must be as independent as possible(depends on itself -> /bin/sh), as SUM is associated with bad things that happened to sys, so ask god what is damaged/available and what isn't.



If you are that concerned about it, how about some code that requires no forks:

```
defined_empty() {
set | while read line
do
  case $line in
  "$1=") echo "\$$1 is set and empty" ;;
  esac
done
}
```

And then to run it:

```
# var=
# defined_empty var
$var is set and empty
```


----------



## Seeker (Feb 8, 2011)

Hey, thx gordon!
Here is a little fix:

```
defined_empty() {
set | while read line
do
  case $line in
  "$1=[color="Red"][B][SIZE="3"]''[/SIZE][/B][/color]") echo "\$$1 is set and empty in line $line";;
  esac
done
}
```


----------



## gordon@ (Feb 8, 2011)

Interesting, bash uses var= while sh uses var=''. Might be a good idea to catch both cases.


----------



## jilles@ (Feb 10, 2011)

Code based on 
	
	



```
set | while read ...
```
 is probably both slow and unreliable. It forks twice and processes a lot of data that is not relevant. Also, it may give wrong answers if there is a variable containing <newline>var=<newline>.

Instead, try
	
	



```
[ ${var+set} ] && ! [ ${var:+setwithval} ]
```

If you want this in a function that is passed the variable name, for some reason, you will need to use eval.

The colon for the +-=? expansions is documented below them in sh(1):


> In the parameter expansions shown previously, use of the colon in the
> format results in a test for a parameter that is unset or null; omission
> of the colon results in a test for a parameter that is only unset.


----------



## qsecofr (Feb 10, 2011)

Is there a sysctl variable that indicates whether the system is in single or multi-user boot mode?


----------

