Setting multiple variables to call in a conditional statement - bash

I want to create an environment variable depending on the location and append that environment variable name to log file. For example, I have multiple AWS accounts and depending on the account I want the variable name to identify that account. I would like to run this in a conditional as well instead I writing multiple scripts.
if account is this echo or printf account and set the account name to that variable before executing the code.
aws_bsd='lab'
aws_csd='engineering'
aws_efg='chemistry'

You can get the absolute path of the currently running script using realpath and take the decision based on that.
#!/usr/bin/env bash
_this_script_path="$(realpath "$0")" # script path
_this_dir_path="$(dirname "$_this_script_path")" # scripts' parent directory path
_account_1_path="/home/acc1"
_account_2_path="/home/acc2"
if [[ "$_this_dir_path" == "$_account_1_path" ]]; then
echo "- setting up account 1"
# do account 1 specific stuff
# export VAR1="var1_value"
# ...
elif [[ "$_this_dir_path" == "$_account_2_path" ]]; then
echo "- setting up account 2"
# do account 2 specific stuff
# ...
fi
Note:
I'm not sure whether realpath is in POSIX standard, but I find it is widely available. In case you don't have it, there are other methods to do the same realpath does. But I believe this is the most concise approach.

Related

How to Access a variable in a Shell script

I'm currently stuck on how to do the following:
I have a settings file that looks like this:
USER_ID=12
ROLE=admin
STARTED=10/20/2010
...
I need to access the role and map the role to one of the variables in the script below. After I will use that variable to call open a doc with the correct name.
test.sh
#!/bin/sh
ADMIN=master_doc
STAFF=staff_doc
GUEST=welcome_doc
echo "accessing role type"
cd /setting
#open `settings` file to access role?
#call correct service
#chmod 555 master_doc.service
Is there a way to interpolate strings using bash like there is in javascript? Also, I'm guessing I would need to traverse through the settings file to access role?
With bash and grep and assuming that the settings file has exactly one line beginning with ROLE=:
#!/bin/bash
admin=master_doc
staff=staff_doc
guest=welcome_doc
cd /setting || exit
role=$(grep '^ROLE=' settings)
role=${role#*=}
echo chmod 555 "${!role}.service"
Drop the echo after making sure it works as intended.
Look into Shell Parameter Expansion for indirect expansion.
From what I understand, you want to get the variables from settings, use $role as an indirect reference to $admin, i.e. master_doc, then turn that into a string, master_doc.service.
Firstly, instead of indirection, I recommend an associative array since it's cleaner.
You can use source to get variables from another file, as well as functions and other stuff.
Lastly, to dereference a variable, you need to use the dollar sign, like $role. Variable references are expanded inside double-quotes, so that's sort of the equivalent of string interpolation.
#!/bin/bash
# Associative array with doc names
declare -A docs=(
[admin]=master_doc
[staff]=staff_doc
[guest]=welcome_doc
)
echo "accessing role type"
cd setting || exit
source settings # Import variables
true "${ROLE?}" # Exit if unset
echo chmod 555 "${docs[$ROLE]}.service" # Select from associative array
# ^ Using "echo" to test. Remove if the script works properly.
You can source the settings file to load the variables:
source settings
And then you can use them in your script:
chmod 555 "${admin}.service" # will replace ${admin} with master_doc
I'd certainly use source(.)
#!/bin/sh
ADMIN=master_doc
STAFF=staff_doc
GUEST=welcome_doc
echo "accessing role type"
. /setting/settings 2> /dev/null || { echo 'Settings file not found!'; exit 1; }
role=${ROLE^^} # upercase rolename
echo ${!role}.service # test echo

Difficulty using readlink in bash with mix of variables and string to return absolute paths

I have a config script where users can specify paths as variables in the header section. I want them to be able to use absolute paths, relative paths and variables (because this is actually called from another shell script from where they get the values for the variables). At the end of the script all the paths are written to a text file.
The challenge I have is that variables used within some of the paths can change in the middle of the script. I am having difficulties in re-evaluating the path to get the correct output.
### HEADER SECTION ###
DIR_PATH="$VAR1/STRING1"
InputDir_DEFAULT="$DIR_PATH/Input"
### END HEADER ###
...some code
if [[ some condition ]]; then DIR_PATH="$VAR2/STRING2"; fi
...more code
# $InputDir_DEFAULT needs re-evaluating here
InputDir=$(readlink -m $InputDir_DEFAULT)
echo $InputDir >> $FILE
When I do as above and 'some condition' is met, the return of 'echo' is the absolute path for $VAR1/STRING1/Input, whereas what I want it the abs path for $VAR2/STRING2/Input.
Below is an alternative, where I try to stop InputDir_DEFAULT being evaluated until the end by storing itself as a string.
### HEADER SECTION ###
DIR_PATH="$VAR1/STRING1"
InputDir_DEFAULT='$DIR_PATH/Input' #NOTE: "" marks have changed to ''
### END HEADER ###
if [[ some condition ]]; then DIR_PATH="$VAR2/STRING2"; fi
STRING_TMP=$InputDir_DEFAULT
InputDir=$(readlink -m $STRING_TMP)
echo $InputDir >> $FILE
This time 'echo' returns a mix of the evaluated variables and un-evaluated string: $VAR2/STRING2/$DIR_PATH/Input which (for me) looks like /home/ubuntu/STRING2/$DIR_PATH/Input. It's just the $DIR_PATH/ that shouldn't be there.
This feels like it should be relatively straightforward. I'm hoping I'm on the right path and that it's my use of "" and '' that's at fault. But I've tried lots of variations with no success.
When you initially set InputDir_DEFAULT, it is taking the currently set value for ${DIR_PATH}; even if you update ${DIR_PATH} later on, InputDir_DEFAULT will remain what it was set to earlier. To resolve this in your current script, you could set InputDir_DEFAULT again inside the if statement:
InputDir_DEFAULT=${DIR_PATH}/Input
Additionally, in your second attempt the single quoted value setting translates to the literal string value and does not expand to the variable's value:
InputDir_DEFAULT='$DIR_PATH/Input'
I would recommend referring to the "Quoting" section in the GNU Bash manual.
This is the solution I came to in the end. It's a mix of what was suggested by #ThatsWhatSheCoded and some other stuff to ensure that the user doesn't have to redefine variables anywhere other than in the header.
I expect there's a more elegant way of doing this, but this does work.
### HEADER SECTION ###
DIR_PATH_DEFAULT="$VAR1/STRING1"
InputDir_DEFAULT="$DIR_PATH_DEFAULT/Input"
### END HEADER ###
...some code
if [[ some condition ]]; then DIR_PATH="$VAR2/STRING2"; fi
### Checks whether $DIR_PATH_DEFAULT is used in any variables.
### If so and $DIR_PATH is different, will replace string.
### This will be done for all variables in a list.
if [[ ! "$DIR_PATH_DEFAULT" =~ "$DIR_PATH" ]]; then
for i in ${!var[#]}; do
var_def_val=${var[i]}_DEFAULT
STRING_TMP=${!var_def_val}
var_def=${var[$i]}_DEFAULT
if [[ $STRING_TMP == *"$DIR_PATH_DEFAULT"* ]] && [[ ! $var_def == "DIR_PATH_DEFAULT" ]]; then
STRING_TMP="${STRING_TMP/$DIR_PATH_DEFAULT/$DIR_PATH}"
eval "${var_def}=$STRING_TMP"
fi
done
fi
...more code
InputDir=$(readlink -m $InputDir_DEFAULT)

Bash check if path contains two folders

How can I search an arbitrary path and determine if it has two folder names? The folder names can appear in any position in either order. Not a shell expert so seeking help here.
if [ -p "$PATH" ]; then
echo "path is set"
else
echo "path is not set"
fi
I found this segment but I'm not sure it's useful. $PATH is a special variable correct?
First, let me make sure I understand the question right. You have some path (like "/home/sam/foo/bar/baz") and you want to test whether it contains two specific directory names (e.g. "foo" and "bar") in either order, right? So, looking for "foo" and "bar":
/home/sam/foo/bar/baz would match
/mnt/bar/subdir/foo would also match
/mnt/bar/foo2 would not match, because "foo2" is not "foo"
If that's correct, you can do this in bash as two tests:
dir1="foo"
dir2="bar"
if [[ "/$path/" = *"/$dir1/"* && "/$path/" = *"/$dir2/"* ]]; then
echo "$path" contains both $dir1 and $dir2"
else
echo "$path" does not contain both $dir1 and $dir2"
fi
Notes:
This is using the [[ ]] conditional expression, which is different from [ ] and not available in basic shells. If you use this type of expression, you need to start the shell script with a shebang that tells the OS to run it with bash, not a generic shell (i.e. the first line should be either #!/bin/bash or #!/usr/bin/env bash), and do not run it with the sh command (that will override the shebang).
The way the comparison works is that it sees whether the path matches both the patterns *"/$dir1/"* and *"/$dir2/"* -- that is, it matches those names, with a slash at each end, maybe with something else (*) before and after. But since the path might not start and/or end with a slash, we add them ("/$path/") to make sure they're there.
Do not use PATH as a variable in your script -- it's a very special variable that tells the shell where to find executable commands. If you ever use it for anything else, your script will suddenly start getting "command not found" errors. Actually, there are a bunch of all-caps special-meaning variables; to avoid conflicts with them, use lowercase or mixed-case variables for your things.

Exporting environment variables for local development and heroku deployment

I would like to setup some files for development, staging and production with environment variables, for example:
application_root/development.env
KEY1=value1
KEY2=value2
There would be similar files staging.env and production.env.
I am looking for a couple different bash scripts which would allow the loading of all these variables in either development or staging/production.
In local development I want to effectively run export KEY1=value1 for each line in the file.
For staging/production I will be deploying to Heroku and would like to effectively run heroku config:set -a herokuappname KEY1=value1 for each line in the staging or production.env files.
I know there are some gems designed for doing this but it seems like this might be pretty simple. I also like the flexibility of having the .env files as simple lists of keys and values and not specifically being tied to any language/framework. If I would have to change something about the way these variables need to be loaded it would be a matter of changing the script but not the .env files.
In the simplest form, you can load the key-value pairs into a bash array as follows:
IFS=$'\n' read -d '' -ra nameValuePairs < ./development.env
In Bash v4+, it's even simpler:
readarray -t nameValuePairs < ./development.env
You can then pass the resulting "${nameValuePairs[#]}" array to commands such as export or heroku config:set ...; e.g.:
export "${nameValuePairs[#]}"
Note, however, that the above only works as intended if the input *.env file meets all of the following criteria:
the keys are syntactically valid shell variable names and the lines have the form <key>=<value>, with no whitespace around =
the lines contain no quoting and no leading or trailing whitespace
there are no empty/blank lines or comment lines in the file.
the values are confined to a single line each.
A different approach is needed with files that do not adhere to this strict format; for instance, this related question deals with files that may contain quoted values.
Below is the source code for a bash script named load_env (the .sh suffix is generally not necessary and ambiguous):
You'd invoke it with the *.env file of interest, and it would perform the appropriate action (running heroku config:set … or export) based on the filename.
However, as stated, you must source the script (using source or its effective bash alias, .) in order to create environment variables (export) visible to the current shell.
To prevent obscure failures, the script complains if you pass a development.env file and have invoked the script without sourcing.
Examples:
./load_env ./staging.dev
. ./load_env ./development.dev # !! Note the need to source
load_env source code
#!/usr/bin/env bash
# Helper function that keeps its aux. variables localized.
# Note that the function itself remains defined after sourced invocation, however.
configOrExport() {
local envFile=$1 doConfig=0 doExport=0 appName
case "$(basename "$envFile" '.env')" in
staging)
doConfig=1
# Set the desired app name here.
appName=stagingapp
;;
production)
doConfig=1
# Set the desired app name here.
appName=productionapp
;;
development)
doExport=1
;;
*)
echo "ERROR: Invalid or missing *.env file name: $(basename "$envFile" '.env')" >&2; exit 2
esac
# Make sure the file exists and is readable.
[[ -r "$envFile" ]] || { echo "ERROR: *.env file not found or not readable: $envFile" >&2; exit 2; }
# If variables must be exported, make sure the script is being sourced.
[[ $doExport -eq 1 && $0 == "$BASH_SOURCE" ]] && { echo "ERROR: To define environment variables, you must *source* this script." >&2; exit 2; }
# Read all key-value pairs from the *.env file into an array.
# Note: This assumes that:
# - the keys are syntactically valid shell variable names
# - the lines contain no quoting and no leading or trailing whitespace
# - there are no empty/blank lines or comment lines in the file.
IFS=$'\n' read -d '' -ra nameValuePairs < "$envFile"
# Run configuration command.
(( doConfig )) && { heroku config:set -a "$appName" "${nameValuePairs[#]}" || exit; }
# Export variables (define as environment variables).
(( doExport )) && { export "${nameValuePairs[#]}" || exit; }
}
# Invoke the helper function.
configOrExport "$#"

Bash recursive program

Here is my function so far:
function create {
a=$3
while [ $a -lt $secondnum ]
do
echo "mkdir $1/$a"
if [ $2 -lt $firstnum ]
then
sum=$(($2+1))
d=$(($a))
create "$1/$d" $sum 0
fi
a=$(($a+1))
done
}
Its echoing
mkdir hw1/0
mkdir hw1/0/0
mkdir hw1/0/0/0
mkdir hw1/0/0/1
mkdir hw1/0/0/2
It's supposed to create a whole directory, not just one branch. (e.g. hw///* branch aswell) This is a homework project so I need to use bash.
I believe bash variables are global by default. If you want a function-local variable, you need to use the local keyword.
$ help local
local: local [option] name[=value] ...
Define local variables.
Create a local variable called NAME, and give it VALUE. OPTION can
be any option accepted by 'declare'.
Local variables can only be used within a function; they are visible
only to the function where they are defined and its children.
Exit Status:
Returns success unless an invalid option is supplied, an error occurs,
or the shell is not executing a function.

Resources