Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Checking Variables

48 views
Skip to first unread message

James

unread,
Jul 26, 2016, 12:28:36 PM7/26/16
to
In bash,

if [ "$A" -a "$B" ]; then

James

unread,
Jul 26, 2016, 12:34:07 PM7/26/16
to
Sorry, I pressed "TAB" and return? and it got posted unintentionally.

On Tuesday, July 26, 2016 at 9:28:36 AM UTC-7, James wrote:
In bash,

if [ "$A" -a "$B" ]; then
do_something
fi

This seems to fail if $A or $B is non-existent or empty. What is the correct way?

Using [[ "$A" -a "$B" ]] is better always?

TIA
James

Janis Papanagnou

unread,
Jul 26, 2016, 12:41:12 PM7/26/16
to
On 26.07.2016 18:34, James wrote:
[...]
> In bash,
>
> if [ "$A" -a "$B" ]; then
> do_something
> fi
>
> This seems to fail if $A or $B is non-existent or empty. What is the correct way?

The test operator -a tests for files, it's an unary operator.

What do you want to achieve in the first place?

>
> Using [[ "$A" -a "$B" ]] is better always?

$ bash -c '[[ "$A" -a "$B" ]]'
bash: -c: line 0: conditional binary operator expected
bash: -c: line 0: syntax error near `-a'
bash: -c: line 0: `[[ "$A" -a "$B" ]]'


Generally, if you want standard conformance then avoid [[...]] and use [...].
But if you are working generally only with a contemporary advanced shell
(bash, ksh, zsh) then [[...]] is preferable.

Janis

>
> TIA
> James
>

Kaz Kylheku

unread,
Jul 26, 2016, 9:24:16 PM7/26/16
to
On 2016-07-26, Janis Papanagnou <janis_pa...@hotmail.com> wrote:
> On 26.07.2016 18:34, James wrote:
> [...]
>> In bash,
>>
>> if [ "$A" -a "$B" ]; then
>> do_something
>> fi
>>
>> This seems to fail if $A or $B is non-existent or empty. What is the correct way?
>
> The test operator -a tests for files, it's an unary operator.

Ther is a binary one, a Boolean AND: EXPR -a EXPR.

$ [ "" -a "" ] && echo true
$ [ "" -a "y" ] && echo true
$ [ "y" -a "" ] && echo true
$ [ "y" -a "y" ] && echo true
true

Indeed, the condition fails if $A or $B is non-existent or empty, or both are.

In the test syntax, here is a hack in that an empty string is false,
and a non-empty string is true (unless it looks like a predicate
operator, which "y" does not).

If A and B could contain anything, including something that looks like
a test operator, then we better use:

[ -n "$A" -a -n "$B" ]

I.e. use the -n predicate to explicitly test an argument for not
empty. Here, A can contain "-z" or whatever without causing confusion.

Also, if the test syntax is completely empty (no expression at all),
that is a false:

$ [ ] && echo true

Any word that doesn't look like an operator is true:

$ [ y ] && echo true
true

Thus, if you have a disciplined boolean variable V which is either
empty/unset or else contains a well-behaved representation of Boolean
true like "y", you can use this shorthand:

if [ $V ] ; then # Look, Ma, no double quotes or -n operator!
echo V is true
fi

Chris F.A. Johnson

unread,
Jul 26, 2016, 10:33:07 PM7/26/16
to
What are you trying to test?

The binary -a operator tests whether both are true; if either is
empty, it is not true.

--
Chris F.A. Johnson

Rakesh Sharma

unread,
Jul 27, 2016, 12:26:26 AM7/27/16
to
On Tuesday, 26 July 2016 22:04:07 UTC+5:30, James wrote:

>
> In bash,
>
> if [ "$A" -a "$B" ]; then
> do_something
> fi
>
> This seems to fail if $A or $B is non-existent or empty. What is the correct way?
>


if [ "${A:++}" -a "${B:++}" ]; then
do_something
fi

Geoff Clare

unread,
Jul 27, 2016, 8:41:06 AM7/27/16
to
James wrote:

> In bash,
>
> if [ "$A" -a "$B" ]; then
> do_something
> fi
>
> This seems to fail if $A or $B is non-existent or empty. What is the correct way?
>
> Using [[ "$A" -a "$B" ]] is better always?

When using "test" (aka "[ ... ]"), it's always better to use the shell's
&& operator rather than the -a binary operator:

[ "$A" ] && [ "$B" ]

or less cryptically:

