I'm trying to run some commands only if a shell command output is empty, like this:
setup-database:
database=$(shell docker-compose exec mariadb mysql -e "select schema_name from information_schema.schemata where schema_name = 'dbname'" --silent --silent)
ifeq ($(strip $(database)),)
docker-compose exec mariadb mysql -e "create database dbname"
# ...
endif
but it doesn't work. It executes the commands inside the if, regardless the first command's output.
The problem is that you're mixing Make commands and shell commands.
setup-database:
database=$(shell docker-compose some things)
ifeq ($(strip $(database)),)
docker-compose some other things
# ...
endif
That ifeq ... endif is a Make conditional. Make will evaluate it before running any rule; the variable database is empty to begin with, so Make always includes that block of rules in the recipe. (And in fact the make variable database remains empty. The database that gets assigned a value is a shell variable.
Since you want to test the variable when the rule is executed, it should be a shell variable, tested with a shell conditional. On the command line, this would be:
database=`docker-compose some things`
if [ -z $database ]
then
docker-compose some other things
...
fi
(I don't think [ -z STRING ] cares about whitespace, so we don't need $(strip ...).)
Since every command in a recipe runs in a separate subshell, the whole thing must be on one line or the value of the variable will be lost:
database=`docker-compose some things`; if [ -z $database ] ; then docker-compose some other things; ...; fi
And when we put this in a makefile, we use $(shell ...) instead of backticks, and escape the $ (and optionally use some backslashes to make the rule more readable):
setup-database:
database=$(shell docker-compose some things);\
if [ -z $$database ] ; then \
docker-compose some other things; \
... \
fi
Make does not provide direct option to extract values of specific operation into variables.
Two options to consider: - using a make symbol, tracking the result a file.
create make symbol
# Note: nothing get executed.
db_check=$(shell docker-compose exec mariadb mysql -e "select schema_name from information_schema.schemata where schema_name = 'dbname'" --silent --silent)
In setup-database:
# Create shell variable. Useful only on the same line.
database="${db_check}" ; [ "$database" ] || docker-compose exec mariadb mysql -e "create database dbname"
# ...
Related
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.)
I have a rule within a Makefile which tries to render some text with env variables, I always get this variable empty whenever I pass it to the $(shell ) command
the following is the Makefile I am trying to pass the env variables with
and I am using sqoop to fetch data from sql server running a query with defined date in where statement
SHELL := /bin/bash
ifndef DATE
override DATE=$(shell date --date="1 day ago" +%Y%m%d)
endif
test:
sqoop --target-dir /tmp/output/ --fields-terminated-by '\t' --split-by id --query '$(shell cat query.sql | env DATE=$(DATE) python -m mypackage.render)'
I run commands like make test DATE=20190728
when I pass the env argument like | env DATE=20190728 python -m mypackage.render then I am able to see the query with where convert(varchar,created_at,112) = '20190728' but when I use env var I always get an empty string with the env variable like where convert(varchar,created_at,112) = ''
The problem is the make $(shell ...) invocation. You probably want command substitution (a shell feature). And kill a useless cat.
test:
$(MAKE) do_test VAR=test
do_test:
VAR=$(VAR) python -m mypackage.render < file.txt
Note that it is not recommended to set SHELL in a Makefile.
I have the most simple issue and I am so not certain what I am doing wrong.
I have a simple shell script using /bin/sh
inside the script I have the following:
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '$#' ${WWW_USER}
fi
}
I am calling it with
exec_as_wwwdata composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN
it just does nothing, no error message nothing.
If I call the following directly inside the script
su -s /bin/sh -c 'composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN' ${WWW_USER}
it works.
What am I doing wrong here?
Based on feedback I have changed it to this
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
fi
}
Although when I am calling it with the following arguments
exec_as_wwwdata /usr/local/bin/php /usr/local/bin/composer create-project --repository-url=xxxx .
I receive the following error message
su: unrecognized option '--repository-url=
I think there is issue with -- in the string. How can I escape that ?
There are two overlapping uses of $# here, and you've inadvertently stumbled on a partial correct solution. -c expects a single word, while "$#" would produce multiple distinct words. The correct solution would be
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
The short version: you don't want to build a command string from the current parameters; you want to pass them as arguments to a hard-coded command string to let the new shell expand things appropriately.
A breakdown:
-s /bin/sh - use /bin/sh instead of the appropriate users's login shell
-c '"$#"' run the command "$#", as desired. Note this is a hard-coded value; the new shell will expand its positional parameters correctly once it has started.
"$WWW_USER" - specify the user to run the shell as
_ - specify the value of $0 in the shell being run. You probably don't care what this value is; you just need some placeholder to prevent your first real argument from being treated as the value for $0.
"$#" pass the current positional parameters as arguments to the new shell, which will expand its "$#" to these values.
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[#]}"
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