Linux if and operator

How to combine AND and OR condition in bash script for if condition?

I was trying to combine logical AND & OR in a bash script within if condition. Somehow I am not getting the desired output and it is hard to troubleshoot. I am trying to validate the input parameters passed to a shell script for no parameter and the first parameter passed is valid or not.

if [ "$#" -ne 1 ] && ([ "$1" == "ABC" ] || [ "$1" == "DEF" ] || [ "$1" == "GHI" ] || [ "$1" == "JKL" ]) then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" exit 1 fi 

3 Answers 3

The immediate problem with your statement is one of logic: you probably meant to write:

if [ "$#" -ne 1 ] || ! ([ "$1" = "ABC" ] || [ "$1" = "DEF" ] || [ "$1" = "GHI" ] || [ "$1" = "JKL" ]) then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2 exit 1 fi 

That is: abort, if either more than 1 argument is given OR if the single argument given does NOT equal one of the acceptable values.

Note the ! to negate the expression in parentheses and the use of the POSIX-compliant form of the string equality operator, = (rather than == ).

However, given that you’re using Bash, you can make do with a single [[ . ]] conditional and Bash’s regular-expression matching operator, =~ :

if [[ $# -ne 1 || ! $1 =~ ^(ABC|DEF|GHI|JKL)$ ]] then echo "Usage: ./myscript.sh [ABC | DEF | GHI | JKL]" >&2 exit 1 fi 

If POSIX compliance is not required, [[ . ]] is preferable to [ . ] for a variety of reasons. In the case at hand, $# and $1 didn’t need quoting, and || could be used inside the conditional.

Note that =~ as used above works in Bash 3.2+, whereas the implicit extglob syntax used in anubhava’s helpful answer requires Bash 4.1+;
in earlier versions you can, however, explicitly enable (and restore to its original value after) the extglob shell option: shopt -s extglob .

Источник

An «and» operator for an «if» statement in Bash

I’m trying to create a simple Bash script to check if the website is down and for some reason the «and» operator doesn’t work:

#!/usr/bin/env bash WEBSITE=domain.example SUBJECT="$WEBSITE DOWN!" EMAILID="an@email.example" STATUS=$(curl -sI $WEBSITE | awk '/HTTP\/1.1/ < print $2 >') STRING=$(curl -s $WEBSITE | grep -o "string_to_search") VALUE="string_to_search" if [ $STATUS -ne 200 ] && [[ "$STRING" != "$VALUE" ]]; then echo "Website: $WEBSITE is down, status code: '$STATUS' - $(date)" | mail -s "$SUBJECT" $EMAILID fi 
if [ $STATUS -ne 200 ] -a [[ "$STRING" != "$VALUE" ]] 

Could you please be more precise as to what «doesn’t work» ? Do you have a specific error message, or does is simply not provide the expected output ?

-a has duplicity. When used with the Bourne shell style test command, a.k.a. [ , the it means and . When used as a conditional expression then it is testing to see if a file exists. Yes it is confusing, best avoided.

Читайте также:  Rx 580 drivers linux

5 Answers 5

What you have should work, unless $ is empty. It would probably be better to do:

if ! [ "$" -eq 200 ] 2> /dev/null && [ "$" != "$" ]; then 

It’s hard to say, since you haven’t shown us exactly what is going wrong with your script.

Personal opinion: never use [[ . It suppresses important error messages and is not portable to different shells.

If STATUS is empty, the code from @HTF would have failed on -ne: unary operator expected . In your case, it will fail on integer expression expected , won’t it ?

I understand that. But you highlight the issue that $STATUS might be empty and suggest a solution (quoting it). Your solution still fails with an empty STATUS , that is all I meant.

@jvivenot You have a point. (My response to your comment was made before you edited your comment, when your comment merely read «the code . would have failed». A simple solution is to use $

Yes, but Google’s Shell Style Guide is misnamed, because it is a style guide for bash. If you care about portability (and you should), you cannot use [[

Further, consider the (lack of) error message generated by [[ in a=»this is not an integer»; test «$a» -ge 0; [[ $a -ge 0 ]]; IMO, the error message is useful, and [[ incorrectly suppresses it. Also, [[ returns 0, while test returns 1. IMO, this makes [[ unusable.

if [ $STATUS -ne 200 -a "$STRING" != "$VALUE" ]; then 

Looks like it works better in some cases. The && operator does not look to work with shell substitution and with two &&. I would suggest trying this option first.

The «-a» operator also doesn’t work:

if [ $STATUS -ne 200 ] -a [[ «$STRING» != «$VALUE» ]]

For a more elaborate explanation: [ and ] are not Bash reserved words. The if keyword introduces a condition to be evaluated by a process 1 . The condition is true if the process’s exit status is 0 , or false otherwise).

For use as condition evaluation processes, there is the test program ( man test ), which is a program that lets you evaluate simple conditions, like file existance tests, string tests, and combinations thereof using e.g. -a and -o logical operator arguments.

As some find lines like if test -f filename; then foo bar; fi , etc. annoying, there is also a program called [ which is in fact a symlink to the test program. When test is called as [ , it expects to find ] as an additional terminating argument — this syntax is made up by the test program and bash is not aware of it at all.

So if test -f filename is basically the same (in terms of processes spawned) as if [ -f filename ] . In both cases the test program will be started, and both processes should behave identically.

Here’s your mistake: if [ $STATUS -ne 200 ] -a [[ «$STRING» != «$VALUE» ]] will parse to if + some process invocation. In other words the [ program will be run with arguments $STATUS , -ne , 200 , ] , -a , [[ , «$STRING» , != , «$VALUE» , ]] 2 . Now note that there is a ] somewhere in the middle of the argument list passed to [ , and also there is a ] missing at the end of the list. This is why the program will complain.

Читайте также:  Эмуляторы для linux ubuntu

Also note, in constrast to the [ program, [[ is indeed a special, «reserved» word in the shell’s syntax (it’s a bash extension, not standardized in POSIX sh ). But it’s only recognized as a special word if it’s the first word in a command. In your example, the [[ occurs later in the command and so will be parsed as a normal argument, literally the string «[[» and be passed as part of the argument list to the invocation of the [ program.

1 Actually it need not be a single process but can be a job, a combination of processes connected by | , && , || etc..

2 Not literally these arguments — the shell will do variable substitution for $STATUS etc.

First, you don’t need to capitalize your variable names. In general, all-caps variable names should be reserved for ones that are being export ed into the environment for the benefit of other executables.

Second, what you have with && should work, but is not very resilient in the face of a status variable that might be empty or non-numeric.

Finally, the -a attempt didn’t work because -a is part of the syntax of test / [ . ] ; it goes inside the brackets.

Fundamentally, the most important thing to know about conditionals in the shell is that they are just commands. This if expression:

if foo; then echo true else echo false fi 

Does exactly the same thing as this one:

foo if [ $? -eq 0 ]; then echo true else echo false fi 

So the thing after the if (or while or until ) can be any command whatsoever.

That’s the main thing that a lot of beginning shell programmers miss. The shell doesn’t have expressions per se, just commands to run.

However, because you so often want to perform comparisons and other operations that would be Boolean expressions in regular non-shell programming languages, there is a special command that you can run to do those. It used to be a separate binary, but these days it’s built into the shell. Its real name is test , but it can also be invoked as [ , in which case it will complain if its last argument is not a matching ] . The arguments inside the brackets or after test constitute an expression to evaluate, and the test command exits with status 0 if the expression is true, and nonzero if it’s false. So it turns the sort of thing that is normally what an if statement expects in other programming languages into a command that you can run, effectively translating it for the shell.

The appearance of [ . ] can be misleading; it really is parsed just as if it were spelled test , with the same command-line processing as any regular shell command. That means that if you try to compare things using < or >they’ll be interpreted as redirects. Try to use ( and ) for grouping and you’ll be creating a subshell (and probably generating a syntax error by doing so in the middle of a command’s arguments). Try to combine expressions with && and || inside the brackets and you’ll be terminating the command early and again triggering a syntax error.

Читайте также:  Running linux usb stick

You can still use && and || outside of the brackets; at that point you’re just running multiple instances of the test command instead of just one. But you can also use -a and -o as long as they’re inside the brackets. So these two pipelines produce the same result:

[ "$status" -ne 200 -a "$string" != "$value" ] [ "$status" -ne 200 ] && [ "$string" != "value" ] 

The difference is that the first one is all checked by one instance of the test command, while the second runs two of them.

The Korn Shell introduced a new version of the test command spelled with two brackets instead of one, between which lies a special syntactic environment that is not parsed the same as a regular command. Between the double brackets you can safely use unquoted parameter expansions, unquoted parentheses for grouping, < and >for string comparison (stick to -lt and -gt to compare numerically), and && and || as Boolean operators. [[ . ]] also adds the ability to match against glob patterns ( [[ foo == f* ]] ) and regular expressions ( [[ foo =~ ^f ]] ).

Modern shells such as Bash and Zsh have inherited this construct from Ksh, but it is not part of the POSIX specification. If you’re in an environment where you have to be strictly POSIX compliant, stay away from it; otherwise, it’s basically down to personal preference.

Note that the arithmetic substitution expression $(( . )) is part of POSIX, and it has very similar rules to [[ . ]] . The main difference is that equality and inequality tests (which here produce a numeric value, 0 for false and 1 for true) are done numerically instead of stringwise: [[ 10 < 2 ]] is true, but $(( 10 < 2 )) produces 0.

Here again, modern shells have expanded upon the POSIX spec to add a standalone $ -less version of (( . )) that functions as basically an autoquoted let command, which you can use in if expressions if you’re dealing with numbers. So your first condition could also be written (( status != 200 )) . Again, outside of having to stick to POSIX, that’s down to personal preference. I like using (( . )) whenever I’m dealing with numbers, but others prefer to just stick to [ or [[ and use the numeric operators like -lt .

So for what it’s worth, here’s how I’d rewrite your code:

website=domain.example subject="$website DOWN!" email=an@email.example value="string_to_search" status=$(curl -sI "$website" | awk '/HTTP\/1.1/ < print $2 >') string=$(curl -s "$website" | grep -o "$value") if (( status != 200 )) && [[ $string != $value ]]; then mail -s "$subject" "$email"  

Источник

Оцените статью
Adblock
detector