Bash - Check If given argument exits - bash

I have a separate shell script that runs my docker-compose environment in attached mode or detached if I pass -d or --detach argument. It works fine when I pass this argument (./run-env.sh -d) but it doesn't when I run my script without any option ( ./run-env, just getting blank output and docker-compose doesn't run), where can be a problem?
#!/usr/bin/env bash
for arg in "$#"; do
if [ "$arg" = '-d' ] || [ "$arg" = '--detach' ]
then
docker-compose -f docker-compose.local-environment.yml up --build -V --detach
else
docker-compose -f docker-compose.local-environment.yml up --build -V --abort-on-container-exit
fi
done

When you don't give argument, you don't even enter the for loop, that's why nothing happens.
#!/usr/bin/env bash
# By default, use '--abort-on-container-exit' option
abort_or_detach="--abort-on-container-exit"
# Search for a parameter asking to run in detached mode
for arg in "$#"; do
if [ "$arg" = '-d' ] || [ "$arg" = '--detach' ]
then
abort_or_detach="--detach"
fi
done
# Run with the correct option
docker-compose -f docker-compose.local-environment.yml up --build -V $abort_or_detach
Here in this script, you call one time docker-compose, and you can manage easily the options with the for loop
Also, with your first try, you would launch docker-compose as many times as you have different parameters. Here, you treat them, and then do a single launch

for arg in "$#" iterates over the arguments. When you pass no arguments, it iterates zero times. Instead, try something like:
extra=--abort-on-container-exit
for arg; do
case "$arg" in
-d|--detach) extra=--detach
esac
done
docker-compose -f docker-compose.local-environment.yml up --build -V $extra
Note that this is one of those cases where you do not want to put quotes around $extra, because if extra is the empty string you don't want to pass anything to docker-compose. (Here, the default will ensure it is not empty, but this is a fairly common pattern and there are cases where it will be the empty string.)

Related

Environment Variables inside the container, but not used in the bash script

I have an application that runs inside a docker container. First I build the image and then run the container. My run command is:
docker run --rm -it -e MODE=custom -e Station=RT -e StartDateReport=2022-09-10 -e Period=1 my-image:1.0.0
I declare the variables MODE, Station, StartDateReport and Period as environment variables. When I start a terminal from the container and type echo $MODE I will get the correct value, custom.
So far, so good, but I am interested in using these variables in a bash script. For example in start.sh I have the following code:
#!/bin/bash
if [[ $MODE == custom ]]; then
// do sth
fi
and here inside the script my variable MODE is undefined, and hence I obtain wrong results.
EDIT
As discussed in the comments below, my application if based on a cronjob to start running.
I managed to solve by myself the problem and the answer is in the comments.
In your environment, does your variable definition have the form
export MODE="custom"
Modified version of your script:
#!/bin/bash
test -z "${MODE}" && ( echo -e "\n\t MODE was not exported from calling environment.\n" ; exit 1 )
if [[ $MODE == custom ]]
then
#// do sth
echo "do sth"
fi
I found the solution for this problem, so I will post the answer here to help others that have the same problem. I found the solution here: How to load Docker environment variables in container
I included export xargs --null --max-args=1 echo < /proc/1/environ in start.sh
Thus, start.sh will be:
#!/bin/bash
export xargs --null --max-args=1 echo < /proc/1/environ
if [[ $MODE == custom ]]; then
// do sth
fi

Autocomplete environment variables in bash commands (like RAILS_ENV=test bundle ...)

