Unable to execute command inside double square brackets inside my Makefile - makefile

clean:
#for container_name in ${NEW_DJANGO_IMAGE_NAME} \
${NEW_MSQL_IMAGE_NAME} \
${NEW_NGINX_IMAGE_NAME} \
${NEW_REDIS_IMAGE_NAME}; \
do if [[ 'a' == 'a' ]]; then echo 'fdfdf'; fi; done;
If I do something like this it works. Now instead of this silly line
do if [[ 'a' == 'a' ]]; then echo 'fdfdf'; fi; done;
I want to write the following:
do if [[ docker ps --filter "name=^/$$container_name$$" --format '{{.Names}}' == $$container_name ]]; then echo 'fdfdf'; fi; done;
The idea is that I iterate over a number of docker containers and if it happens that some of them are running I want to stop them. So in the place of echo 'fdfdf' I want to see this line: docker container stop <CONTAINER_NAME>;
Looks as simple as hell but I can't get it to work in the Makefile...What am I doing wrong?

You apparently think that [[ cmd == "string" ]] executes cmd before performing the test. This is not the case. Use:
[[ `cmd` == "string" ]]
instead. In your case it would look like this:
do if [[ `docker ps --filter "name=^/$$container_name$$" --format '{{.Names}}'` == $$container_name ]]; then docker container stop $$container_name; fi; done;
Or, a bit more readable, maybe:
IMAGES := $(NEW_DJANGO_IMAGE_NAME) $(NEW_MSQL_IMAGE_NAME) \
$(NEW_NGINX_IMAGE_NAME) $(NEW_REDIS_IMAGE_NAME)
clean:
#for cn in $(IMAGES); do \
tmp=`docker ps --filter "name=^/$$cn$$" --format '{{.Names}}'`; \
if [ -n "$$tmp" ]; then \
docker container stop $$cn; \
fi; \
done
Note that, in this last version, we use the bourne shell test commmand ([) instead of the bash-only conditional expression ([[...]]).

Related

How to make one loop out of five in bash?

I have a small script for Mac where I'm adding printers. It works fine but I think I could make it simpler or at least it would be interesting to know a different solution.
while IFS= read -r line;
do
if [[ $line == *"Printer_E1"* ]]
then
if [[ "$FIND_PRINTERS" =~ "$PRINTER_E1_IP" ]];
then
echo "found printer e1"
else
echo "adding printer e1"
"$LPADMIN" -p "$PRINTER_E1_IP" -v "lpd://$PRINTER_E1_IP" -L "$PRINTER_E1_LOCATION" -P "$PRINTER_E1_PPD" -E -o printer-is-shared=false -D "$PRINTER_E1_NAME"
echo "adding printer e1 done"
fi
fi
done <<< "$AD_GROUPS"
The content of $AD_GROUPS is:
Printer_E0
Printer_E1
Printer_E2
Printer_E3
Printer_E4
Printer_Strasse
Printer_Wien
I have such a loop for 5 printers, so 5 times that just with different variables.
How could I do that with one loop? (or how can I make that different or simpler)?
Something like this:
while IFS= read -r printer; do
[[ "$FIND_PRINTERS" =~ "${printer}_IP" ]] && \
echo "Found ${printer}" && continue
echo "Adding ${printer}..."
"$LPADMIN" -p "${printer}_IP" \
-v "lpd://${printer}_IP" \
-L "${printer}_LOCATION" \
-P "${printer}_PPD" -E -o printer-is-shared=false \
-D "${printer}_NAME" \
&& echo "Done"
done <<< "$AD_GROUPS"
I assume your variable FIND_PRINTERS has some printers you want to skip, that you have already set the parameters (IP, LOCATION etc) related to each printer.
We use the variable inside double quotes into there, so it expands to what you want for the various commands. Also I have simplified the if condition then command to condition && command and also continue moves to next iteration.

sh conditionally pass an option to command

I want to do something like:
#!/bin/sh
[ -f "/tmp/nodes" ]
[[ $? -eq 0 ]] && VAL=$? ||
geth --datadir /root/.ethereum \
${VAL+"--nodekey \"/root/nodekey.txt\""} \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0" \
I want the option --nodekey "/root/nodekey.txt" to be passed if the file /tmp/nodes exists. How can that be done more elegantly than an if with two nearly identical commands?
--EDIT--
This is the best I've been able to get working so far:
if [ $VAL -eq 0 ]; then
/geth --datadir /root/.ethereum \
--nodekey "/root/nodekey.txt" \
# No dice
# Would be nice if this worked so I didn't need the if
# ${VAL+ --nodekey "/root/nodekey.txt" } \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0"
else
/geth --datadir /root/.ethereum \
--networkid 1999 \
--rpc \
--rpcaddr "0.0.0.0" \
fi
This is another line in the file and works fine:
ENODE_URL=$(/geth --datadir /root/.ethereum ${VAL+ --nodekey "/root/nodekey.txt"} --exec "${JS}" console 2>/dev/null | sed -e 's/^"\(.*\)"$/\1/')
There's a bashism here, but it's [[ $? -eq 0 ]], as [[ is a ksh extension adopted by bash. There's no point to using $? at all here, since you can just directly perform your assignment based on whether the test -f succeeds:
touch /tmp/nodes # set us up for the truthy path
if test -f /tmp/nodes; then tmp_nodes_exists=1; else unset tmp_nodes_exists; fi
printf '%s\n' /tmp/nodes ${tmp_nodes_exists+"REALLY EXISTS" "(yes, really)"}
...properly emits as output (as run with dash, perhaps the most common minimal /bin/sh interpreter):
/tmp/nodes
REALLY EXISTS
(yes, really)
By contrast, to demonstrate that the other path fails as it should:
rm -f -- /tmp/nodes # set us up for the falsey path
if test -f /tmp/nodes; then tmp_nodes_exists=1; else unset tmp_nodes_exists; fi
printf '%s\n' /tmp/nodes ${tmp_nodes_exists+"REALLY EXISTS" "(yes, really)"}
emits as output only:
/tmp/nodes

Conditional in Docker argument in Bash

In my bash file I've something like this
docker run -d \
--network=host \
--name my-service \
--log-driver="$LOGGING" \
if [[ "$LOGGING" == 'splunk' ]]; then
echo "--log-opt tag={{.ImageName}}/{{.Name}}/{{.ID}} \\";
echo "--log-opt env=NODE_ENV \\";
fi
But shellcheck complains by showing the following result. Any idea?
https://github.com/koalaman/shellcheck/wiki/SC1089
Build the argument list first (in an array), then call docker. This has the additional benefit of getting rid of the ugly line continuation characters.
docker_opts=(
-d
--network=host
--name my-service
--log-driver="$LOGGING"
--log-opt="$log_opt"
)
if [[ $LOGGING == splunk ]]; then
docker_opts+=(
--log-opt "tag={{.ImageName}}/{{.Name}}/{{.ID}} \\"
--log-opt "env=NODE_ENV \\"
)
fi
docker run "${docker_opts[#]}"
The main idea, though, is to keep the conditional code as small as possible and keep it separate from the unconditional code.
I suggest to use $(if ..; then ...; fi):
docker run -d \
--network=host \
--name my-service \
--log-driver="$LOGGING" \
$(if [[ "$LOGGING" == 'splunk' ]]; then
echo "--log-opt tag={{.ImageName}}/{{.Name}}/{{.ID}}"
echo "--log-opt env=NODE_ENV"
fi)

How to securely quote an optional flag?

If $FOO is set, I want to run:
cd "$OUTPUTDIR" && fpm -s dir -t rpm \
-a x86_64 \
--epoch "${PKGEPOCH}" \
-n "${PACKAGENAME}" \
--version "${PKGVERSION}" \
--iteration "${PKGRELEASE}" \
-C "$OUTPUTDIR/installroot" \
--description="${PKGDESCRIPTION}" \
.
If $FOO is not set, I don't want to include the flag at all.
The program fails if --description= (empty).
However, sometimes descriptions include quotes and other special characters so I don't want to do:
if [[ -z "PKGDESCRIPTION" ]]; then
D=--description="${PKGDESCRIPTION}"
fi
cd "$OUTPUTDIR" && fpm -s dir -t rpm \
-a x86_64 \
--epoch "${PKGEPOCH}" \
-n "${PACKAGENAME}" \
--version "${PKGVERSION}" \
--iteration "${PKGRELEASE}" \
-C "$OUTPUTDIR/installroot" \
$D
.
If I put quotes around $D then it becomes an additional (blank) arg.
Is there a way to do this that won't be a security problem if $PKGDESCRIPTION includes special chars AND doesn't generate a blank arg?
Using an array is the only sane way to do this:
options=( -a x86_64 -C "$OUTPUTDIR/installroot" )
[[ $PKGEPOCH ]] && options+=( --epoch "$PGKEPOCH" )
[[ $PACKAGENAME ]] && options+=( -n "$PACKAGENAME" )
[[ $PKGVERSION ]] && options+=( --version "$PKGVERSION" )
[[ $PKGRELEASE ]] && options+=( --iteration "$PKGRELEASE" )
[[ $PKGDESCRIPTION ]] && options+=( --description="$PKGDESCRIPTION" )
cd "$OUTPUTDIR" && fpm -s dir -t rpm "${options[#]}"
See also http://mywiki.wooledge.org/BashFAQ/050
If PKGDESCRIPTION is the only argument that needs this conditional treatment, you can use an "alternate value" expansion:
[...] && fpm -s dir -t rpm \
[...] \
${PKGDESCRIPTION:+ --description="${PKGDESCRIPTION}"} \
.
Explanation: the :+ means this will expand to nothing at all unless PKGDESCRIPTION is set to a non-null value; if it is set to something non-null, it expands to --description="${PKGDESCRIPTION}", and the double-quotes make it ignore special characters in PKGDESCRIPTION's value. Note that the space in :+ -- isn't needed, but does no harm and makes it at least slightly easier to read.
BTW, if more than one argument needs this treatment, I'd go with #glenn jackman's approach.

Bash: Batch Resize Images

I'm working on a Bash script that will take an array of directories, iterate through it, create a directory named "processed" inside each directory, and run a command on each file within the directory. Here's my code (read the comment in the code to see what I'm stuck on). Any ideas?
#!/bin/bash
command -v convert >/dev/null 2>&1 || {
echo >&2 "Convert is not installed. Aborting.";
exit 1;
}
declare -a directories_to_process=(
"$HOME/Desktop/Album 1"
"$HOME/Desktop/Album 2"
"$HOME/Desktop/Album 3"
);
for directory in "${directories_to_process[#]}"
do
if [ -d "$directory" ]; then
if [ ! -d "$directory/processed" ]; then
mkdir "$directory/processed"
fi
# Insert code to run the following command on each file in $directory:
#
# convert $directory/$filename -resize 108x108^ -gravity center -extent 108x108 $directory/processed/$filename
fi
done
UPDATE:
Here is the working script:
#!/bin/bash
command -v convert >/dev/null 2>&1 || {
echo >&2 "Convert is not installed. Aborting.";
exit 1;
}
directories_to_process=(
"$HOME/Desktop/Album 1"
"$HOME/Desktop/Album 2"
"$HOME/Desktop/Album 3"
);
for directory in "${directories_to_process[#]}"
do
[[ -d $directory ]] || continue
mkdir -p "$directory/processed"
for infile in "$directory"/*.jpg
do
outfile="$directory/processed/${infile##*/}"
convert "$infile" \
-resize '108x108^' \
-gravity center \
-extent 108x108 \
"$outfile"
done
done
Add this in the commented-out area:
for infile in "$directory"/*; do
outfile="$directory/processed/${infile##*/}"
convert "$infile" \
-resize '108x108^' \
-gravity center \
-extent 108x108 \
"$outfile"
done
A few other notes:
Instead of nesting a great deal of logic inside of if [ -d "$directory" ], consider putting [[ -d $directory ]] || continue at the top of the loop to reduce the nesting depth. (Unlike [ ], quoting is not needed inside [[ ]] in this case).
Instead of testing [ ! -d "$directory/processed" ] and using that to decide whether to create the directory, consider unconditionally running mkdir -p "$directory/processed", which will simply exit with a successful status if the directory already exists.
Consider replacing command -v convert with type convert, which is somewhat better known than the command -v syntax but will have the same effect.
You don't need the declare -a when declaring an array variable outside of a function; simply directories_to_process=( ... ) will work.

Resources