[ -n "$A" ] && [ -n "$B" ]

In "[[ ... ]]" the equivalent of test's "-a" is "&&", so you can do either:

[[ -n "$A" && -n "$B" ]]

or:

[[ -n "$A" ]] && [[ -n "$B" ]]

to achieve the same thing. However, using "test" or "[ ... ]" is more
portable because "[[ ... ]]" is not (yet) in POSIX.

--
Geoff Clare <net...@gclare.org.uk>

Kaz Kylheku

unread,
Jul 27, 2016, 9:42:07 AM7/27/16
to
On 2016-07-27, Geoff Clare <ge...@clare.See-My-Signature.invalid> wrote:
> James wrote:
>
>> In bash,
>>
>> if [ "$A" -a "$B" ]; then
>> do_something
>> fi
>>
>> This seems to fail if $A or $B is non-existent or empty. What is the correct way?
>>
>> Using [[ "$A" -a "$B" ]] is better always?
>
> When using "test" (aka "[ ... ]"), it's always better to use the shell's
> && operator rather than the -a binary operator:
>
> [ "$A" ] && [ "$B" ]

No, it isn't. It's more verbose, and basically involves running separate
test commands rather than keeping everything in one expression.

The && and || operators do not obey the usual associativity and
precedence, so that

[ "$A" ] && [ "$B" ] || [ "$C" ] && [ "$D" ]

does not mean what it looks like it means.

&& is useful if the expression on the right has side effects that
we want to suppress based on the outcome of the left.

> or less cryptically:
>
> [ -n "$A" ] && [ -n "$B" ]
>
> In "[[ ... ]]" the equivalent of test's "-a" is "&&", so you can do either:
>
> [[ -n "$A" && -n "$B" ]]

So you like -a if it is spelled &&.

Janis Papanagnou

unread,
Jul 27, 2016, 11:26:50 AM7/27/16
to
For [...] it may be the same, but [[...]] does not seem to support -a .

Wasn't there an issue with some systems that -a may not be defined for older
test(1) commands, and that this is one reason to prefer composing test(1)
commands instead of test arguments if portability matters?

WRT the performance issue; in newer shells there's a good chance that test(1)
is a built-in and no separate process.

Glad to hear from Geoff that [[...]] might eventually enter POSIX. With all
the quirks of [...] to consider I anyway prefer the syntactical construct.

Janis

Ian Zimmerman

unread,
Jul 27, 2016, 1:50:05 PM7/27/16
to
On 2016-07-27 01:24 +0000, Kaz Kylheku wrote:

> If A and B could contain anything, including something that looks like
> a test operator, then we better use:
>
> [ -n "$A" -a -n "$B" ]
>
> I.e. use the -n predicate to explicitly test an argument for not
> empty. Here, A can contain "-z" or whatever without causing confusion.
>
> Also, if the test syntax is completely empty (no expression at all),
> that is a false:
>
> $ [ ] && echo true
>
> Any word that doesn't look like an operator is true:
>
> $ [ y ] && echo true
> true
>
> Thus, if you have a disciplined boolean variable V which is either
> empty/unset or else contains a well-behaved representation of Boolean
> true like "y", you can use this shorthand:
>
> if [ $V ] ; then # Look, Ma, no double quotes or -n operator!
> echo V is true
> fi

This kind of madness is why I religiously avoid both the test command
and its punned variants.

If A and B could _really_ contain anything, I'd write this as:

case "${A:-}" in
()
;;
(*)
case "${B:-}" in
()
;;
(*)
echo true
;;
esac
esac

If (as is more likely), some punctuation character (such as ':') can be
safely assumed not to occur, I'll write:

case "${A:-}:${B:-}" in
(?*:?*)
echo true
;;
esac

--
Please *no* private Cc: on mailing lists and newsgroups
Why does the arrow on Hillary signs point to the right?

Janis Papanagnou

unread,
Jul 27, 2016, 2:17:26 PM7/27/16
to
On 27.07.2016 19:48, Ian Zimmerman wrote:
>> [...]
>
> This kind of madness is why I religiously avoid both the test command
> and its punned variants.
>
> If A and B could _really_ contain anything, I'd write this as:
>
> case "${A:-}" in
> ()
> ;;
> (*)
> case "${B:-}" in
> ()
> ;;
> (*)
> echo true
> ;;
> esac
> esac
>
> If (as is more likely), some punctuation character (such as ':') can be
> safely assumed not to occur, I'll write:
>
> case "${A:-}:${B:-}" in
> (?*:?*)
> echo true
> ;;
> esac

