Send keystroke to Dockerfile, Ubuntu - bash

I'm creating Dockerfile script and it has a command line that executes a program and requires user input 1 from keyboard as selected option to go to further steps.
Xdotool, man yes or expect cannot help in this situation.
Update source-code:
First off, download and extract RevoMath library, navigate to RevoMath folder then execute the install script.
...
RUN wget -q https://mran.microsoft.com/install/mro/3.2.4/RevoMath-3.2.4.tar.gz
RUN tar -xzf RevoMath-3.2.4.tar.gz
RUN cd RevoMath/
RUN ./RevoMath.sh
...
Install script has some select options as follow:
echo "1. Install MKL"
echo "2. Uninstall MKL"
echo "3. Exit utility"
We need to enter 1 from keyboard to install. How can we do it via Docker command?
Any help would be appreciated!

If I correctly understand you, you would like to add echo 1 | before ./RevoMath.sh in your Dockerfile:
...
RUN cd RevoMath/ && echo 1 | ./RevoMath.sh
...
BTW: In your example this lines will not work as you expected:
RUN cd RevoMath/
RUN ./RevoMath.sh
Because each RUN is an independent execution.
You should use && if you want to execute RevoMath.sh script from specific folder (see my example in the beginning)

I suggest to use redirect from standard input.
For example install.sh required some input(s) from user at execution time.
Suppose you need to enter 1 as a response to first interaction(questions) and
then you have another response as y for further interaction then it's good to use redirect from stdin.
$#>install.sh <EOF
$#>1
$#>y
$#>EOF
This way whenever script is waiting for inputs it will answer as 1 for the first question and y for the second question.

Related

Loop trough docker output until I find a String in bash

I am quite new to bash (barely any experience at all) and I need some help with a bash script.
I am using docker-compose to create multiple containers - for this example let's say 2 containers. The 2nd container will execute a bash command, but before that, I need to check that the 1st container is operational and fully configured. Instead of using a sleep command I want to create a bash script that will be located in the 2nd container and once executed do the following:
Execute a command and log the console output in a file
Read that file and check if a String is present. The command that I will execute in the previous step will take a few seconds (5 - 10) seconds to complete and I need to read the file after it has finished executing. I suppose i can add sleep to make sure the command is finished executing or is there a better way to do this?
If the string is not present I want to execute the same command again until I find the String I am looking for
Once I find the string I am looking for I want to exit the loop and execute a different command
I found out how to do this in Java, but if I need to do this in a bash script.
The docker-containers have alpine as an operating system, but I updated the Dockerfile to install bash.
I tried this solution, but it does not work.
#!/bin/bash
[command to be executed] > allout.txt 2>&1
until
tail -n 0 -F /path/to/file | \
while read LINE
do
if echo "$LINE" | grep -q $string
then
echo -e "$string found in the console output"
fi
done
do
echo "String is not present. Executing command again"
sleep 5
[command to be executed] > allout.txt 2>&1
done
echo -e "String is found"
In your docker-compose file make use of depends_on option.
depends_on will take care of startup and shutdown sequence of your multiple containers.
But it does not check whether a container is ready before moving to another container startup. To handle this scenario check this out.
As described in this link,
You can use tools such as wait-for-it, dockerize, or sh-compatible wait-for. These are small wrapper scripts which you can include in your application’s image to poll a given host and port until it’s accepting TCP connections.
OR
Alternatively, write your own wrapper script to perform a more application-specific health check.
In case you don't want to make use of above tools then check this out. Here they use a combination of HEALTHCHECK and service_healthy condition as shown here. For complete example check this.
Just:
while :; do
# 1. Execute a command and log the console output in a file
command > output.log
# TODO: handle errors, etc.
# 2. Read that file and check if a String is present.
if grep -q "searched_string" output.log; then
# Once I find the string I am looking for I want to exit the loop
break;
fi
# 3. If the string is not present I want to execute the same command again until I find the String I am looking for
# add ex. sleep 0.1 for the loop to delay a little bit, not to use 100% cpu
done
# ...and execute a different command
different_command
You can timeout a command with timeout.
Notes:
colon is a utility that returns a zero exit status, much like true, I prefer while : instead of while true, they mean the same.
The code presented should work in any posix shell.

