Not able retrieve the key value from shell script - bash

I am working on the shell script to read config properties from a .properties files, below is the sample config
RCTP_servername=test1
RCTP_databasename=test2
RCTP_portnumber=test3
RCTP_username=test4
RCTP_password=test5
i have written a shell script as below but it doesn't work could anyone please have look and guide me how to solve this
#building the keys based on environment
environment=RCTP
servername_key="$environment"_servername
databasename_key="$environment"_databasename
portnumber_key="$environment"_portnumber
username_key="$environment"_username
password_key="$environment"_username
#read the config.properties files
file=serverconfig.properties
if [ -f "$file" ]
then
echo "$file found."
while IFS='=' read -r key value
do
key=$(echo $key )
eval "${key}='${value}'"
done < "$file"
servername_value=${servername_key}
databasename_value=${databasename_key}
portnumber_value=${portnumber_key}
username_value=${username_key}
password_value=${password_key}
else
echo "$file not found."
fi
echo "$servername_value"
but am getting an below when i tried to run it, the error is ./test_script_fte.sh: line 23: ${servername_key}: bad substitution
The expected output is when echo $servername_value executed is test1

Though eval is not recommended most of the time, here is a solution that uses indirect-reference as
echo "${!servername_value}"
I have also tweaked the logic to source the properties file over using eval from your logic. With the complete script as below.
#!/bin/bash
#building the keys based on environment
environment=RCTP
servername_key="$environment"_servername
databasename_key="$environment"_databasename
portnumber_key="$environment"_portnumber
username_key="$environment"_username
password_key="$environment"_username
#read the config.properties files
file=serverconfig.properties
if [ -f "$file" ]
then
echo "$file found."
# sourcing the properties file in the current shell to fetch the values
source "$file"
servername_value=${servername_key}
databasename_value=${databasename_key}
portnumber_value=${portnumber_key}
username_value=${username_key}
password_value=${password_key}
else
echo "$file not found."
fi
echo "${!servername_value}"
echo "${!databasename_value}"
echo "${!portnumber_value}"
echo "${!username_value}"
echo "${!password_value}"

It seems you want to use the value of a variable as the name of another variable.
please replace your last line with followings
eval echo \"\$$servername_value\"

Related

How to syntax check a shell script before sourcing it?

I want to run this command source .env (sourcing a .env file) and if the .env file had some errors while sourcing. I want to show a message before the error output "Hey you got errors in your .env" else if there's no error, I don't want to show anything.
Here's a code sample that needs editing:
#!/bin/zsh
env_auto_sourcing() {
if [[ -f .env ]]; then
OUTPUT="$(source .env &> /dev/null)"
echo "${OUTPUT}"
if [ -n "$OUTPUT" ]; then
echo "Hey you got errors in your .env"
echo "$OUTPUT"
fi
}
You could use bash -n (zsh has has a -n option as well) to syntax check your script before sourcing it:
env_auto_sourcing() {
if [[ -f .env ]]; then
if errs=$(bash -n .env 2>&1);
then source .env;
else
printf '%s\n' "Hey you got errors" "$errs";
fi
fi
}
Storing the syntax check errors in a file is a little cleaner than the subshell approach you have used in your code.
bash -n has a few pitfalls as seen here:
How do I check syntax in bash without running the script?
Why not just use the exit code from the command source ?
You don't have to use bash -n for this because ...
If let's say your .env file contains these 2 invalid lines:
dsadsd
sdss
If you run your current accepted code using the example above:
if errs=$(bash -n .env 2>&1);
the above condition will fail to stop the file from sourcing.
So, you can use source command return code to handle all of this:
#!/bin/bash
# This doesn't actually source it. It just test if source is working
errs=$(source ".env" 2>&1 >/dev/null)
# get the return code
retval=$?
#echo "${retval}"
if [ ${retval} = 0 ]; then
# Do another check for any syntax error
if [ -n "${errs}" ]; then
echo "The source file returns 0 but you got syntax errors: "
echo "Error details:"
printf "%s\n" "${errs}"
exit 1
else
# Success here. we know that this command works without error so we source it
echo "The source file returns 0 and no syntax errors: "
source ".env"
fi
else
echo "The source command returns an error code ${retval}: "
echo "Error details:"
printf "%s\n" "${errs}"
exit 1
fi
The best thing with this approach is, it will check both bash syntax and source syntax as well:
Now you can test this data in your env file:
-
~
#
~<
>

Bash script not creating files

