Changing the value of the variable from another shell script - shell

I have a shell script that has a on/off switch inside. I programmed the Housekeeping.sh to execute this certain line of code if the value is at 1 and don't execute it if it's at 0.
The following is the code on my Housekeeping.sh:
ARCHIVE_SWITCH=1
if [[ $ARCHIVE_SWITCH -eq 1 ]]; then
sqlplus ${BPS_SCHEMA}/${DB_PASSWORD}#${ORACLE_SID} #${BATCH_HOME}/sql/switch_archive.sql
fi
Now I want to create another shell script file that I'll execute to automatically change the variable ARCHIVE_SWITCHequals to 0 everytime I execute this script.
Is there any other way that I can change the value of the variable ARCHIVE_SWITCH from another shell script file that I'll execute manually?

I'd use an option to the script:
bash housekeeping.sh # default is off
bash housekeeping.sh -a # archive switch is on
#!/usr/bin/env bash
archive_switch=0
while getopts :a opt; do
case $opt
a) archive_switch=1 ;;
*) echo "unknown option -$opt" >&2 ;;
esac
done
shift $((OPTIND-1))
if ((archive_switch)); then
sqlplus ...
fi

Related

Shell script not running on Mac OS

I have what amounts to a very simple bash script that executes a deployment. Here is my code:
#!/usr/bin/env bash
function print_help
{
echo '
Deploy the application.
Usage:
-r reinstall
-h show help
'
}
reinstall=false
while getopts "rh" opt; do
case ${opt} in
r)
echo "clean"
reinstall=true
;;
h)
echo "help"
print_help
exit 0
;;
esac
done
I am calling the script as follows:
. deploy.sh -h
No matter what I do, neither option (i.e. -r, -h) results in the respective echo and in the case of -h the print_help function isn't called.
What am doing wrong?
getopts uses a global variable OPTIND to keep track about which argument it processes currently. Each option it parses, it increments/changes OPTIND to keep track which argument will be next.
If you call getopt without changing OPTIND it will start from where it last ended. If it already parsed first argument, it would want to continue parsing from the second argument, etc. Because there is no second argument the second (or later) time you source your script, there is only -h, getopt will just fail, because it thinks it already parsed -h.
If you want to re-parse arguments in current shell, you need to just reset OPTIND=1. Or start a fresh new shell, which will reset OPTIND to 1 by itself.
If there's a space between the dot and your script name then you're not lauching the script but just sourcing it to your current shell session !
if you want to run your scipt you should do :
# chmod +x ./deploy.sh
# ./deploy.sh -h
if you source it the functtions and variables inside your scipt will be available in your current shell session this could allow you to do things like that :
# . ./deploy.sh
# print_help

Can a shell script flag have optional arguments if parsing with getopts?

I have a script that I want to run in three ways:
Without a flag -- ./script.sh
With a flag but no parameter -- ./script.sh -u
With a flag that takes a parameter -- ./script.sh -u username
Is there a way to do this?
After reading some guides (examples here and here) it doesn't seem like this is a possibility, especially if I want to use getopts.
Can I do this with getopts or will I need to parse my options another way? My goal is to continue using getopts if I can.
The non-getopts example in BashFAQ #35 can cover the use case:
user_set=0 # 1 if any -u is given
user= # set to specific string for -u, if provided
while :; do
case $1 in
-u=*) user_set=1; user=${1#*=} ;;
-u) user_set=1
if [ -n "$2" ]; then
user=$2
shift
fi ;;
--) shift; break ;;
*) break ;;
esac
shift
done

getopt erroneously caches arguments

I've created a script in my bash_aliases to make SSH'ing onto servers easier. However, I'm getting some odd behavior that I don't understand. The below script works as you'd expect, except for when it's re-used.
If I use it like this for this first time in a shell, it works exactly as expected:
$>sdev -s myservername
ssh -i ~/.ssh/id_rsa currentuser#myservername.devdomain.com
However, if I run that a second time, without specifying -s|--server, it will use the server name from the last time I ran this, having seemingly cached it:
$>sdev
ssh -i ~/.ssh/id_rsa currentuser#myservername.devdomain.com
It should have exited with an error and output this message: /bin/bash: A server name (-s|--server) is required.
This happens with any of the arguments; that is, if I specify an argument, and then the next time I don't, this method will use the argument from the last time it was supplied.
Obviously, this is not the behavior I want. What's responsible in my script for doing that, and how do I fix it?
#!/bin/bash
sdev() {
getopt --test > /dev/null
if [[ $? -ne 4 ]]; then
echo "`getopt --test` failed in this environment"
exit 1
fi
OPTIONS=u:,k:,p,s:
LONGOPTIONS=user:,key:,prod,server:
# -temporarily store output to be able to check for errors
# -e.g. use “--options” parameter by name to activate quoting/enhanced mode
# -pass arguments only via -- "$#" to separate them correctly
PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTIONS --name "$0" -- "$#")
if [[ $? -ne 0 ]]; then
# e.g. $? == 1
# then getopt has complained about wrong arguments to stdout
exit 2
fi
# read getopt’s output this way to handle the quoting right:
eval set -- "$PARSED"
domain=devdomain
user="$(whoami)"
key=id_rsa
# now enjoy the options in order and nicely split until we see --
while true; do
case "$1" in
-u|--user)
user="$2"
shift 2
;;
-k|--key)
key="$2".pem
shift 2
;;
-p|--prod)
domain=proddomain
shift
;;
-s|--server)
server="$2"
shift 2
;;
--)
shift
break
;;
*)
echo "Programming error"
exit 3
;;
esac
done
if [ -z "$server" ]; then
echo "$0: A server name (-s|--server) is required."
kill -INT $$
fi
echo "ssh -i ~/.ssh/$key.pem $user#$server.$domain.com"
ssh -i ~/.ssh/$key $user#$server.$domain.com
}
server is a global shell variable, so it's shared between runs of the function (as long as they're run in the same shell). That is, when you run sdev -s myservername, it sets the variable server to "myservername". Later, when you run just sdev, it checks to see if $server is empty, finds it's not, and goes ahead and uses it.
Solution: use local variables! Actually, it'd be best to declare all of the variables you use in the function as local; that way, you don't run the risk of interfering with something else that's trying to use the same variable name. I'd also recommend avoiding all-caps variable names (like OPTIONS, LONGOPTIONS, and PARSED) -- there are a bunch of all-caps variables that have special meanings to the shell and/or other programs, and if you use one of those by mistake it can cause weird problems.
Anyway, here's the simple solution: add this near the beginning of the script:
local server=""