How to record shell interaction from a background script

I want to write a background sh (it can be python or any other language actually) script very much like script.
Its main purpose is to run on the background after invoked and listen to inputs (not the outputs nor the PS1 shell prompt string nor keystrokes like "End", "Ctrl", arrow-keys, etc.) that the user enters to the shell from which the script was originally invoked and then record them.
The dumbest approach would use script and then try to subtract PS1 and outputs from the generated file. But this would be error-prone and there sure are better ways to do this.
I've read about pam_tty_audit but I'm not sure if one can easily filter out just the user input and I'm afraid it won't work for all Linux distributions.
What else can I look into to accomplish that?
A quick example illustrating what I seek:
The user would input this to the shell:
$ ./myscript
MyScript started in the background
$ echo "foo"
$ sudo apt install netcat
[sudo] password for user: MYPASSWORD
...
Do you want to continue? [Y/n] NO
...
$ exit
MyScript exited
And my script would render this output file:
#!/bin/bash
eval "echo \"foo\""
echo "NO" | echo "MYPASSWORD" | eval "sudo apt install netcat"
Obs.: Capturing the password typed for sudo is not really something I would like to perform, the above example is just the first which came to mind.

Simple if statement in bash doesn't work

I am learning a little bit of bash in Linux and I just can't understand why this doesn't work. It is a simple IF statement and a read command to keep the window opened. What happens is that when I execute the .sh file the terminal's window opens for a second and closes back. I can't see any message or check whether there's any error or why it doesn't work. If I remove the IF block then I can see the message and the window remains opened. This is the code inside my file
count=99
if [ $count -eq 100 ]; then
echo "Count is 100"
else
echo "Count is not 100"
fi
read -p "Press enter to continue" nothing
I tried many other ways of using the IF structure but seems like none works
Use the dos2unix utility to convert the text file created on Windows to the correct format for Linux. See this wikipedia page for more details.
Install if necessary:
$ sudo apt-get install dos2unix
<snip>
Setting up dos2unix (5.3.1-1) ...
$
Run it on your script:
$ dos2unix if.sh
dos2unix: converting file if.sh to Unix format ...
$
Your script is completely correct. atleast its okai in my linux mint

Leaving a Command Running on a Remote Server

Forgive me if this is something I've just completely missed, however, I have a remote server (a NAS) that I'd like to start a command running on, while I do some work locally. Now, I believe I could probably do this with a command like:
ssh foo#bar 'cp -Rl /foo/bar /bar/foo'
However, I need a return value in my main script from part of the command, so I need it to return but leave the cp command running. For example:
foo=$(ssh foo#bar <<- REMOTE_COMMANDS
cp -Rl /foo/bar /bar/foo &
echo "foobar"
REMOTE_COMMANDS)
However I don't believe this returns until the cp command has completed, but if I use exit I think the cp is interrupted?
Is there another way to leave cp running, or will I need to run two ssh commands (one for the cp, one to get the return value I need?)
You can use one the following choices :
tmux
nohup
screen
tmux & screen are some complete environments that can be attached and detached for 1 to N users.
If you need something straightforward, look nohup first.
You can use screen command.
Simply create a new screen using : screen -R screen_name.
Run your command or code and then exit that screen by pressing ctrl + a + d.
If you want to switch back to the screen, enter this command : screen -r screen_name.
Hope it helps.

Formatting Expect Script Inside of User Command in Bash Script

