How can I use bash-only syntax in CMD in a Dockerfile? - bash

I am creating a Dockerfile which starts a Java application. This Java application is given a file path which contains the output of ls -l.
(Note that in my real Dockerfile I am not doing ls - l but rather complex commands. I altered that to ls - l to simplify the question.)
I tried the following:
FROM openjdk:8-jre
ARG JAR
COPY target/$JAR /app/app.jar
CMD java -jar /app/app.jar <( ls -l )
This bash <( ... ) construction should create a temporary file containing the output of ls -l.
When starting the Docker file, I get:
/bin/sh: 1: Syntax error: "(" unexpected
Now, sh does not support the <( ... ) construction, hence the error. How can I start the application safely via bash instead of sh? With safely I mean that the Java app still will receive all OS signals (SIGHUP, ...) and react appropriately.

Replace your command with a JSON list, for which the first two elements are bash -c, and the last element is the shell command you actually want to run.
CMD ["bash", "-c", "exec java -jar /app/app.jar <( ls -l )"]
To generate such an array for a more complex command, you might consider using jq to ensure that syntax is correct, even for input with backslashes, newlines or quotes:
jq -cnRs '["bash", "-c", input]' <<'EOF'
# you can put other shell commands here if you need to
exec java -jar /app/app.jar <( ls -l )
EOF
exec ensures that the java instance replaces bash, and thus is sent signals directly.

If you're doing complex things on startup it's often easier to write them into a script than try to build a very complicated command line. Once you're doing that, you can use the set of primitives that are available in the POSIX shell standard, even if they require multiple commands to do things that GNU bash could do inline.
For this I might write a script:
#!/bin/sh
ls -l >/ls-l.txt
exec java -jar /app/app.jar /ls-l.txt
and then copy it in as the default thing your image runs
FROM openjdk:8-jre
ARG JAR
COPY target/$JAR /app/app.jar
COPY launch-app.sh /usr/bin/app
# RUN chmod +x /usr/bin/app, if it's not already executable
CMD ["app"]

Related

Running an if statement in shell script as a single line with docker -c option