Getopts in sourced Bash function works interactively, but not in test script?

I have a Bash function library and one function is proving problematic for testing. prunner is a function that is meant to provide some of the functionality of GNU Parallel, and avoid the scoping issues of trying to use other Bash functions in Perl. It supports setting a command to run against the list of arguments with -c, and setting the number of background jobs to run concurrently with -t.
In testing it, I have ended up with the following scenario:
prunner -c "gzip -fk" *.out - works as expected in test.bash and interactively.
find . -maxdepth 1 -name "*.out" | prunner -c echo -t 6 - does not work, seemingly ignoring -c echo.
Testing was performed on Ubuntu 16.04 with Bash 4.3 and on Mac OS X with Bash 4.4.
What appears to be happening with the latter in test.bash is that getopts is refusing to process -c, and thus prunner will try to directly execute the argument without the prefix command it was given. The strange part is that I am able to observe it accepting the -t option, so getopts is at least partially working. Bash debugging with set -x has not been able to shed any light on why this is happening for me.
Here is the function in question, lightly modified to use echo instead of log and quit so that it can be used separately from the rest of my library:
prunner () {
local PQUEUE=()
while getopts ":c:t:" OPT ; do
case ${OPT} in
c) local PCMD="${OPTARG}" ;;
t) local THREADS="${OPTARG}" ;;
:) echo "ERROR: Option '-${OPTARG}' requires an argument." ;;
*) echo "ERROR: Option '-${OPTARG}' is not defined." ;;
esac
done
shift $(($OPTIND-1))
for ARG in "$#" ; do
PQUEUE+=("$ARG")
done
if [ ! -t 0 ] ; then
while read -r LINE ; do
PQUEUE+=("$LINE")
done
fi
local QCOUNT="${#PQUEUE[#]}"
local INDEX=0
echo "Starting parallel execution of $QCOUNT jobs with ${THREADS:-8} threads using command prefix '$PCMD'."
until [ ${#PQUEUE[#]} == 0 ] ; do
if [ "$(jobs -rp | wc -l)" -lt "${THREADS:-8}" ] ; then
echo "Starting command in parallel ($(($INDEX+1))/$QCOUNT): ${PCMD} ${PQUEUE[$INDEX]}"
eval "${PCMD} ${PQUEUE[$INDEX]}" || true &
unset PQUEUE[$INDEX]
((INDEX++)) || true
fi
done
wait
echo "Parallel execution finished for $QCOUNT jobs."
}
Can anyone please help me to determine why -c options are not working correctly for prunner when lines are piped to stdin?
My guess is that you are executing the two commands in the same shell. In that case, in the second invocation, OPTIND will have the value 3 (which is where it got to on the first invocation) and that is where getopts will start scanning.
If you use getopts to parse arguments to a function (as opposed to a script), declare local OPTIND=1 to avoid invocations from interfering with each other.
Perhaps you are already doing this, but make sure to pass the top-level shell parameters to your function. The function will receive the parameters via the call, for example:
xyz () {
echo "First arg: ${1}"
echo "Second arg: ${2}"
}
xyz "This is" "very simple"
In your example, you should always be calling the function with the standard args so that they can be processed in the method via getopts.
prunner "$#"
Note that pruner will not modify the standard args outside of the function.

make getopts error when optionstring does not start with a dash

I am writing a script that uses getopts for options that require arguments and some that do not. I want getopts to exit with an error if any switches do not start with a '-', rather than simply stop parsing the options and continue. So if this is my getopts line:
while getopts "a:e:f:o:q:r:djpsvV" opt; do
and I call the script like this:
script.sh -a word -o eat me -j -d -e testing
the script stops parsing at "me" and the remaining switches are ignored. I want it to error out when it reaches "me", because it does not start with a '-'. How do I do that?
After your while getopts loop, do this:
shift $((OPTIND-1))
if (( $# > 0 )); then
echo "error: extra args detected: $*" >&2
exit 1
fi

Resources