In bash how can I issue a command to a running process I just started?
For example;
# Start Bluez gatttool then Connect to bluetooth device
gatttool -b $MAC -I
connect # send 'connect' to the gatttool process?
Currently my shell script doesn't get to the connect line because the gatttool process is running.
If you simply want to send the string "connect\n" to the process, you can use a standard pipe:
echo "connect" | gatttool -b $MAC -I
If you want to engage in a more complex "conversation" with the gatttool process, take a look at the expect (1) and chat (8) tool, which allow you to send a sequence of strings, and wait for certain responses.
If you'd prefer a slightly "lighter" way of piping you could use a heredoc such as in:
gatttool -b $MAC -I <<EOF
connect
(...)
EOF
Everything contained between the two EOF tags will be piped to the command's input. I believe this will not allow you to interact with the command whilst between the EOF tags so, as mentioned in the previous answer, you might want to consider using expect if you need to act upon the commands' output before sending something back to it.
Related
I'm working on a bash script to connect to a server via SSH that is running sish (https://github.com/antoniomika/sish). This will essentially create a port forward on the internet like ngrok using only SSH. Here is what happens during normal usage.
The command:
ssh -i ./tun -o StrictHostKeyChecking=no -R 5900:localhost:5900 tun.domain.tld sleep 10
The response:
Starting SSH Forwarding service for tcp:5900. Forwarded connections can be accessed via the following methods:
TCP: tun.domain.tld:43345
Now I need to send the ssh command to the background and figure out some way of capturing the response from the server as a variable so that I can grab the port that sish has assigned and send that somewhere (probably a webhook). I've tried a few things like using -f and piping to a file or named pipe and trying to cat it, but the issue is that the piping to the file never works and although the file is created, it's always empty. Any assistance would be greatly appreciated.
If you're running a single instance of sish (and the tunnel you're attempting to define) you can actually have sish bind the specific part you want (in this case 5900).
You just set the --bind-random-ports=false flag on your server command in order to tell sish that it's okay to not use random ports.
If you don't want to do this (or you have multiple clients that will expose this same port), you can use a simple script like the following:
#!/bin/bash
ADDR=""
# Start the tunnel. Use a phony command to tell ssh to clean the output.
exec 3< <(ssh -R 5900:localhost:5900 tun.domain.tld foobar 2>&1 | grep --line-buffered TCP | awk '{print $2; system("")}')
# Get our buffered output that is now just the address sish has given to us.
for i in 1; do
read <&3 line;
ADDR="$line"
done
# Here is where you'd call the webhook
echo "Do something with $ADDR"
# If you want the ssh command to continue to run in the background
# you can omit the following. This is to wait for the ssh command to
# exit or until this script dies in order to kill the ssh command.
PIDS=($(pgrep -P $(pgrep -P $$)))
function killssh() {
kill ${PIDS[0]}
}
trap killssh EXIT
while kill -0 ${PIDS[0]} 2> /dev/null; do sleep 1; done;
sish also has an admin api which you can scrape. The information on that is available here.
References: I build and maintain sish and use it myself (as well as a similar type of script).
I have a USB LTE modem connected to my Raspberry and I need to read replies sent via serial line, generated by requests sent using the "echo" command.
Example:
cat /dev/ttyUSB0 &>> /ttyUSB0_logs &
echo "AT+csq" > /dev/ttyUSB0
echo "AT+cgreg=2" > /dev/ttyUSB0
echo "AT+cgreg?" > /dev/ttyUSB0
The problem is, although the "cat" command should run on the background and all output is directed to the file, script still freezes at this point. If I use the first command outside of the script, it works as I expect - it stores all output to the file ttyUSB0_logs on the background and I can use the received data for other operations. The question is - how can I integrate the first command to the script to get it work this way? Thanks a lot.
you want:
cat /dev/ttyUSB0 >> /ttyUSB0_logs &
if that doesn't work, you should double check what is actually freezing. you can put set -x at the top of the script to get tracing output.
I have the following scenario:
I use netcat to connect to a host running telnet server on port 23, I log in using provided username and password, issue some commands, after which I need to do fairly complex analysis of the provided output. Naturally, expect comes to mind, with a script like this:
spawn nc host 23
send "user\r"
send "pass\r"
send "command\r"
expect EOF
then, it is executed with expect example.scr >output.log, so the output file can be parsed. The parser is 150+ lines of bash code that executes under 2 seconds, and makes a decision what command should be executed next. Thus, it replaces "command" with "command2", and executes the expect script again, like this:
sed -i '/send "command\r"/send "command2\r"/' example.scr
expect example.scr >output.log
Obviously, it is not needed to re-establish telnet connection and perform log in process all over again, just to issue a single telnet command after 2 seconds of processing. A conclusion can be made, that telnet session should be kept alive as a background process, so one could freely talk to it at any given time. Naturally, using named pipes comes to mind:
mkfifo in
mkfifo output.log
cat in | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" >in
cat output.log
After the file is written to, EOF causes the named pipe to close, thus terminating the telnet session. I was thinking what kind of eternal process could be piped to netcat so it can be used as telnet relay to host. I came up with a very silly idea, but it works:
nc -k -l 666 | nc host 23 >output.log &
echo -e "user\npass\ncommand\n" | nc localhost 666
cat output.log
The netcat server is started with k(eep alive), listening on port 666, and any data stream is redirected to the netcat telnet client connected to the host, while the entire conversation is dumped to output.log. One can now echo telnet commands to nc localhost 666, and read the result from output.log.
One should keep in mind that the expect script can be easily modified to accommodate SSH and even serial console connection, just by spawning ssh or socat instead of netcat. I never liked expect because it forces a use of another scripting language within bash, requires tcl libraries, and needs to be compiled for the embedded platforms, while netcat is a part of busybox and readily available everywhere.
So, the question is - could this be done in a simpler way? I'd put my bet on having some sort of link between console and TCP socket. Any suggestions are appreciated.
How about using like a file descriptor?
exec 3<>/dev/tcp/host/port
while true; do
echo -e "user\npass\ncommand" >&3
read_response_generate_next_command <&3 >&3
# if no more commands, break;
done
exec 3>&-
So, I've established a connection via ssh to a remote machine; and now what I would like to do is to execute few commands, grab some files and copy them back to my host machine.
I am aware that I can run
ssh user#host "command1; command2;....command_n"
and then close the connection, but how can I do the same without use the aforememtioned syntax? I have a lot of complex commands that has a bunch of quote and characters that would be a mess to escape.
Thanks!
My immediate thought is why not create a script and push it over to the remote machine to have it run locally in a text file? If you can't for whatever reason, I fiddled around with this and I think you could probably do well with a HEREDOC:
ssh -t jane#stackoverflow.com bash << 'EOF'
command 1 ...
command 2 ...
command 3 ...
EOF
and it seems to do the right thing. Play with your heredoc to keep your quotes safe, but it will get tricky. The only other thing I can offer (and I totally don't recomend this) is you could use a toy like perl to read and write to the ssh process like so:
open S, "| ssh -i ~/.ssh/host_dsa -t jane#stackoverflow.com bash";
print S "date\n"; # and so on
but this is a really crummy way to go about things. Note that you can do this in other languages.
Instead of the shell use some scripting language (Perl, Python, Ruby, etc.) and some module that takes care of the ugly work. For example:
#!/usr/bin/perl
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($host, user => $user);
$ssh->system('echo', 'Net::Open$$H', 'Quot%$', 'Th|s', '>For', 'You!');
$ssh->system({stdout_file => '/tmp/ls.out'}, 'ls');
$ssh->scp_put($local_path, $remote_path);
my $out = $ssh->capture("find /etc");
From here: Can I ssh somewhere, run some commands, and then leave myself a prompt?
The use of an expect script seems pretty straightforward... Copied from the above link for convenience, not mine, but I found it very useful.
#!/usr/bin/expect -f
spawn ssh $argv
send "export V=hello\n"
send "export W=world\n"
send "echo \$V \$W\n"
interact
I'm guessing a line like
send "scp -Cpvr someLocalFileOrDirectory you#10.10.10.10/home/you
would get you your files back...
and then:
send "exit"
would terminate the session - or you could end with interact and type in the exit yourself..
I'm new in this of bash programming in linux, basically what I want to do is to program a bash file that can open the port ttyUSB0 and then I need to interrogate it with AT commands (like "0100") and then assign the response to a variable, I've been trying this with this different ways:
1) Using cat
#!/bin/bash
PORT= \ls /dev/ttyU*
cat $PORT
????
2) Using Minicom
`#!/bin/bash
minicom
????
'
3) Using Screen
#!/bin/bash
PORT= \ls /dev/ttyU*
screen $PORT
????
How can I interrogate it before the cat, minicom and screen starts? What should I have to put in ???? of the 3 different codes?
Thank you so much!!!
Don't try writing to a tty device using bash, you'll end up chasing your own tail forever. Use minicom or C-Kermit for that.
If you want to check that the device is active before starting minicom, you can read from it with bash and there is a good explanation of how to achieve this here: Bash read from ttyUSB0 and send to URL
You should be able to use my atinout program for this. It is a command line tool to talk with a modem:
$ echo AT | atinout - /dev/ttyUSB0 -
AT
OK
$
So with a little bit of scripting you should be able to extract the response you want (remember to always check for a successful OK response).