I need to run below code as a single line in docker run -it image_name -c \bin\bash --script with --script below
(dir and dockerImageName being parameters)
'''cd ''' + dir+ ''' \
&& if make image ''' + dockerImageName''' 2>&1 | grep -m 1 "No rule to make target"; then
exit 1
fi'''
How can this be run as a single line?
You can abstract all of this logic into your higher-level application. If you can't do this, write a standard shell script and COPY it into your image.
The triple quotes look like Python syntax. You can break this up into three parts:
The cd $dir part specifies the working directory for the subprocess;
make ... is an actual command to run;
You're inspecting its output for some condition.
In Python, you can call subprocess.run() with an array of arguments and specify these various things at the application level. The array of arguments isn't reinterpreted by a shell and so protects you from this particular security issue. You might run:
completed = subprocess.run(['make', 'image', dockerImageName],
cwd=dir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
if 'No rule to make target' in completed.stdout:
...
If you need to do this as a shell script, doing it as a proper shell script and making sure to quote your arguments again protects you.
#!/bin/sh
set -e
cd "$1"
if make image "$2" 2>&1 | grep -m 1 "No rule to make target"; then
exit 1
fi
You should never construct a command line by combining strings in the way you've shown. This makes you vulnerable to a shell injection attack. Especially if an attacker knows that the user has permissions to run docker commands, they can set
dir = '.; docker run --rm -v /:/host busybox cat /host/etc/shadow'
and get a file of encrypted passwords they can crack at their leisure. Pretty much anything else is possible once the attacker uses this technique to get unlimited root-level read/write access to the host filesystem.

How to translate an alias into a real file?

Most of the time, an alias works well, but some times, the command is executed by other programs, and they find it in the PATH, in this situation an alias not works as well as a real file.
e.g.
I have the following alias:
alias ghc='stack exec -- ghc'
And I want to translate it into an executable file, so that the programs which depending on it will find it correctly. And the file will works just like the alias does, including how it process it's arguments.
So, is there any tool or scripts can help doing this?
Here is my solution, I created a file named ghc as following:
#!/bin/sh
stack exec -- ghc "$#"
The reason why there is double quote around $# is explained here: Propagate all arguments in a bash shell script
So, is there any tool or scripts can help doing this?
A lazy question for a simple problem... Here's a function:
alias2script() {
if type "$1" | grep -q '^'"$1"' is aliased to ' ; then
alias |
{ sed -n "s#.* ${1}='\(.*\)'\$##\!/bin/sh\n\1 \"\${\#}\"#p" \
> "$1".sh
chmod +x "$1".sh
echo "Alias '$1' hereby scriptified. To run type: './$1.sh'" ;}
fi; }
Let's try it on the common bash alias ll:
alias2script ll
Output:
Alias 'll' hereby scriptified. To run type: './ll.sh'
What's inside ll.sh:
cat ll.sh
Output:
#!/bin/sh
ls -alF "${#}"

How to run bash script in the current directory from cmd-file by using Cygwin?

How to run bash script in the current directory from cmd-file by using Cygwin?
It doesn't work - my file script.cmd contains: https://stackoverflow.com/a/17204645/1558037
c:\cygwin64\bin\bash -l -c '%CD%/my_script.sh'
Output
E:\mydir>c:\cygwin64\bin\bash -l -c 'E:\mydir/my_script.sh'
/usr/bin/bash: E:mydir/my_script.sh: No such file or directory
Answer:
I can successfully use such commands:
c:\cygwin64\bin\bash -l -c "cd %CD:\=/%/; %CD:\=/%/my_script.sh"
c:\cygwin64\bin\bash -l -c "cd %CD:\=/%/; echo $PWD"
solution in two steps, first convert the %CD% with cygpath
then call bash with the converted path in POSIX format
FOR /F %%I IN ('c:\cygwin64\bin\cygpath -c -u %CD%') DO SET CDU=%%I
c:\cygwin64\bin\bash -l -c %CDU%/my_script.sh
In the returned error message you showed the backslash between E: and mydir disappeared, which lets me assume bash uses such as escape characters.
Windows Command Prompt (cmd) however uses backslashes as path separators, hence %CD% contains such. However, bash expects forward-slashes as path separators.
Therefore, to replace all backslashes by forward-slashes, use sub-string substitution, like this:
c:\cygwin64\bin\bash -l -c '%CD:\=/%/my_script.sh'
In case the single-quotes cause troubles as well, use double-quotes:
c:\cygwin64\bin\bash -l -c "%CD:\=/%/my_script.sh"

Bash error not recognizing asterisk

Hello I am trying to write a Bash Script that will loop through a directory, and run the files in that directory through a command line program.
Unfortunately when I run it I keep getting
/home/user/Documents/Original_Files/*.fastq.gz: No such file or directory
Here's my code
Origin=/home/user/Documents/Original_Files/*.fastq.gz
for a in "$Origin"
do
BASE=basename "$a"
nohup java -jar $
done
Use an array if you want to keep several values in a variable.
Origin=(/home/user/Documents/Original_Files/*.fastq.gz)
for a in "${Origin[#]}" ; do
BASE=$(basename "$a")
nohup java -jar "$BASE"...

cygwin bash forward slash converted to backslash

Cannot figure this one our or create a sample which can reproduce this.
Am trying using an external sh file to invoke the hadoop sh file
myfile.sh
# workingdir = /data1/some/path/lib
WORKINGDIR=`cygpath -p -w -m "$WORKINGDIR"`
# so the above in windows land is C:/some/path/lib
# HADOOPCMD essentially is a call to hadoop.sh
$HADOOPCMD jar $WORKINGDIR/myjar.jar
hadoop.sh
echo "$#" // still C:/some/path/lib
exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$#"
However when "$#" is expanded in cygwin the resultant path becomes C:\some\path\lib and then the hadoop.sh complaints that it cannot find the jar ... Not a valid JAR : C:\some\path\lib
However if I were to hardcode the path in myfile.sh ; it works
$HADOOPCMD jar C:/some/path/lib/myjar.jar
Question
why does the exec command's "$#" change the / to \ in the WORKINGDIR variable ?
why does the hardcoded path work ?
how to get this to work with variable substituion ?
Quoting the variable works "$WORKINGDIR"/myjar.jar

Resources