I want to add some environment variable names and possible value to the list of completions in bash.
For an example, in Ruby on Rails, I run commands like
RAILS_ENV=test bundle exec rails db:migrate
I want to be able to type
RAI<tab>
> completion fills in RAILS_ENV=
RAILS_ENV=te<tab>
> completion fills in RAILS_ENV=test
Sometimes I can use complete -W "my values" my_command to attach values to a specific command, but here I do not want to attach these to a command, I want them just available, much like the list of commands themselves.
Is this possible?
Here's an example which requires Bash 5.0+ and = be removed from COMP_WORDBREAKS.
# tested with bash-5.0.16
_rails()
{
local cmd=$1 cur=$2 pre=$3
local -a envs=()
local i
for i in production test-foo test-bar; do
envs+=( "RAILS_ENV=$i" )
done
if [[ $cur == R* ]]; then
COMPREPLY=( $( compgen -W "${envs[*]}" -- "$cur" ) )
fi
}
COMP_WORDBREAKS=${COMP_WORDBREAKS//=/}
complete -I -F _rails -o bashdefault
NOTE:
It completes to RAILS_ENV= only if the first word starts with R.
By using -o bashdefault, it can still autocomplete normal command names.

Expand environment variable inside container on Docker command line

Suppose that I create a Dockerfile that just runs an echo command:
FROM alpine
ENTRYPOINT [ "echo" ]
and that I build it like this:
docker build -t my_echo .
If I run docker run --rm my_echo test it will output test as expected.
But how can I run the command to display an environment variable that is inside the container?
Example:
docker run --rm --env MYVAR=foo my_echo ???
How to access the $MYVAR variable that is in the container to display foo by replacing the ??? part of that command?
Note:
This is a simplified version of my real use case. My real use case is a WP-CLI Docker image that I built with a Dockerfile. It has the wp-cli command as the ENTRYPOINT.
I am trying to run a container based on this image to update a WordPress parameter with an environment variable. My command without Docker is wp-cli option update siteurl "http://example.com" where http://example.com would be in an environment variable.
This is the command I am trying to run (wp_cli is the name of my container):
docker run --rm --env WEBSITE_URL="http://example.com" wp_cli option update siteurl ???
It's possible to have the argument that immediately follows ["bash", "-c"] itself be a shell script that looks for sigils to replace. For example, consider the following script, which I'm going to call argEnvSubst:
#!/usr/bin/env bash
args=( "$#" ) # collect all arguments into a single array
for idx in "${!args[#]}"; do # iterate over the indices of that array...
arg=${args[$idx]} # ...and collect the associated values.
if [[ $arg =~ ^#ENV[.](.*)#$ ]]; then # if we have a value that matches a pattern...
varname=${BASH_REMATCH[1]} # extract the variable name from that pattern
args[$idx]=${!varname} # and replace the value with a lookup result
fi
done
exec "${args[#]}" # run our resulting array as a command.
Thus, argEnvSubst "echo" "#ENV.foobar#" will replace #ENV.foobar# with the value of the environment named foobar before it invokes echo.
While I would strongly suggest injecting this into your Dockerfile as a separate script and naming that script as your ENTRYPOINT, it's possible to do it in-line:
ENTRYPOINT [ "bash", "-c", "args=(\"$#\"); for idx in \"${!args[#]}\"; do arg=${args[$idx]}; if [[ $arg =~ ^#ENV[.](.*)#$ ]]; then varname=${BASH_REMATCH[1]}; args[$idx]=${!varname}; fi; done; \"${args[#]}\"", "_" ]
...such that you can then invoke:
docker run --rm --env WEBSITE_URL="http://example.com" \
wp_cli option update siteurl '#ENV.WEBSITE_URL#'
Note the use of bash -- this means alpine (providing only dash) isn't sufficient.

Accessing the number of arguments pass into a docker run command script

I have a docker Entrypoint script that looks like this:
#!/bin/sh
LABEL=$1
mkdir -p /backup/$LABEL
...
I can access the arguments passed in the normal bash way via $1, $2, etc. but I also need to know the number of arguments passed in. At first I thought I could do this like this:
if [ $# -eq 2 ];
then
However that does not work. Any ideas on how to retrieve the number of arguments?
TIA,
Ole
Weird. This should work. But, if you can read the positional parameters $1 and $2, you may have luck looping over them:
#!/bin/bash
params="$#"
while param=$1 && [ -n "$param" ]
do
shift
((count += 1))
echo "here comes $param"
done
echo "All params: ${params[#]}"
echo "We saw $count of them"
OK - In reality nothing passed in was resolving. The reason is that the entrypoint line needs to look like this:
ENTRYPOINT ["bash", "/run.sh"]
Mine looked like this:
ENTRYPOINT ["/run.sh"]
See here for more info:
Referencing the first argument passed to the docker entrypoint?

How to escape space in bash script from inline if?

I know that similar questions have been asked and answered before on stackoverflow (for example here and here) but so far I haven't been able to figure it out for my particular case.
I'm trying to create a script that adds the -v flag only if the variable something is equal to "true" (what I'm trying to do is to mount the current folder as a volume located at /src in the Docker container):
docker run --name image-name `if [ "${something}" == "true" ]; then echo "-v $PWD:/src"; fi` ....
The problem is that $PWD may contain spaces and if so my script won't work. I've also tried assigning "$PWD" to an intermediate variable but it still doesn't work:
temp="$PWD"
docker run --name image-name `if [ "${something}" == "true" ]; then echo "-v $temp:/src"; fi` ....
If I run:
docker run --name image-name -v "$PWD":/src ....
from plain bash (without using my script) then everything works.
Does anyone know how to solve this?
Use an array.
docker_args=()
if something; then
docker_args+=( -v "$PWD/src" )
fi
docker run --blah "${docker_args[#]}" …
Don't have arrays? Use set (in a function, so it doesn't affect outer scope).
Generally:
knacker() {
if something; then
set -- -v "$PWD:/src" "$#"
fi
crocker "$#"
}
knacker run --blah
But some commands (like docker, git, etc) need special treatment because of their two-part command structure.
slacker() {
local cmd="$1"
shift
if something; then
set -- -v "$PWD:/src" "$#"
fi
docker "$cmd" "$#"
}
slacker run --blah
Try this (using the array way):
declare -a cmd=()
cmd+=(docker run --name image-name)
if [ "${something}" = "true" ]
then
cmd+=(-v "$PWD:/src")
fi
"${cmd[#]}"

Resources