So I have three installers for NVIDIA's CUDA API -- the first is a driver and comes with nice silent install flag options (but you have to be root and have to have run level 3).
The second two follow are shown manually installing below (cut out the long mess of install afterwards for brevity)
[root]# sh cudatoolkit_4.1.28_linux_64_rhel5.x.run Verifying archive
integrity... All good. Uncompressing NVIDIA
CUDA.............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Enter install path (default /usr/local/cuda, '/cuda' will be
appended): A previous version of CUDA was found in /usr/local/cuda/bin
Would you like to uninstall? (yes/no/abort): yes
In other words, I need to recognize:
"Enter install path" and output a '\n'
Now the tricky part is the uninstall may not be always be there. If it's not I need to simply wait for the install to finish, but if I see "Would you like to uninstall?" I need to output "yes" to complete.
The third and final installer's output is shown below....
[root]# sh gpucomputingsdk_4.1.28_linux.run Verifying archive
integrity... All good. Uncompressing NVIDIA GPU Computing
SDK............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Enter install path (default ~/NVIDIA_GPU_Computing_SDK):
/usr/local/CUDA_SDK Located CUDA at /usr/local/cuda If
this is correct, choose the default below. If it is not correct,
enter the correct path to CUDA Enter CUDA install path
(default /usr/local/cuda):
For this one, there is no uninstall action so it's seemingly a bit simpler.
I just need to detect "Enter install path" and output "/usr/local/CUDA_SDK\n" and then detect "Enter CUDA install path" and output "\n"
My idea was to use a pair of expect scripts -- one for each installer -- but due to the nesting within the double quotes of the command to switch to root, I'm having some difficulties with this. What I currently have is:
#!/bin/bash
CR="\"\n\""
YES="\"Yes\""
INSTALL_PATH_REQUEST="\"Enter install path\""
CUDA_PATH_REQUEST="\"Enter CUDA install path\""
UNINSTALL_REQUEST="\"Would you like to uninstall?\""
TOOLKIT=`ls -t cudatoolkit* | head -n 1`
TOOLKIT_EXPECT="sh $TOOLKIT"
SDK=`ls -t gpucomputingsdk* | head -n 1`
SDK_INSTALL_PATH="\"/usr/local/CUDA_SDK\n\""
SDK_EXPECT="sh $SDK"
/bin/su root -c "yum -q -y install expect expectk;
/sbin/init 3; sh `ls -t NVIDIA*|head -n 1` -s --update -a -X;
/usr/bin/expect <<EOF;
spawn $TOOLKIT_EXPECT
expect $INSTALL_PATH_REQUEST
send $CR
expect $UNINSTALL_REQUEST
send $YES
EOF
/usr/bin/expect <<EOF;
spawn $SDK_EXPECT
expect $INSTALL_PATH_REQUEST
send $SDK_INSTALL_PATH
expect $CUDA_PATH_REQUEST
send $CR
EOF
/sbin/init 5"
This switches to root properly (once the password is entered) and installs the driver with the built in options correctly. It then appears to spawn the second install process and enter the first argument (a carriage return), but seems to exit the second installer prematurely (e.g. I don't see the "yes" option.).
I feel like I'm pretty close, hopefully somebody can point me to where I'm going wrong and suggest the correct syntax.
NOTES:
I added the yum install command, as some of the machines I'm installing on didn't have expect (stock CentOS 6), so that saves me the trouble there....
Might be an issue with timeout here... not sure how long the installer takes.
The default expect timeout is 10 seconds, if it doesn't see the expected text in that time, it will proceed regardless, you could change the timeout values like so:
expect -timeout 100 $INSTALL_PATH_REQUEST
Also change your $YES from
YES="\"Yes\""
To:
YES="\"Yes\r\""
(Best to use \r instead of \n in $CR too)
It's also a good idea to expect some 'safety string' at the end of the install, for example:
expect -timeout 320 "Install Complete."
So the expect script doesn't terminate before the spawned process is complete.

Resources