I don't know what is wrong with my function; it is not returning value properly.
function validate_directory_isempty {
retval=""
NumOfFiles=`ls -l $input | egrep -c "^-"`
if [ "$NumOfFiles" == "0" ];then
retval=true
else
retval=false
fi
echo $retval
}
retval=$(validate_directory_isempty /opt/InstallationManager)
echo "retval : " $retval
if [ "$retval" == "true" ]; then
echo ""
echo "Installing Installation Manager"
# Install_IM
else
echo ""
echo "DIRECTORY is not empty. Please make sure install location $DIRECTORY is empty before installing PRODUCT"
fi
The idiomatic way to have a function return true or false is to use the return keyword. A return value of 0 means success, a non-zero value means failure. Also note that a function will return with a status of the last command executed if return is not present.
I would write your function like this
is_dir_empty() {
shopt -s nullglob
local -a files=( "$1"/* )
(( ${#files[#]} == 0 ))
}
directory=/opt/InstallManager
if is_dir_empty "$directory"; then
echo "$directory" is empty
fi
The first line sets a shell option that pattern matching no files expands to null instead of the pattern itself as a string.
The second line fills an array with the filenames in the given directory.
The last line tests the number of elements in the array. If zero entries, return success else return failure.
i just replaced my script as below and it worked
removed the below commands
retval=$(validate_directory_isempty /opt/InstallationManager)
echo "retval : " $retval
added
input=/opt/InstallationManager
validate_directory_isempty
and it worked.
Thanks again for your valuable inputs
Related
I'm new to bash so assume that I don't understand everything in this simple script as I've been putting this together as of today with no prior experience with bash.
I get this error when I run test.sh:
command substitution: line 29: syntax error near unexpected token `$1,'
./f.sh: command substitution: line 29: `index_of($1, $urls))'
FILE: f.sh
#!/bin/bash
urls=( "example.com" "example2.com")
error_exit()
{
echo "$1" 1>&2
exit 1
}
index_of(){
needle=$1
haystack=$2
for i in "${!haystack[#]}"; do
if [[ "${haystack[$i]}" = "${needle}" ]]; then
echo "${i}"
fi
done
echo -1
}
validate_url_param(){
index=-2 #-2 as flag
if [ $# -eq 0 ]; then
error_exit "No url provided. Exiting"
else
index=$(index_of($1, $urls)) #ERROR POINTS TO THIS LINE
if [ $index -eq -1 ]; then
error_exit "Provided url not found in list. Exiting"
fi
fi
echo $index
}
FILE: test.sh
#!/bin/bash
. ./f.sh
index=$(validate_url_param "example.com")
echo $index
echo "${urls[0]}"
I've lost track of all of the tweaks I tried but google is failing me and I'm sure this is some basic stuff so... thanks in advance.
The immediate error, just like the error message tells you, is that shell functions (just like shell scripts) do not require or accept commas between their arguments or parentheses around the argument list. But there are several changes you could make to improve this code.
Here's a refactored version, with inlined comments.
#!/bin/bash
urls=("example.com" "example2.com")
error_exit()
{
# Include script name in error message; echo all parameters
echo "$0: $#" 1>&2
exit 1
}
# A function can't really accept an array. But it's easy to fix:
# make the first argument the needle, and the rest, the haystack.
# Also, mark variables as local
index_of(){
local needle=$1
shift
local i
for ((i=1; i<=$#; ++i)); do
if [[ "${!i}" = "${needle}" ]]; then
echo "${i}"
# Return when you found it
return 0
fi
done
# Don't echo anything on failure; just return false
return 1
}
validate_url_param(){
# global ${urls[#]} is still a bit of a wart
if [ $# -eq 0 ]; then
error_exit "No url provided. Exiting"
else
if ! index_of "$1" "${urls[#]}"; then
error_exit "Provided url not found in list. Exiting"
fi
fi
}
# Just run the function from within the script itself
validate_url_param "example.com"
echo "${urls[0]}"
Notice how the validate_url_param function doesn't capture the output from the function it is calling. index_of simply prints the result to standard output and that's fine, just let that happen and don't intervene. The exit code tells us whether it succeeded or not.
However, reading the URLs into memory is often not useful or necessary. Perhaps you are simply looking for
grep -Fx example.com urls.txt
getAnimalFolder() {
local animal=""
if [[ ${ANIMAL} == "lion" ]]; then
animal = "./animals/lion/"
elif [[ ${ANIMAL} == "tiger" ]]; then
animal = "./animals/tiger/"
elif [[ ${ANIMAL} == "cheetah" ]]; then
animal = "./animals/cheetah/"
else
echo "inavalid animal"
exit 1`enter code here`
fi
echo $animal
}
result=$(getAnimalFolder)
cd ../result/age/
If the animal is not lion, tiger or cheetah, the function returns invalid animal and hence gives an error 'No such file or directory', instead I need to do an exit with code = 1. Hence I went for the second option -
if [[ ${ANIMAL} != "lion" && ${ANIMAL} != "tiger" && ${ANIMAL} != "cheetah" ]]; then
echo "Invalid animal"
exit 1
fi
getAnimalFolder() {
local animal=""
if [[ ${ANIMAL} == "lion" ]]; then
animal = "./animals/lion/"
elif [[ ${ANIMAL} == "tiger" ]]; then
animal = "./animals/tiger/"
elif [[ ${ANIMAL} == "cheetah" ]]; then
animal = "./animals/cheetah/"
fi
echo $animal
}
result=$(getAnimalFolder)
cd ../result/age/
This looks like a fix to my problem but if in the future more animals are added, then I need to remember to make changes in 2 places for every new animal added. So is there a better way to do this?
There are a number of problems here; #1 and #3 are the ones that directly address your question.
When a function/command/whatever may need to print both regular output (e.g. the path to an animal directory) and error/status output (e.g. "inavalid animal"), it should send the regular output to standard output (aka stdout aka FD #1, the default), and error/status output to standard error (aka stderr aka FD #2), like this:
echo "Invalid animal" >&2 # This is sent to stderr
Generally, functions should return rather than exiting. If a function does exit, it exits the entire shell, but in this case the function is running in a subshell due to $( ), so it only exits that. Using return avoids this inconsistency.
When a function/command/whatever may fail, you should check its exit status; there are a number of ways to do this:
if result=$(getAnimalFolder); then
: # command succeeded!
else
echo "OMG it failed!" >&2
exit 1
fi
or
result=$(getAnimalFolder)
if [ $? -ne 0 ]; then # $? is the status of the last command
echo "OMG it failed!" >&2
exit 1
fi
or
result=$(getAnimalFolder) || {
echo "OMG it failed!" >&2
exit 1
}
I use the last form a lot, since there are a lot of steps in a script might fail, and having a simple & compact way to include the failure handing code makes the overall script more readable.
In general, functions should take their input as arguments rather than via global variables. So in the function you'd refer to $1 instead of $ANIMAL, and you'd run the function with something like:
result=$(getAnimalFolder "$ANIMAL")
There are also a number of basic syntax errors and bad scripting practices in the script: don't put spaces around the equal sign in assignments; do put double-quotes around variable references; don't use all-caps variable names (to avoid conflicts with the many all-caps variables that have special meanings); do check for errors on cd commands (if they fail, the rest of the script will run in the wrong place); and when comparing a single variable against a bunch of values, use case instead of a bunch of if elseif etc.
shellcheck.net is good at recognizing many of these common mistakes. Strongly recommended.
Here's what I get with all fixes in place:
#!/bin/bash
getAnimalFolder() {
local animalname=$1
local animaldir=""
case "$animalname" in
lion ) animaldir="./animals/lion/" ;;
tiger ) animaldir="./animals/tiger/" ;;
cheetah ) animaldir="./animals/cheetah/" ;;
* )
echo "Invalid animal: $animalname" >&2
return 1 ;;
esac
echo "$animaldir"
}
read -p "Give me an animal: " animalname
result=$(getAnimalFolder "$animalname") || {
exit 1 # Appropriate error message has already been printed
}
cd "../$result/age/" || {
echo "Error changing directory to ../$result/age/ -- aborting" >&2
exit 1
}
Put the animals in an array:
#!/bin/bash
animals=(lion tiger cheetah)
getAnimalFolder() {
local i
for i in "${animals[#]}"; do
if [ "$i" == "${1}" ] ; then
animaldir="./animals/${1}"
return 0
fi
done
exit 1
}
read -rp "Give me an animal: " animalname
getAnimalFolder "${animalname}"
echo "Animaldir=${animaldir}"
EDIT:
I did not use the construction result=$(getAnimalFolder), assuming the OP wants to use the new path once. When needed, the function can be changed into
echo "./animals/${1}"
When the function is called with result=$(getAnimalFolder), OP needs to look at the line
cd ../result/age/
Is resulta fixed path or does he want to use the path from the function:
cd ../${result}/age/
I am trying to check for duplicate records in my database using shell scripting.
For this, I have created a function named "check()" which echo's True or False and is stored in variable "result". But while evaluating using if statement it is always returning "True".
#redundancy check function
check() {
temp=$(grep -w -c "$1" database.dat)
echo $temp
if [ "$temp" != 0 ]
then
echo True
else
echo False
fi
}
insert() {
option="y"
while [ "$option" == "y" ]
do
echo "Rollno: \c"
read roll
result="$(check $roll)"
echo $result
if [ "$result" == "False" ]
then
echo Do something
else
echo "ERROR: Duplicate record found...\nEXITING...\n"
option="n"
fi
done
}
If you're using a shell that doesn't support the == extension to test, then your tests will always, unconditionally fail simply on account of invalid syntax. Use = for string comparisons to be portable to all POSIX-compliant implementations.
Moreover, there's no point to storing and then comparing the output from grep at all: Use the exit status of grep -q when your only goal is to check whether the number of matches is zero or more-than-zero; this allows grep to exit immediately when a match is seen, rather than needing to read the rest of the file.
# with -q, this emits no stdout, but exits w/ status 0 (matches exist) or 1 (otherwise)
check() { grep -q -w -e "$1" database.dat; }
insert() {
option=y
while [ "$option" = y ]; do
printf '%b\n' "Rollno: \c"
read -r roll
if check "$roll"; then
printf "ERROR: Duplicate record found...\nEXITING...\n"
option=n
else
echo "Check failed; do something"
fi
done
}
I have following function which checks connection for internet (I know there are better ways to check for internet connection but that's not the topic):
function checkInternet() {
local HOST="http://google.com"
local WGET="/usr/bin/wget"
$WGET -q --tries=10 --timeout=10 --spider $HOST
if [[ $? -eq 0 ]]
then
echo "online"
else
echo "offline"
fi
}
Now I want to request the return value of the function directly in an if-statement:
I tried several scripts similar to e.g. this one (what I found here):
if( $(checkInternet) -eq "online" )
then
echo "Function returned online"
else
echo "Function returned offline"
fi
I don't want to initialisize further variables before the if-statement what I meant with "directly".
You can use this in BASH to check the output of a function:
[[ $(checkInternet) == online ]] && echo "Function returned online" ||
echo "Function returned offline"
To compare conditions use [[...]] (preferable) or [...].
You can simplify above to to just this without evaluating any conditions:
echo "Function returned $(checkInternet)"
A case statement will do this nicely and without non-portable bashisms:
case $(checkInternet) in
(online) echo is online;;
(offline) echo is offline;;
esac
You can also make your checkInternet return a status, which simplifies things in an if:
...
if [ $? -eq 0 ]; then
true
else
false
fi
Along with
if checkInternet; then
# online
else
# offLine
fi
You are not actually returning a value from your checkInternet () function. If you look at the example you posted, you'll see that their function has a return statement at the end.
My strings are stored inside array, a sample string contains a 'variable'.
While iterating over this array, I want to substitute the 'variable' with a 'value'.
This fails for me. I have tried my lot in various ways & googled but could not figure it out
# Array of strings (each string is a command)
clean_aa_commands=(
"sourceanalyzer -b ${FortifyBuildId} -clean"
"cd ${unifiedbuilddir}/AA/AAUI"
"mvn clean"
)
# Functions
function check {
if [ "$?" -ne 0 ]; then
echo "Operation [$1] Unsuccessful!"
else
echo "Operation [$1] Success!"
fi
}
function runcmds {
echo "using $FortifyBuildId *************************"
cmdArr=("${!1}")
for cmd in "${cmdArr[#]}"
do
echo "-->Running [$cmd]"
eval "$cmd"
check "$cmd"
echo ""
echo ""
done
}
# main
FortifyBuildId="$1"
echo "FortifyBuildId is $FortifyBuildId"
unifiedbuilddir=`pwd`
runcmds clean_aa_commands["#"]
How about this
cmds='cmds=( /test/$mydir /test/$test )'
mydir=$(pwd)
test="me"
eval ${cmds}
echo ${cmds[#]}