I am trying to read a properties file from a shell script which contains a period (.) character like below:
# app.properties
db.uat.user=saple user
db.uat.passwd=secret
#/bin/sh
function pause(){
read -p "$*"
}
file="./app.properties"
if [ -f "$file" ]
then
echo "$file found."
. $file
echo "User Id " $db.uat.user
echo "user password =" $db.uat.passwd
else
echo "$file not found."
fi
I have tried to parse the file after sourcing the file but it is not working since the keys contains the "." character and there are spaces in that value also.
My properties file always resides in the same directory of the script or somewhere in /usr/share/doc
I use simple grep
inside function in bash script to receive properties from .properties
file.
This properties file I use in two places - to setup dev environment and as application parameters.
I believe that grep
may work slow in big loops but it solves my needs when I want to prepare dev
environment.
Hope, someone will find this useful.
Example:
File: setup.sh
#!/bin/bash
ENV=${1:-dev}
function prop {
grep "${1}" env/${ENV}.properties|cut -d'=' -f2
}
docker create \
--name=myapp-storage \
-p $(prop 'app.storage.address'):$(prop 'app.storage.port'):9000 \
-h $(prop 'app.storage.host') \
-e STORAGE_ACCESS_KEY="$(prop 'app.storage.access-key')" \
-e STORAGE_SECRET_KEY="$(prop 'app.storage.secret-key')" \
-e STORAGE_BUCKET="$(prop 'app.storage.bucket')" \
-v "$(prop 'app.data-path')/storage":/app/storage \
myapp-storage:latest
docker create \
--name=myapp-database \
-p "$(prop 'app.database.address')":"$(prop 'app.database.port')":5432 \
-h "$(prop 'app.database.host')" \
-e POSTGRES_USER="$(prop 'app.database.user')" \
-e POSTGRES_PASSWORD="$(prop 'app.database.pass')" \
-e POSTGRES_DB="$(prop 'app.database.main')" \
-e PGDATA="/app/database" \
-v "$(prop 'app.data-path')/database":/app/database \
postgres:9.5
File: env/dev.properties
app.data-path=/apps/myapp/
#==========================================================
# Server properties
#==========================================================
app.server.address=127.0.0.70
app.server.host=dev.myapp.com
app.server.port=8080
#==========================================================
# Backend properties
#==========================================================
app.backend.address=127.0.0.70
app.backend.host=dev.myapp.com
app.backend.port=8081
app.backend.maximum.threads=5
#==========================================================
# Database properties
#==========================================================
app.database.address=127.0.0.70
app.database.host=database.myapp.com
app.database.port=5432
app.database.user=dev-user-name
app.database.pass=dev-password
app.database.main=dev-database
#==========================================================
# Storage properties
#==========================================================
app.storage.address=127.0.0.70
app.storage.host=storage.myapp.com
app.storage.port=4569
app.storage.endpoint=http://storage.myapp.com:4569
app.storage.access-key=dev-access-key
app.storage.secret-key=dev-secret-key
app.storage.region=us-east-1
app.storage.bucket=dev-bucket
Usage:
./setup.sh dev
@fork2x
I have tried like this .Please review and update me whether it is right approach or not.
#/bin/sh
function pause(){
read -p "$*"
}
file="./apptest.properties"
if [ -f "$file" ]
then
echo "$file found."
dbUser=`sed '/^\#/d' $file | grep 'db.uat.user' | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'`
dbPass=`sed '/^\#/d' $file | grep 'db.uat.passwd' | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'`
echo database user = $dbUser
echo database pass = $dbPass
else
echo "$file not found."
fi
Since variable names in the BASH shell cannot contain a dot or space it is better to use an associative array in BASH like this:
#!/bin/bash
# declare an associative array
declare -A arr
# read file line by line and populate the array. Field separator is "="
while IFS='=' read -r k v; do
arr["$k"]="$v"
done < app.properties
Testing:
Use declare -p to show the result:
> declare -p arr
declare -A arr='([db.uat.passwd]="secret" [db.uat.user]="saple user" )'
I found using while IFS='=' read -r
to be a bit slow (I don't know why, maybe someone could briefly explain in a comment or point to a SO answer?). I also found @Nicolai answer very neat as a one-liner, but very inefficient as it will scan the entire properties file over and over again for every single call of prop
.
I found a solution that answers the question, performs well and it is a one-liner (bit verbose line though).
The solution does sourcing but massages the contents before sourcing:
#!/usr/bin/env bash
source <(grep -v '^ *#' ./app.properties | grep '[^ ] *=' | awk '{split($0,a,"="); print gensub(/\./, "_", "g", a[1]) "=" a[2]}')
echo $db_uat_user
Explanation:
grep -v '^ *#'
: discard comment lines
grep '[^ ] *='
: discards lines without =
split($0,a,"=")
: splits line at =
and stores into array a
, i.e. a[1] is the key, a[2] is the value
gensub(/\./, "_", "g", a[1])
: replaces .
with _
print gensub... "=" a[2]}
concatenates the result of gensub
above with =
and value.
Edit: As others pointed out, there are some incompatibilities issues (awk) and also it does not validate the contents to see if every line of the property file is actually a kv pair. But the goal here is to show the general idea for a solution that is both fast and clean. Sourcing seems to be the way to go as it loads the properties once that can be used multiple times.
Source: Stackoverflow.com