Why did you use the empty default assignment (and the quotes)? - Seems to
be unnecessary. case ${A}:${B} in (*?:?*) echo true ;; esac works for
me with spaces, empty, or non-existing arguments, in ksh, bash, zsh, dash.

Janis

Rakesh Sharma

unread,
Jul 27, 2016, 3:14:22 PM7/27/16
to
Your construct, viz.,

case ${A}:${B}
(*?:?*) echo true;;
esac

is faulty for 2 reasons.
a) It would sputter to a halt when run with
the '-u' option turned on AND either of
the shell variables 'A' or 'B' is undef-
ined.

b) Assuming we ameliorate that with
case ${A-}:${B-}
(*?:?*) echo true;;
esac

Now if say A were null (A=) & B contained
atleast the following string of 3 chars,
.:., where a . implies any char, then your
construct would give out a false positive.
Hence, it's not a very reliable of checkin
the AND of 2 variables.

The foolproof way to it is as under

case ${A:++}${B:++} in ??) echo true;; esac

Janis Papanagnou

unread,
Jul 27, 2016, 5:46:08 PM7/27/16
to
It's noteworthy that shells behave differently when using it with -u .

But I cannot reproduce what you say. With -u ksh does not mind at all, and
bash will report only an unset variable. In other words, you get what you
asked for; if you want to abort (-u) and provide an uninitialized variable,
and not abort if you omit -u or provide only defined or empty variables.

>
> b) Assuming we ameliorate that with
> case ${A-}:${B-}
> (*?:?*) echo true;;
> esac
>
> Now if say A were null (A=) & B contained
> atleast the following string of 3 chars,
> .:., where a . implies any char, then your
> construct would give out a false positive.
> Hence, it's not a very reliable of checkin
> the AND of 2 variables.

This is already covered by the previous poster's prerequisite; he defined
the prerequisite: "If [...] some punctuation character (such as ':') can be
safely assumed not to occur".

Janis

Cary

unread,
Jul 28, 2016, 2:33:07 AM7/28/16
to
James wrote:
> In bash,
>
> if [ "$A" -a "$B" ]; then
>
true;
fi



Geoff Clare

unread,
Jul 28, 2016, 8:41:06 AM7/28/16
to
Kaz Kylheku wrote:

> On 2016-07-27, Geoff Clare <ge...@clare.See-My-Signature.invalid> wrote:
>> James wrote:
>>
>>> In bash,
>>>
>>> if [ "$A" -a "$B" ]; then
>>> do_something
>>> fi
>>>
>>> This seems to fail if $A or $B is non-existent or empty. What is the correct way?
>>>
>>> Using [[ "$A" -a "$B" ]] is better always?
>>
>> When using "test" (aka "[ ... ]"), it's always better to use the shell's
>> && operator rather than the -a binary operator:
>>
>> [ "$A" ] && [ "$B" ]
>
> No, it isn't. It's more verbose, and basically involves running separate
> test commands rather than keeping everything in one expression.

Running separate test commands isn't a problem, as it's a shell built-in
in all modern shells.

Using && is better because it avoids the problems that can occur when
"test" treats an expanded variable as a special value but that wasn't
intended. If you never use -a or -o then the POSIX number-of-arguments
rules ensure that "test" always does the right thing even if a variable
expands to something that would otherwise be special. As soon as you
use -a or -o, all bets are off. That's why POSIX has marked -a and -o
as obsolescent (and they were always optional in POSIX anyway, although
mandated for UNIX conformance).

An obvious problem case for the command in question is if "$A"
expands to "!", in which case bash will do a negated file existence
test for "$B".

> The && and || operators do not obey the usual associativity and
> precedence, so that
>
> [ "$A" ] && [ "$B" ] || [ "$C" ] && [ "$D" ]
>
> does not mean what it looks like it means.

That's true, but if you use multiple -a and -o operators with "test",
you increase the chances of the above mentioned problems.

>> or less cryptically:
>>
>> [ -n "$A" ] && [ -n "$B" ]
>>
>> In "[[ ... ]]" the equivalent of test's "-a" is "&&", so you can do either:
>>
>> [[ -n "$A" && -n "$B" ]]
>
> So you like -a if it is spelled &&.

In [[ ... ]], && and || don't have the problems that -a and -o have
in "test", because for [[ ... ]] the shell parses the expression
_before_ it expands variables.

--
Geoff Clare <net...@gclare.org.uk>
0 new messages