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

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?

Related

Use Dockerfile commands inside of a shell script?

Suppose that I have an entry-point to a shell script as I want to use some conditionals in a dockerfile. Is there a way to do something like this?
ENTRYPOINT ["./entry.sh", "lambda-name"]
Inside of entry.sh
#!/usr/bin/env bash
lambda_name=$1
echo "$lambda_name"
if [ "$lambda_name" = "a.handler" ]; then
CMD [ "a.handler" ]
elif [ "$lambda_name" = "b.handler" ];then
CMD [ "b.handler" ]
else
echo "not found"
fi
first of all you don't need that complication.
why not like this?
#!/usr/bin/env bash
lambda_name=$1
echo "$lambda_name"
if [ "$lambda_name" = "a.handler" ]; then
./a.handler
elif [ "$lambda_name" = "b.handler" ];then
./b.handler
else
echo "not found"
fi
also in your script you could use something like
exec "$#"
at the end of your script. this would run all your arguments.
The ENTRYPOINT is the main container process. It executes when you docker run the built image; it's too late to run any other Dockerfile directives at that point.
In particular, the ENTRYPOINT gets passed the image's CMD (or a Compose command: or an alternate command after the docker run image-name) as arguments, and it can do whatever it wants with that. So at this point you don't really need to "set the container's command", you can just execute whatever command it is you might have wanted to run.
#!/bin/sh
case "$1" in
a.handler) a.handler ;;
b.handler) b.handler ;;
*)
echo "$1 not found" >&2
exit 1
;;
esac
With this setup, a fairly common Docker pattern is just to take whatever gets passed as arguments and to run that at the end of the entrypoint script.
#!/bin/sh
# ... do any startup-time setup required here ...
exec "$#"
That matches what you show in the question: if the first argument is a.handler then run that command, and so on.

Bash - Check If given argument exits

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.)

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[#]}"

bash programming - best practice setting up variables within a loop or not

I have the following logic in my bash/shell script. Where essentially, I'm trying to pass one argument manually and then passing in other values from a hidden file, like so:
if [[ $# != 1 ]]; then
echo "./tstscript.sh <IDNUM>" 2>&1
exit 1
fi
MYKEY=/dev/scripts/.mykey
if [ -f "$MYKEY" ]
then
IFS=';'
declare -a arr=($(< $MYKEY))
# DECLARE VARIABLES
HOSTNM=localhost
PORT=5432
PSQL_HOME=/bin
IDNUM=$1
DBU1=${arr[0]}
export HOSTNM PORT PSQL_HOME IDNUM DBU1 DBU2
$PSQL_HOME/psql -h $HOSTNM -p $PORT -U $DBU1 -v v1=$IDNUM -f t1.sql postgres
else
echo "Mykey not found"
fi
rt_code=?
exit 1
Am I declaring my variables in the right place? Should it be declaring within my if statement?
Most of your variables are redundant. psql already has a few well-known environment variables it will use if you don't specify various parameters on the command line. The others are just hard-coded, so it's not really important to define them. It really doesn't matter much where you define them, as long as you define them before they are used, since this isn't a very large script. It's a good sign that you've outgrown shell script and are ready for a more robust programming language when you start worrying about the design of the shell script.
if [[ $# != 1 ]]; then
echo "./tstscript.sh <IDNUM>" 2>&1
exit 1
fi
MYKEY=/dev/scripts/.mykey
if ! [ -f "$MYKEY" ]; then
echo "Mykey not found"
exit 1
fi
# You only use the first word/line of the file,
# so this should be sufficient.
IFS=";" read -a arr < "$MYKEY"
export PGHOST=localhost
export PGPORT=5432
export PGUSER=${arr[0]}
: ${PSQL_HOME:=/bin}
"$PSQL_HOME"/psql -v v1="$1" -f t1.sql postgres
When you fill /dev/scripts/.mykey with lines in the form key=value, you can source that file.
$ cat /dev/scripts/.mykey
DBU1=noober
FIELD2="String with space"
echo "Keep it clean, do not use commands like this echo in the file"
In your script you can activate the settings by sourcing the file
if [ -f "${MYKEY}" ]; then
. "${MYKEY}"
# Continue without an array, DBU1 and FIELD2 are set.

Sourcing a script file in bash before starting an executable

I'm trying to write a bash script that "wraps" whatever the user wants to invoke (and its parameters) sourcing a fixed file just before actually invoking it.
To clarify: I have a "ConfigureMyEnvironment.bash" script that must be sourced before starting certain executables, so I'd like to have a "LaunchInMyEnvironment.bash" script that you can use as in:
LaunchInMyEnvironment <whatever_executable_i_want_to_wrap> arg0 arg1 arg2
I tried the following LaunchInMyEnvironment.bash:
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee"
where I have to use the "launchee" variable to save the $# var because after executing source, $# becomes empty.
Anyway, this doesn't work and fails as follows:
myhost $ LaunchInMyEnvironment my_executable -h
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: /home/bin/my_executable -h: No such file or directory
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: exec: /home/bin/my_executable -h: cannot execute: No such file or directory
That is, it seems like the "-h" parameter is being seen as part of the executable filename and not as a parameter... But it doesn't really make sense to me.
I tried also to use $* instead of $#, but with no better outcoume.
What I'm doing wrong?
Andrea.
Have you tried to remove double quotes in exec command?
Try this:
#!/usr/bin/bash
typeset -a launchee
launchee=("$#")
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "${launchee[#]}"
That will use arrays for storing arguments, so it will handle even calls like "space delimited string" and "string with ; inside"
Upd: simple example
test_array() { abc=("$#"); for x in "${abc[#]}"; do echo ">>$x<<"; done; }
test_array "abc def" ghi
should give
>>abc def<<
>>ghi<<
You might want to try this (untested):
#!/usr/bin/bash
launchee="$1"
shift
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee" $#
The syntax for exec is exec command [arguments], however becuase you've quoted $launchee, this is treated as a single argument - i.e., the command, rather than a command and it's arguments. Another variation may be to simply do: exec $#
Just execute it normally without exec
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
$launchee
Try dividing your list of argumets:
ALL_ARG="${#}"
Executable="${1}"
Rest_of_Args=${ALL_ARG##$Executable}
And try then:
$Executable $Rest_of_Args
(or exec $Executable $Rest_of_Args)
Debugger

Resources