Wrote a Bash script to create two files, each with a list of dates, to be used later. The script requires three parameters: a data subject, a minimum date, and a maximum date. Here's my script:
#!/bin/sh
dataSubj=$1
minDate=$2
maxDate=$3
echo -e "my variables:\nsubject:\t$dataSubj\nstart:\t$minDate\nend:\t$maxDate"
//Wrote the above line for debugging
configDir=/opt/site1/ETL/MFGEDW/config/MERS2
dateCount=1
addTime=00:00:00
fromDates=$dataSubj_fromDates.txt
toDates=$dataSubj_toDates.txt
cd $configDir
echo "Creating fromDates file and adding $minDate"
echo -e "$minDate $addTime" > ./$fromDates
echo "Creating toDates file"
>./$toDates
while [[ $minDate < $maxDate ]]
do
minDate=$(date -d "$minDate 7 days" +%Y-%m-%d)
((dateCount++))
if [[ $minDate < $maxDate ]]; then
echo "Adding $minDate to fromDates file"
echo -e "$minDate $addTime," >> ./$fromDates
fi
echo "Adding $minDate to toDates file"
echo -e "$minDate $addTime," >> ./$toDates
echo "$dateCount dates total"
done
exit $dateCount
My issue is that instead of having two files with the desired dates, I have one hidden file with all the dates that should have been written in the two files. I'm fairly new to scripting, but modeled this after other scripts that I've used and know work. Is there something I'm missing or added unnecessarily? Thanks in advance.
This is your problem:
fromDates=$dataSubj_fromDates.txt
toDates=$dataSubj_toDates.txt
Bash doesn't know you mean dataSubj is the name of the variable. You're trying to use two different variables:
dataSubj_fromDates
dataSubj_toDates
Pretty sure those don't exist. Note '.' is a stopper for variable naming. Try using:
fromDates=${dataSubj}_fromDates.txt
toDates=${dataSubj}_toDates.txt
Next time print all variables when debugging.

bash let variable default to user input

I want to write a script for both interactive and batch use. If arguments are not provided the script will ask for the users input.
Unlike here the user shouldn't be bothered if the variable is already defined by arguments.
Using parameter expansion I tried this:
${FILE="$( echo "Please provide the file:" >&2 ; read a; echo $a )"}
... but I always get a command not found error.
Any suggestions?
I would write
# earlier in program, $FILE may or may not be defined
if [[ -z $FILE ]]; then
read -p "Please provide the file: " FILE
fi
If you really want to cram it into one line, use the : command and the assignment expansion syntax
: ${FILE:=$(read -p "file?"; echo "$REPLY")}
Note, don't use ALL_CAPS_VARS: someday you'll use PATH and wonder why your script is broken.
It should be
FILE="$( echo "Please provide the file:" >&2 ; read a; echo $a )"
You are trying to execute the command instead of initialization
${FILE="$( echo "Please provide the file:" >&2 ; read a; echo $a )"}
Please provide the file:
Mahesh
-bash: Mahesh: command not found
$ FILE="$( echo "Please provide the file:" >&2 ; read a; echo $a )"
Please provide the file:
mahesh
$ echo $FILE
mahesh

Shell script to check a log file and print the errors that are found in it

