I have a script that will check for multiple conditions in if statement and run a desired command if its true.
if [ ! -f /tmp/a ] && [ ! -f /tmp/b ]; then
touch /tmp/c else
echo "file exists" fi
I would now need to know which out of the multiple condition was true.
Eg: /tmp/a or /tmp/b which ever existed. Is there a way to get that in my else condition?
Since your if is using a compound condition, there is no way for else to figure out which part of the compound condition failed. You could rewrite your code this way:
a_exists=0
b_exists=0
[[ -f /tmp/a ]] && a_exists=1 # flag set to 1 if /tmp/a exists
[[ -f /tmp/b ]] && b_exists=1 # flag set to 1 if /tmp/b exists
if [[ $a_exists == 0 && $b_exists == 0 ]]; then
touch /tmp/c
else
[[ $a_exists == 1 ]] && echo "a exists"
[[ $b_exists == 1 ]] && echo "b exists"
fi
The above code can be written even more concisely with the Bash arithmetic operator (( ... )):
a_exists=0
b_exists=0
[[ -f /tmp/a ]] && a_exists=1 # flag set to 1 if /tmp/a exists
[[ -f /tmp/b ]] && b_exists=1 # flag set to 1 if /tmp/b exists
if !((a_exists + b_exists)); then
touch /tmp/c
else
((a_exists)) && echo "a exists"
((b_exists)) && echo "b exists"
fi
This smells like one day there will be more than two files to check. Use a loop:
i_am_happy=yeah
for f in a b
do
if [[ ! -f /tmp/$f ]]
then
echo "Criminy! No $f in tmp!" # or what else you would like to do.
i_am_happy=nope
fi
done
[[ i_am_happy == nope ]] && touch /tmp/c
Related
I have written some code that uses the /bin/true by accident and I want to force the usage of the builtin of true and false.. any Idea what is the correct way of doing so?
#!/usr/bin/env bash
set -eEuo pipefail
CONFIRM=false
if [[ $1 == "Y" ]]; then
CONFIRM=true
fi
"${CONFIRM}" || echo "Deletion will not happen, please run with '${0} Y' to confirm"
if $CONFIRM; then
echo "I deleted the stuff!"
fi
To me, this looks like a job for if ... else:
#!/usr/bin/env bash
set -eEuo pipefail
if [[ $# -eq 1 && $1 == Y ]]; then
echo "I deleted the stuff!"
else
echo "Deletion will not happen, please run with '${0} Y' to confirm"
fi
If you need to save the result:
#!/usr/bin/env bash
set -eEuo pipefail
# with the set-mode above, you can set `conform` like this:
[[ $# -eq 1 && $1 == Y ]] && confirm=$? || confirm=$?
# otherwise, you could just set it after the above test
# truth test
[[ $confirm -eq $() ]] || echo "Deletion will not happen, please run with '${0} Y' to confirm"
# truth test again
if [[ $confirm -eq $() ]]; then
echo "I deleted the stuff!"
fi
I'm writing a BASH script to purge the cache from a web server. The script is designed to take arguments from positional parameters. "ShellCheck.net" is telling me that my script is functionally correct, but when I test it I'm getting error where I shouldn't ... so I thought I'd ask for some folks to put fresh eyes on it. Take a look, I'll continue below and describe my problem:
#!/bin/bash
#
# Verify the user running is root, if not, fail.
if [[ "$UID" -ne "0" ]]; #added-1438279711
then
echo 'Only ROOT may run this script.';
exit 1;
fi
#
# Set the variables
BASE="/path/to/folder/foo/bar/" #added-1438279711
DOMAINOPT="$1" #added 1438451428
PATHOPT="$2" #added 1438451428
#
# Define Functions
function usage() { #added-1438382631
echo -en "Proper Usage:\n\n"
echo -en "\tSpecify the domain to be used\n"
echo -en "\tUsage: \"cleancache.sh abc.com\"\n"
echo -en "\t\tNote: This option will search for files and folders, recursively, within the domain folder, and remove them.\n\n"
echo -en "\tSpecify the URI you'd like to act upon within the domain\n"
echo -en "\tUsage: \"cleancache.sh abc.com /path/to/folder/\"\n"
echo -en "\t\tNote: This option will search for files and folders, recursively,\n\t\twithin the specified path, and remove them. Removing a single file is not currently supported with this script.\n\n"
}
#
# Validate the input
if [[ ! -z "$DOMAINOPT" ]] && [[ "$DOMAINOPT" != "^[A-Za-z0-9-]*[\.][a-z]*$" ]] #added-1438462778
then
clear
echo -en "Please follow the proper format for the DOMAIN option\n\n"
usage
exit 1
elif [[ ! -z "$DOMAINOPT" ]] && [[ "$DOMAINOPT" = "^[A-Za-z0-9-]*[\.][a-z]*$" ]]
then
DOMAINOPT="$DOMAINOPT"
else
clear
echo -en "Please enter a domain!\n\n"
usage
exit 1
fi
if [[ ! -z "$PATHOPT" ]] && [[ "$PATHOPT" != "^[\/][\S]*[\/]$" ]] #added-1438456371
then
clear
echo "Please follow the proper format for the PATH option"
usage
exit 1
elif [[ ! -z "$PATHOPT" ]] && [[ "$PATHOPT" = "^[\/][\S]*[\/]$" ]]
then
PATHOPT="$PATHOPT"
else
echo ""
fi
#
# Doing Stuff
if [[ "$#" -gt "2" ]]
then
echo -en "Too many arguments!\n\n"
usage
exit 1
elif [[ "$#" -eq "2" ]]
then
echo "Purging Cache in \"$BASE$DOMAINOPT$PATHOPT\""
find "$BASE""$DOMAINOPT""$PATHOPT" -type d -exec rm -rf {} \;
find "$BASE""$DOMAINOPT""$PATHOPT" -type f -exec rm -f {} \;
echo "Purging Complete"
exit 0
else
echo "Purging Cache in \"$BASE$DOMAINOPT\""
find "$BASE" -type d -name "$DOMAINOPT" -exec rm -rf {} \;
mkdir -p "$BASE$DOMAINOPT" && chown apache:apache "$BASE$DOMAINOPT" && chmod 755 "$BASE$DOMAINOPT"
echo "Purging Complete!"
echo "Creating \".stat\" file"
echo "" > "$BASE""$DOMAINOPT""/.stat"
if [[ -f "$BASE""$DOMAINOPT""/.stat" ]] #added-1438387045
then
echo "$BASE$DOMAINOPT/.stat file created!"
fi
fi
echo "All Operations Complete, exiting now!"
Everything responds normally if you run the script without any arguments (Please enter a domain), It responds normally if you try to enter a path before a domain ... but when I do it correctly, when I type: "cleancache.sh abc.com", I get an error like i haven't met the required pattern ("Please follow the proper format for the DOMAIN option") ... when that is exactly write! ... I don't understand what I'm missing, been banging my head all day, no joy.
PLEASE HELP!
Use this to match a regex:
[[ "$DOMAINOPT" =~ ^[A-Za-z0-9-]*[\.][a-z]*$ ]]
or this:
[[ ! "$DOMAINOPT" =~ ^[A-Za-z0-9-]*[\.][a-z]*$ ]]
Don't quote the regex.
I've bumped into a Nagios check script which has been written by someone who already left my company and there's an operator there which I can't understand it's use.
This is the relevant part from the shell script:
if [[ "$URL" =~ $ACTIVE ]] && [[ "$URL2" =~ $ACTIVE2 ]]; then
echo "OK: $HOST is ACTIVE in the Load Balancer"
exit 0
My question is:
What is this =~?
I've checked about it in the internet and found that it's a bitwise which "Flips the bits in the operand", but I don't understand where and how to use it, can you please elaborate?
Edit #1:
This is the full script:
#!/bin/bash
#Purpose: Checks if proxy is active in the LB
#Date: May 09, 2011
#Variables
HOST=$1
URL=`wget --timeout=60 -w 3 -qO- http://$HOST:8080/proxy/keepalive?file=/workspace/temp/1`
URL2=`wget --timeout=60 -w 3 -qO- http://$HOST:8080/proxy/keepalive?file=/workspace/temp/1.txt`
ACTIVE="1"
ACTIVE2="ppp"
LOG="/tmp/PROXY-LB.log"
#Begin Code
if [ -z $HOST ]; then
echo "You must specify IPADDRESS (e.g. 68.67.174.34)"
exit 3
fi
if [[ "$URL" =~ $ACTIVE ]] && [[ "$URL2" =~ $ACTIVE2 ]]; then
echo "OK: $HOST is ACTIVE in the Load Balancer"
exit 0
else
echo "*** Problem: $HOST is out from the Load Balancer"
echo "`date`: *** HOST $HOST is out from the Load Balancer" >> $LOG 2>&1
exit 2
fi
#END
My question is, couldn't the coder use this (without the ~) instead?
if [[ "$URL" = $ACTIVE ]] && [[ "$URL2" = $ACTIVE2 ]]; then
Edit #2:
Here are some examples I tried:
$ d="hello"
$ [[ "$d" =~ *ll* ]] && echo "yes"
$ [[ "$d" =~ he* ]] && echo "yes"
yes
$ [[ "$d" =~ *lo ]] && echo "yes"
$
Edit #3:
Okay, I've done some more tests and I believe I understand it now:
$ [[ "$d" =~ he* ]] && echo "yes"
yes
$ [[ "$d" =~ *lo ]] && echo "yes"
$ [[ "$d" =~ h* ]] && echo "yes"
yes
$ [[ "$d" =~ lo$ ]] && echo "yes"
yes
$ [[ "$d" =~ ^he ]] && echo "yes"
yes
$ [[ "$d" =~ [a-z]ll[a-z] ]] && echo "yes"
yes
$
Thank you all for your help and information!
It is used to perform comparisons in strings.
if [[ "$URL" =~ $ACTIVE ]] && [[ "$URL2" =~ $ACTIVE2 ]]; then
Checks if $URL contains the content of the variable $ACTIVE and if $URL2 contains the content of the variable $ACTIVE2.
See a test:
$ d="hello"
$ [[ "$d" =~ he* ]] && echo "yes"
yes
$ [[ "$d" =~ *ba* ]] && echo "yes"
$
$ [[ $d =~ .*ll.* ]] && echo "yes"
yes
In the last one you have to indicate the regex properly. It is equivalent to using == and just *ll*.
$ [[ $d == *ll* ]] && echo "yes"
yes
From man bash -> 3.2.4.2 Conditional Constructs:
An additional binary operator, =~, is available, with the same
precedence as == and !=. When it is used, the string to the
right of the operator is considered an extended regular expression and
matched accordingly (as in regex(3)). The return value is 0 if the
string matches the pattern, and 1 otherwise. If the regular
expression is syntactically incorrect, the conditional expression's
return value is 2.
I am attempting to run a block of code if one flag is set to true and the other is set to false. ie
var1=true
var2=false
if [[ $var1 && ! $var2 ]]; then var2="something"; fi
Since that did not evaluate the way that I expected I wrote several other test cases and I am having a hard time understanding how they are being evaluated.
aa=true
bb=false
cc="python"
if [[ "$aa" ]]; then echo "Test0" ; fi
if [[ "$bb" ]]; then echo "Test0.1" ; fi
if [[ !"$aa" ]]; then echo "Test0.2" ; fi
if [[ ! "$aa" ]]; then echo "Test0.3" ; fi
if [[ "$aa" && ! "$bb" ]]; then echo "Test1" ; fi
if [[ "$aa" && ! "$aa" ]]; then echo "Test2" ; fi
if [[ "$aa" ]] && ! [[ "$bb" ]]; then echo "test3" ; fi
if [[ "$aa" ]] && ! [[ "$cc" ]]; then echo "test4" ; fi
if [[ $aa && ! $bb ]]; then echo "Test5" ; fi
if [[ $aa && ! $aa ]]; then echo "Test6" ; fi
if [[ $aa ]] && ! [[ $bb ]]; then echo "test7" ; fi
if [[ $aa ]] && ! [[ $cc ]]; then echo "test8" ; fi
When I run the preceding codeblock the only output I get is
Test0
Test0.1
Test0.2
however, my expectation is that I would get
Test0
Test1
Test3
Test5
Test7
I have tried to understand the best way to run similar tests, however most examples I have found are set up in the format of
if [[ "$aa" == true ]];
which is not quite what I want to do. So my question is what is the best way to make comparisons like this, and why do several of the test cases that I would expect to pass simply not?
Thank you!
Without any operators, [[ only checks if the variable is empty. If it is, then it is considered false, otherwise it is considered true. The contents of the variables do not matter.
Your understanding of booleans in shell context is incorrect.
var1=true
var2=false
Both the above variables are true since those are non-empty strings.
You could instead make use of arithmetic context:
$ a=1
$ b=0
$ ((a==1 && b==0)) && echo y
y
$ ((a==0 && b==0)) && echo y
$
$ ((a && !(b))) && echo y; # This seems to be analogous to what you were attempting
y
The shell does not have Boolean variables, per se. However, there are commands named true and false whose exit statuses are 0 and 1, respectively, and so can be used similarly to Boolean values.
var1=true
var2=false
if $var1 && ! $var2; then var2="something"; fi
The difference is that instead of testing if var1 is set to a true value, you expand it to the name of a command, which runs and succeeds. Likewise, var2 is expanded to a command name which runs and fails, but because it is prefixed with ! the exit status is inverted to indicate success.
(Note that unlike most programming languages, an exit status of 0 indicates success because while most commands have 1 way to succeed, there are many different ways they could fail, so different non-zero values can be assigned different meanings.)
true and false are evaluated as strings ;)
[[ $var ]] is an equivalent of [[ -n $var ]] that check if $var is empty or not.
Then, no need to quote your variables inside [[. See this reminder.
Finally, here is an explication of the difference between && inside brackets and outside.
The closest you can come seems to be use functions instead of variables because you can use their return status in conditionals.
$ var1() { return 0; }
$ var2() { return 1; } # !0 = failure ~ false
and we can test this way
$ var1 && echo "it's true" || echo "it's false"
it's true
$ var2 && echo "it's true" || echo "it's false"
it's false
or this way
$ if var1; then echo "it's true"; else echo "it's false"; fi
it's true
$ if var2; then echo "it's true"; else echo "it's false"; fi
it's false
Hope this helps.
Here is the code I'm trying to get right, but I know that it is logically incorrect, as the ! binds only to the first test.
# if Vim was compiled in the same month, skip.
if ! [[ -f /usr/local/bin/vim ]] && /usr/local/bin/vim --version | grep "compiled $(date +%b) [0-9]{1,2} $(date +%Y) " -
The reason that I know it is logically wrong is:
$ if ! [[ a == b ]] && [[ c == c ]]; then echo yy; fi
yy
$ if ! [[ a == a ]] && [[ a == c ]]; then echo yy; fi
$
I believe the solution is to use the curlies:
$ if ! { [[ a == a ]] && [[ a == c ]]; }; then echo yy; fi
yy
I wonder if there is anything more elegant.
if [[ a != a ]] || [[ a != c ]]; then echo yy; fi
or:
if ! /usr/local/bin/vim --version 2> /dev/null | grep "compiled ...