Context: I'm trying to write a shortcut for my daily use of the docker exec command. For some reasons, I'm experimenting the problem that my output is sometimes broken when I'm using a bash console inside a container (history messed up, lines overwrite each other as I'm writing, ...)
I read here that you could overcome this problem by adding some command before starting the bash console.
Here is a relevant excerpt of what my script does
#!/bin/bash
containerHash=$1
commandToRun='bash -c "stty cols $COLUMNS rows $LINES && bash -l"'
finalCommand="winpty docker exec -it $containerHash $commandToRun"
echo $finalCommand
$finalCommand
Here is the output I get:
winpty docker exec -it 0b63a bash -c "stty cols $COLUMNS rows $LINES && bash -l"
cols: -c: line 0: unexpected EOF while looking for matching `"'
cols: -c: line 1: syntax error: unexpected end of file
I read here that this had to do with parsing and expansion. However, I can't use a function or an eval command (or at least I didn't succeed in making it work).
If I execute the first output line directly in my terminal, it works without trouble.
How can I overcome this problem?
It's not Docker related, but Bash (In other words, the docker's part of the command works well, it's just bash grumbling on the container like it would grumble on your host):
Minimal reproducible error
cmd='bash -c "echo hello"'
$cmd
hello": -c: line 0: unexpected EOF while looking for matching `"'
hello": -c: line 1: syntax error: unexpected end of file
Fix
cmd='bash -c "echo hello"'
eval $cmd
hello
Answer
foo='docker exec -it XXX bash -c "echo hello"'
eval $foo
This will let you execute your command echo hello on your container, now if you want to add dynamic variables to this command (like echo $string) you just have to get rid of single quotes for double ones, to make this works you will have to escape inner double quotes:
foo="docker exec -it $container bash -c \"echo $variable\""
A complete example
FOO="Hello"
container=$1
bar=$2
cmd="bash -c \"echo $FOO, $bar\""
final_cmd="docker exec -it $container $cmd"
echo "running command: \"$final_cmd\""
eval $final_cmd
Let's take time to dig in,
$FOO is a static variable, in our case it works exactly like a regular variable, just to show you.
$bar is a dynamic variable which takes second command line argument as value
Because $cmd and $final_cmd uses only double quotes, variables are interpreted
Because we use eval $final_cmd command is well interpreted, bash is happy.
Finally, a usage example:
bash /tmp/dockerize.sh 5b02ab015730 world
Gives
running command: "docker exec -it 5b02ab015730 bash -c "echo Hello, world""
Hello, world
Related
If I run this command from the command line, it works as expected on the remote server:
ssh admin#example.com "docker exec -it -d tasks_live_work_ec2_test_server /bin/sh -c \"/usr/bin/nvim -c 'silent! call SetupInstantServer()'\""
However, if I try to execute it from a perl script, I get errors:
my $cmd = qq|docker exec -it -d ${image_name}_server /bin/sh -c \"/usr/bin/nvim -c 'silent! call SetupInstantServer()'\"|;
`ssh admin\#example.com "$cmd"`;
bash: -c: line 1: syntax error near unexpected token '('`
Escaping the parens with backslashes suppresses the error, but the SetupInstantServer function in vim never gets called.
What I would do, using 2 here-doc:
#!/usr/bin/perl
system<<PerlEOF;
ssh admin\#example.com<<ShellEOF
docker exec -it -d ${image_name}_server /bin/sh -c "
/usr/bin/nvim -c 'silent! call SetupInstantServer()'
"
ShellEOF
PerlEOF
You can decide to add quotes on a 'HereDoc' to prevent shell expansion or the need to escape #. Up to your needs.
Check perldoc perlop#Quote-and-Quote-like-Operators
Consider the following shell script, where POD is set to the name of a K8 pod.
kubectl exec -it $POD -c messenger -- bash -c "echo '$#'"
When I run this script with one argument, it works fine.
hq6:bot hqin$ ./Test.sh x
x
When I run it with two arguments, it blows up.
hq6:bot hqin$ ./Test.sh x y
y': -c: line 0: unexpected EOF while looking for matching `''
y': -c: line 1: syntax error: unexpected end of file
I suspect that something is wrong with how the arguments are passed.
How might I fix this so that arguments are expanded literally by my shell and then passed in as literals to the bash running in kubectl exec?
Note that removing the single quotes results in an output of x only.
Note also that I need the bash -c so I can eventually pass in file redirection: https://stackoverflow.com/a/49189635/391161.
I managed to work around this with the following solution:
kubectl exec -it $POD -c messenger -- bash -c "echo $*"
This appears to have the additional benefit that I can do internal redirects.
./Test.sh x y '> /tmp/X'
You're going to want something like this:
kubectl exec POD -c CONTAINER -- sh -c 'echo "$#"' -- "$#"
With this syntax, the command we're running inside the container is echo "$#". We then take the local value of "$#" and pass that as parameters to the remote shell, thus setting $# in the remote shell.
On my local system:
bash-5.0$ ./Test.sh hello
hello
bash-5.0$ ./Test.sh hello world
hello world
In a Dockerfile, I want to use process substitution:
RUN echo <(echo '$DATA:'"$DATA")
But docker build runs every RUN command with /bin/sh. Apparently being run as sh causes bash to switch to POSIX mode, which does not allow process substitution:
/bin/sh: -c: line 0: syntax error near unexpected token `('
I tried switching off POSIX mode:
RUN set +o posix && echo <(echo '$DATA:'"$DATA")
But it seems the syntax error happens even before the first command is run. Same if I replace && with ;.
Note that the command (even the one that I used as a simplified example here) contains both single and double quotes, so I can't simply prepend bash -c.
The used shell is actually a bash, but it is invoked as /bin/sh by docker:
Step 7 : RUN ls -l /bin/sh
---> Running in 93a9809e12a7
lrwxrwxrwx 1 root root 9 Dec 28 03:38 /bin/sh -> /bin/bash
If you are sure you have bash in your image being built, then you can change the shell invokation by using the SHELL command, which I described in another question.
You can use SHELL [ "/bin/bash", "-c" ]. Consider:
$ docker build --no-cache - < <(echo '
> FROM fedora
> RUN cat <(echo hello world)
> ')
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM fedora
---> ef49352c9c21
Step 2/2 : RUN cat <(echo hello world)
---> Running in 573730ced3a3
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `cat <(echo hello world)'
The command '/bin/sh -c cat <(echo hello world)' returned a non-zero code: 1
$ docker build --no-cache - < <(echo '
> FROM fedora
> SHELL ["/bin/bash", "-c"]
> RUN cat <(echo hello world)
> ')
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM fedora
---> ef49352c9c21
Step 2/3 : SHELL ["/bin/bash", "-c"]
---> Running in e78260e6de42
Removing intermediate container e78260e6de42
---> ff6ec782a9f6
Step 3/3 : RUN cat <(echo hello world)
---> Running in afbb42bba5b4
hello world
Removing intermediate container afbb42bba5b4
---> 25f756dcff9b
Successfully built 25f756dcff9b
Assuming your sh is not bash, you can't use process substitution in shell mode directly; you need to spawn a bash session (non-login, non-interactive here):
RUN [ "/bin/bash", "-c", "echo <(echo '$DATA:'\"$DATA\")" ]
Here i have used the json (aka exec) form to make sure the quotes are easily managed, here you just need to escape quotes around $DATA: \"$DATA\" -- to prevent json interpretation beforehand.
If your sh is in fact bash, this should do:
RUN "echo <(echo '$DATA:'"$DATA")"
Also this just outputs the file descriptor, i am not rally sure about your plan.
I am trying to do the following:
if ps aux | grep "[t]ransporter_pulldown.py" > /dev/null
then
echo "Script is already running. Skipping"
else
exec "sudo STAGE=production $DIR/transporter_pulldown.py" # this line errors
fi
$ sudo STAGE=production $DIR/transporter_pulldown.py works on the command line, but in a bash script it gives me:
./transporter_pulldown.sh: line 9:
exec: /Users/david/Desktop/Avails/scripts/STAGE=production
/Users/david/Desktop/Avails/scripts/transporter_pulldown.py:
cannot execute: No such file or directory
What would be the correct syntax here?
sudo isn't a command interpreter thus its trying to execute the first argument as a command.
Instead try this:
exec sudo bash -c "STAGE=production $DIR/transporter_pulldown.py"
This creates uses a new bash processes to interpret the variables and execute your python script. Also note that $DIR will be interpreted by the shell you're typing in rather than the shell that is being executed. To force it to be interpreted in the new bash process use single quotes.
This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 6 years ago.
I'm currently having problems to execute a command from a shell variable.
In general the following works as expected:
COMMAND="echo A"
echo $COMMAND
$COMMAND
produces:
echo A
A
But if I do
COMMAND="su aUser -s /bin/bash -c 'echo A'"
echo $COMMAND
$COMMAND
I get
su aUser -s /bin/bash -c 'echo A'
Password:
A': -c: line 0: unexpected EOF while looking for matching `''
A': -c: line 1: syntax error: unexpected end of file
If I enter the line
su aUser -s /bin/bash -c 'echo A'
directly it works as expected.
It seems my assumption that $COMMAND is equal to entering the content as command directly is wrong.
Questions
1) Does anyone know how I can run the command from a variable?
2) What exactly is the difference between
COMMAND="command"
$COMMAND
and
command
?
Arrays are useful to keep your parameters whole:
command=(su aUser -s /bin/bash -c 'echo A')
and invoke it exactly like this:
"${command[#]}"
You need eval.
$ eval $VARIABLE