I am pretty new to shell scripting . I am trying to write a script to check for logfile for errors (error strings are hardcoded), and i have to print the lines containing the error . i am able to write the logic but need pointers to read a file from user input.
Appreciate the help thanks.
Logic:
Accept the logfile patch from user
Check if the logfile is present or not
If present search the file for lines containing the error string (eg. Error, ORA)
Print the lines containing error strings , also write the output to a logfile
Read the log file from user
Set error strings
search="ERROR"
set a path for output file
outfile="file1.txt"
Execution logic
find "$mydir" -type f -name "$filename" |while read file
do
RESULT=$(grep "$search" "$file")
if [[ ! -z $RESULT ]]
then
echo "Error(s) in $file: $RESULT" >> "$outfile"
fi
done
I'm not sure what you mean with "need pointers to read a file from user input". I assume "pointers" are script arguments.
You can use this script:
#!/bin/bash
expected=outfile
for f in $#
do
if [ "$expected" = outfile ]; then
OUTFILE=$1
expected=search
elif [ "$expected" = search ]; then
SEARCH=$2
expected=files
elif [[ -f $f ]]; then
RESULT=`grep "$SEARCH" $f`
if [ -n "$RESULT" ]; then
echo -e "\n"
echo "Error(s) in "$f":"
echo $RESULT
echo -e "\n" >> $OUTFILE
echo "Error(s) in "$f":" >> $OUTFILE
echo $RESULT >> $OUTFILE
fi
fi
done
Invoke with:
scriptname outfile search files
where:
scriptname: is the name of file containing the script.
outfile: the name of the output file
search: the text to be searched
files: one or many file name or file patterns.
Examples (I assume the name of the script is searcherror and it is in the system path):
searcherror errorsfound.txt primary /var/log/*.log
searcherror moreerrors.txt "ORA-06502" file1.log /var/log/*.log ./mylogs/*

Check if file exists [BASH]

How do I check if file exists in bash?
When I try to do it like this:
FILE1="${#:$OPTIND:1}"
if [ ! -e "$FILE1" ]
then
echo "requested file doesn't exist" >&2
exit 1
elif
<more code follows>
I always get following output:
requested file doesn't exist
The program is used like this:
script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE
Any ideas please?
I will be glad for any help.
P.S. I wish I could show the entire file without the risk of being fired from school for having a duplicate. If there is a private method of communication I will happily oblige.
My mistake. Fas forcing a binary file into a wrong place. Thanks for everyone's help.
Little trick to debugging problems like this. Add these lines to the top of your script:
export PS4="\$LINENO: "
set -xv
The set -xv will print out each line before it is executed, and then the line once the shell interpolates variables, etc. The $PS4 is the prompt used by set -xv. This will print the line number of the shell script as it executes. You'll be able to follow what is going on and where you may have problems.
Here's an example of a test script:
#! /bin/bash
export PS4="\$LINENO: "
set -xv
FILE1="${#:$OPTIND:1}" # Line 6
if [ ! -e "$FILE1" ] # Line 7
then
echo "requested file doesn't exist" >&2
exit 1
else
echo "Found File $FILE1" # Line 12
fi
And here's what I get when I run it:
$ ./test.sh .profile
FILE1="${#:$OPTIND:1}"
6: FILE1=.profile
if [ ! -e "$FILE1" ]
then
echo "requested file doesn't exist" >&2
exit 1
else
echo "Found File $FILE1"
fi
7: [ ! -e .profile ]
12: echo 'Found File .profile'
Found File .profile
Here, I can see that I set $FILE1 to .profile, and that my script understood that ${#:$OPTIND:1}. The best thing about this is that it works on all shells down to the original Bourne shell. That means if you aren't running Bash as you think you might be, you'll see where your script is failing, and maybe fix the issue.
I suspect you might not be running your script in Bash. Did you put #! /bin/bash on the top?
script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE
You may want to use getopts to parse your parameters:
#! /bin/bash
USAGE=" Usage:
script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE
"
while getopts gpr:d: option
do
case $option in
g) g_opt=1;;
p) p_opt=1;;
r) rfunction_id="$OPTARG";;
d) dfunction_id="$OPTARG";;
[?])
echo "Invalid Usage" 1>&2
echo "$USAGE" 1>&2
exit 2
;;
esac
done
if [[ -n $rfunction_id && -n $dfunction_id ]]
then
echo "Invalid Usage: You can't specify both -r and -d" 1>&2
echo "$USAGE" >2&
exit 2
fi
shift $(($OPTIND - 1))
[[ -n $g_opt ]] && echo "-g was set"
[[ -n $p_opt ]] && echo "-p was set"
[[ -n $rfunction_id ]] && echo "-r was set to $rfunction_id"
[[ -n $dfunction_id ]] && echo "-d was set to $dfunction_id"
[[ -n $1 ]] && echo "File is $1"
To (recap) and add to #DavidW.'s excellent answer:
Check the shebang line (first line) of your script to ensure that it's executed by bash: is it #!/bin/bash or #!/usr/bin/env bash?
Inspect your script file for hidden control characters (such as \r) that can result in unexpected behavior; run cat -v scriptFile | fgrep ^ - it should produce NO output; if the file does contain \r chars., they would show as ^M.
To remove the \r instances (more accurately, to convert Windows-style \r\n newline sequences to Unix \n-only sequences), you can use dos2unix file to convert in place; if you don't have this utility, you can use sed 's/'$'\r''$//' file > outfile (CAVEAT: use a DIFFERENT output file, otherwise you'll destroy your input file); to remove all \r instances (even if not followed by \n), use tr -d '\r' < file > outfile (CAVEAT: use a DIFFERENT output file, otherwise you'll destroy your input file).
In addition to #DavidW.'s great debugging technique, you can add the following to visually inspect all arguments passed to your script:
i=0; for a; do echo "\$$((i+=1))=[$a]"; done
(The purpose of enclosing the value in [...] (for example), is to see the exact boundaries of the values.)
This will yield something like:
$1=[-g]
$2=[input.txt]
...
Note, though, that nothing at all is printed if no arguments were passed.
Try to print FILE1 to see if it has the value you want, if it is not the problem, here is a simple script (site below):
#!/bin/bash
file="${#:$OPTIND:1}"
if [ -f "$file" ]
then
echo "$file found."
else
echo "$file not found."
fi
http://www.cyberciti.biz/faq/unix-linux-test-existence-of-file-in-bash/
Instead of plucking an item out of "$#" in a tricky way, why don't you shift off the args you've processed with getopts:
while getopts ...
done
shift $(( OPTIND - 1 ))
FILE1=$1

Resources