I am currently learning bash programming and dont really understand why the passing argument for me is not working.
i have a script like this
#!/bin/bash
# the following environment variables must be set before running this script
# SIM_DIR name of directory containing armsim
# TEST_DIR name of the directory containing this script and the expected outputs
# LOG_DIR name of the directory that your output is written to by the run_test2 script
# ARMSIM_VERBOSE set to "-v" for verbose logging or leave unset
# First check the environment variables are set
giveup=0
if [[ ${#SIM_DIR} -eq 0 || ${#TEST_DIR} -eq 0 || ${#LOG_DIR} -eq 0 ]] ; then
echo One or more of the following environment variables must be set:
echo SIM_DIR, TEST_DIR, LOG_DIR
giveup=1
fi
# Now check the verbose flag
if [[ ${#ARMSIM_VERBOSE} != 0 && "x${ARMSIM_VERBOSE}" != "x-v" ]] ; then
echo ARMSIM_VERBOSE must be unset, empty or set to -v
giveup=1
fi
# Stop if environment is not set up
if [ ${giveup} -eq 1 ] ; then
exit 0
fi
cd ${TEST_DIR}
for i in test2-*.sh; do
echo "**** Running test ${i%.sh} *****"
./$i > ${LOG_DIR}/${i%.sh}.log
done
When I run the .sh file and pass in 3 example argument as below:-
$ ./run_test2 SIM_DIR TEST_DIR LOG_DIR
It still show: One or more of the following environment variables must be set:
SIM_DIR, TEST_DIR, LOG_DIR
Can anyone guide me on this? Thank you.
That's not how it's intended to work. The environment variables must be set beforehand either in the script or in the terminal like
export SIM_DIR=/home/someone/simulations
export TEST_DIR=/home/someone/tests
export LOG_DIR=/home/someone/logs
./run_test2
If you use these variables frequently, you might want to export them in ~/.bashrc. The syntax is identical to the exports in the above example.
Environment variables aren't really arguments in the sense I understand from your question/example. It sounds to me like you want to give arguments to a function/script, if you do that you can find your arguments in $1-9 (I think bash supports even more, unsure), the number of arguments are stored in $#
Example function that expects two arguments:
my_func() {
if [ $# -ne 2 ]; then
printf "You need to give 2 arguments\n"
return
fi
printf "Your first argument: %s\n" "$1"
printf "Your second argument: $s\n" "$2"
}
# Call the functionl like this
my_func arg1 arg2
Related
I try to check the command return inside my bash script for all commands. I created a function for this named check_command_return_code. This function is called in some other function which run command and it seems to work as expected except for the envsubst command.
This is my check_command_return_code:
check_command_return_code(){
"$#"
if [ "$?" -ne 0 ]; then
echo "[ERROR] Error with command $#"
exit 1
fi
echo "[SUCCESS] Command $# has successfully run"
}
I also write this function in order to substitute env variable inside yaml file:
substitute_env_variables_into_file(){
echo "Create new file named $2 from $1 by substituting environment variables within it"
check_command_return_code envsubst < $1 > $2
}
I call my function which proceeds the substitution like this:
substitute_env_variables_into_file "./ingress-values.yaml" "./ingress-values-subst.yaml"
This is my ingress-values.yaml file:
controller:
replicaCount: 2
service:
loadBalancerIP: "$INTERNAL_LOAD_BALANCER_IP"
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
I expect my ingress-values-subst.yaml looks like this:
controller:
replicaCount: 2
service:
loadBalancerIP: "my_private_ip"
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal:
Unfortunately the ingress-values-subst.yaml is expanded with the echo of my check_command_return_code function as you can see:
controller:
replicaCount: 2
service:
loadBalancerIP: "my_private_ip"
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[SUCCESS] Command envsubst has successfully run
I enabled the "debug" mode thanks to the following command in order to have more verbosity:
set -x
These logs are those from the output of my script:
++ substitute_env_variables_into_file ./private-ingress-values.yaml ./private-ingress-values-subst.yaml
++ echo 'Create new file named ./ingress/private-ingress-values-subst.yaml from ./private-ingress-values.yaml by substituting environment variables within it'
Create new file named ./private-ingress-values-subst.yaml from ./private-ingress-values.yaml by substituting environment variables within it
++ check_command_return_code envsubst
++ envsubst
++ '[' 0 -ne 0 ']'
++ echo '[SUCCESS] Command envsubst has successfully run'
I don't understand why the parameter of my command envsubst are not passed into my check_command_return_code function as you can see in the previous logs.
Thanks in advance for your help
I don't understand why the parameter of my command envsubst are not passed into my check_command_return_code
Redirections are not parameters. Redirections are opened at the time the line is executed.
When you do your_function > file, then inside your_function standard output is redirected to file for the whole duration of the function, including all the commands inside your_function.
Wrap it in yet another function:
myenvsubst() {
envsubst < "$1" > "$2"
}
check_command_return_code myenvsubst "$1" "$2"
Or better yet, write log information to standard error, or another file descriptor.
echo "[ERROR] Error with command $*" >&2
Check your scripts with shellcheck to find such problems like:
< $1 > $2
are not quoted. They should be < "$1" > "$2"
if [ "$?" -ne 0 ]; then
is an antipattern. Prefer if ! "$#"; then.
echo "[ERROR] Error with command $#"
is an odd usage of quoted $#. Prefer $*, or move to a separate argument.
I have below shell script where i want to create another parameter and instead of in in the line $JAVA_BIN $JAVA_OPTS --workflow test_method --config config/dbimport.$1.properties in i want to provide that parameter.
#!/bin/bash
if [[ $# -lt 1 ]]; then
echo "usage: test_method_full.sh <env> ; whereas env is one of dev|simu|prod"
exit 1
fi
# ******************** Check arguments **********
if [[ $1 != "dev" && $1 != "simu" && $1 != "prod" ]]; then
echo "env parameter has to be dev, simu or prod. Found ${1}"
exit 3
fi
# ***************** Java-Environment **************
# use given JAVA_HOME or openjava-1.8 if available
if [[ -z ${JAVA_HOME} ]]; then
# JAVA_HOME does not exist
if [[ -d /opt/java-1.8 ]]; then
JAVA_HOME=/opt/java-1.8
else
echo "JAVA_HOME could not be determined!"
exit 5
fi
fi
# set the Java binary based on JAVA_HOME
JAVA_BIN=${JAVA_HOME}/bin/java
$JAVA_BIN $JAVA_OPTS -classpath "/opt/Software/lib/*:/opt/Software/test_method/lib/*" --workflow test_method --config config/dbimport.$1.properties in
While running the script it should run like below:
test_method_full.sh simu /opt/Software/in
I dont want to hardcode /opt/Software/in/ in my shell script and make it changeable while runnig the script
You can choose to pass it as optional parameter (i.e. "${2:-/opt/Software/in/}"), which can override the default, but uses the default, if you don't pass a parameter, or use an environment variable for it, say: "${JAVA_ARG:-/opt/Software/in/}", in a similar way you are already doing it for JAVA_OPTS.
I am very new to Bash scripting, can someone explain to me how the $# and $? work in the following code?
#!/bin/bash
ARGS=3 # Script requires 3 arguments.
E_BADARGS=85 # Wrong number of arguments passed to script.
if [ $# -ne "$ARGS" ]
then
echo "Usage: `basename $0` old-pattern new-pattern filename"
exit $E_BADARGS
fi
old_pattern=$1
new_pattern=$2
if [ -f "$3" ]
then
file_name=$3
else
echo "File \"$3\" does not exist."
exit $E_BADARGS
fi
exit $?
From Learn Bash in Y minutes:
# Builtin variables:
# There are some useful builtin variables, like
echo "Last program's return value: $?"
echo "Script's PID: $$"
echo "Number of arguments passed to script: $#"
echo "All arguments passed to script: $#"
echo "The script's name: $0"
echo "Script's arguments separated into different variables: $1 $2..."
From https://www.gnu.org/software/bash/manual/html_node/Special-Parameters.html
$# Expands to the number of positional parameters in decimal.
$? Expands to the exit status of the most recently executed foreground pipeline.
$# shows the number of the script's arguments
$? shows the last script's return value
about arguments: echo "ARG[$#]" before if and then execute the script like
script.sh 1
the ouput will be
ARG[1]
Usage: g old-pattern new-pattern filename
and so on
the ouput of $? could be also used on the command line:
#shell>ls
file1.txt g inpu nodes_list
#shell>echo $?
0
#shell>ls FileNameNotFound
ls: FileNameNotFound: No such file or directory
#shell> echo $?
1
In bash exist special variables... and i write you some of then.
$#- this is an special variable that content inside the number of command line (you can just count how many parameters were entered) you passed to the script. tis variable also represent the last command line but its better do this ${!#}
$?- this one is very special cause its represents is your script is fine this variable holds the exit status of the previosly command... its a littler confusing but it work perfectly... when you end you script you can positional this variable at the end and if she return 0 value you scrip is perfect is true, if she return 1 or others you must check out your lines.
I have an if statement which is used many times in my script:
if [[ $? -ne 0 ]]; then
echo [$JOB_NAME] failed.
exit 1
fi
is it possible to define a variable and assign this statement to it, and then call it each time I need ti?
Example of use:
echo [$JOB_NAME] extracting manifests...
unzip -o ZIP_FILE "*.yml"
# Push the app to CF
cf push -f $MANIFEST_FILE -p ZIP_FILE $NEW_APP
if [[ $? -ne 0 ]]; then
echo [$JOB_NAME] failed.
exit 1
fi
As mentioned in the comments, this should be a function, which could be written like this:
ensure_success () {
if [[ $# -eq 0 ]]; then
echo "no command passed to ensure_success."
elif ! "$#"; then
echo "[$JOB_NAME] failed."
exit 1
fi
}
"$#" expands to the full list of arguments passed to the function. I added a check based on Inian's suggestion in the comments, to ensure that at least one argument is passed to the function.
This combines running the command and checking the error code, so you can use it like:
ensure_success command arg1 arg2 arg3
So, based on the example in your question it would be:
ensure_success cf push -f "$MANIFEST_FILE" -p ZIP_FILE "$NEW_APP"
The quotes are free.
I'm not sure where $JOB_NAME is defined but presumably it is a global.
I am trying to execute a hallo_word.sh that is stored at ~/bin from this script that is stored at my ~/Desktop. I have made both scripts executable. But all the time I get the problem message. Any ideas?
#!/bin/sh
clear
dir="$PATH"
read -p "which file you want to execute" fl
echo ""
for fl in $dir
do
if [ -x "$fl" ]
then
echo "executing=====>"
./$fl
else
echo "Problem"
fi
done
This line has two problems:
for fl in $dir
$PATH is colon separated, but for expects whitespace separated values. You can change that by setting the IFS variable. This changes the FIELD SEPARATOR used by tools like for and awk.
$fl contains the name of the file you want to execute, but you overwrite its value with the contents of $dir.
Fixed:
#!/bin/sh
clear
read -p "which file you want to execute" file
echo
IFS=:
for dir in $PATH ; do
if [ -x "$dir/$file" ]
then
echo "executing $dir/$file"
exec "$dir/$file"
fi
done
echo "Problem"
You could also be lazy and let a subshell handle it.
PATH=(whatever) bash command -v my_command
if [ $? -ne 0 ]; then
# Problem, could not be found.
else
# No problem
fi
There is no need to over-complicate things.
command(1) is a builtin command that allows you to check if a command exists.
The PATH value contains all the directories in which executable files can be run without explicit qualification. So you can just call the command directly.
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
"$fl" || { echo "Problem" ; exit 1 ; }
I quote the name as it could have spaces.
To test if the command exists before execution use type -p
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
type -p "$fq" >/dev/null || exit 1
"$fl" || { echo "Problem" ; exit 1 ; }