I want to open a netcat connection and keep sending some text forever - bash

echo " The NC send commands on port 2612"
while :
do
echo "hello " | nc -q -1 <some IP> 2612
done
I want open a netcat session forever, and that is been achived by -q -1.
How can I send "hello" on the same channed in every 20 second.
my earlier script was as following, but that opens nc connection every time.
What I really want is to open connection once and send "echo hello" evey 20 sec.
while :
do
echo "hello " | nc 192.168.100.161 2612
sleep 20
done

while echo "hello"; do
sleep 20
done | nc -q 192.168.100.161 2612
Note that we moved the echo into the condition of the loop, so that we stop trying to run it if nc exits (causing the echos to fail).
If you want to be able to retain state (for instance, a counter) from inside the loop and access it after the pipeline exited, then things need to change a bit further:
#!/bin/bash
# ^^^^- process substitutions aren't part of POSIX sh
count=0
while echo "hello"; do
(( ++count ))
sleep 20
done > >(nc -q 192.168.100.161 2612)
echo "Ran $count loops before exiting" >&2
There, the redirection is done as a process substitution, and the loop takes place inside the same shell instance that continues after it exits. See BashFAQ #24 for more details on this problem and solution.

Related

Kill bash command when line is found

I want to kill a bash command when I found some string in the output.
To clarify, I want the solution to be similar to a timeout command:
timeout 10s looping_program.sh
Which will execute the script: looping_program.sh and kill the script after 10 seconds of execute.
Instead I want something like:
regexout "^Success$" looping_program.sh
Which will execute the script until it matches a line that just says Success in the stdout of the program.
Note that I'm assuming that this looping_program.sh does not exit at the same time it outputs Success for whatever reason, so simply waiting for the program to exit would waste time if I don't care about what happens after that.
So something like:
bash -e looping_program.sh > /tmp/output &
PID="$(ps aux | grep looping_program.sh | head -1 | tr -s ' ' | cut -f 2 -d ' ')"
echo $PID
while :; do
echo "$(tail -1 /tmp/output)"
if [[ "$(tail -1 /tmp/output)" == "Success" ]]; then
kill $PID
exit 0
fi
sleep 1
done
Where looping_program.sh is something like:
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Success"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
echo "Fail"
sleep 1;
But that is not very robust (uses a single tmp file... might kill other programs...) and I want it to just be one command. Does something like this exist? I may just write a c program to do it if not.
P.S.: I provided my code as an example of what I wanted the program to do. It does not use good programming practices. Notes from other commenters:
#KamilCuk Do not use temporary file. Use a fifo.
#pjh Note that any approach that involves using kill with a PID in shell code runs the risk of killing the wrong process. Use kill in shell programs only when it is absolutely necessary.
There are more suggestions below from other users, I just wanted to make sure no one came across this and thought it would be good to model their code after.
looping_program() {
for i in 1 2 3; do echo $i; sleep 1; done
echo Success
yes
}
coproc looping_program
while IFS= read -r line; do
if [[ "$line" =~ Success ]]; then
break
fi
done <&${COPROC[0]}
exec {COPROC[0]}>&- {COPROC[1]}>&-
kill ${COPROC_PID}
wait ${COPROC_PID}
Notes:
Do not use temporary file. Use a fifo.
Do not use tail -n1 to read last line. Read from the stream in a loop.
Do not repeat tail -1 twice. Cache the result.
Wait for pid after killing to synchronize.
When you're using a coprocess, use COPROC_PID to get the PID
When you're not using a coprocess, use $! to get the PID of a background process started from the current shell.
When you can't use $! (because the process you're trying to get a PID of was not spawned in the background as a direct child of the current shell), do not use ps aux | grep to get the pid. Use pgrep.
Do not use echo $(stuff). Just run the stuff, no echo.
With expect
#!/usr/bin/env -S expect -f
set timeout -1
spawn ./looping_program.sh
expect "Success"
send -- "\x03"
expect eof
Call it looping_killer:
$ ./looping_killer
spawn ./looping_program.sh
Fail
Fail
Fail
Success
^C
To pass the program and pattern:
./looping_killer some_program "some pattern"
You'd change the expect script to
#!/usr/bin/env -S expect -f
set timeout -1
spawn [lindex $argv 0]
expect -- [lindex $argv 1]
send -- "\x03"
expect eof
Assuming that your looping program exists when it tries to write to a broken pipe, this will print all output up to and including the 'Success' line and then exit:
./looping_program | sed '/^Success$/q'
You may need to disable buffering of the looping program output. See Force line-buffering of stdout in a pipeline and How to make output of any shell command unbuffered? for ways to do it.
See Should I save my scripts with the .sh extension? and Erlkonig: Commandname Extensions Considered Harmful for reasons why I dropped the '.sh' suffix.
Note that any approach that involves using kill with a PID in shell code runs the risk of killing the wrong process. Use kill in shell programs only when it is absolutely necessary.

How to adjust this bash script to run telnet commands successfully while being in SSH?

I am attempting to write a bash script that will do the following work flow:
Telnet into networked device via IP address on port 9100 telnet x.x.x.x 9100
Run SGD command ! U1 getvar \"internal_wired.ip.timeout.value\".
Expect output value of "10".
Here is the bash script I've written so far:
#!/bin/bash
IP=(x.x.x.x)
for i in ${IP}
do
echo " "
echo "Welcome! This script will check the timeout value of this networked device."
echo "The expected output should be `"10`". Let's get started!!"
echo " "
sleep 4
echo "5....."
sleep 1
echo "4...."
sleep 1
echo "3..."
sleep 1
echo "2.."
sleep 1
echo "1."
sleep 1
echo " "
telnet ${i} 9100 << END_SSH
sleep 5
getvar \"internal_wired.ip.timeout.value\"
sleep 5
END_SSH
done
When I run this script via bash mycode.sh, I get the following output in Terminal.app:
$ bash mycode.sh
Welcome! This script will check the timeout value of this networked device.
The expected output should be "10". Let's get started!!
5.....
4....
3...
2..
1.
Trying x.x.x.x...
Connected to x.x.x.x.
Escape character is '^]'.
Connection closed by foreign host.
[user#server ~]$
x.x.x.x is an IP placeholder just to add.
In theory, after the Escape character is '^]'. line, the script should have ran the ! U1 getvar "internal_wired.ip.timeout.value\" command.
Also, we should have had an expected output of "10".
When I first wrote this script, I initially did not have the END_SSH command in it. A colleague introduced that to me and said to wrap the telnet commands in the END_SSH because of how Terminal technically jumps out of SSH when you are in telnet. I've tried utilizing END_SSH, but am not successful.
How do I get the telnet command to run successfully and get the expected output value?
You misunderstand what "END_SSH" is. It's not a "command" - it's what's called "Here-document" in bash.
Essentially the text between the <<END_SSH and the END_SSH is a "here-document" that is piped into stdin of telnet ${i} 9100. So, the sleep 5 commands are never actually executed and the input reaches EOF before the connection is even established.
I don't know what exactly you are trying to accomplish, but I would guess that the following will work better. Oh, and what's with that weird IP=(x.x.x.x) declaration? Is that supposed to be an array?
#!/bin/bash
declare -a IP=(1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4)
for i in "${IP[#]}"; do
echo " "
echo "Welcome! This script will check the timeout value of this networked device."
echo "The expected output should be \"10\". Let's get started!!"
sleep 4
for j in {5..1}; do
echo $j
sleep 1
done
{ sleep 5; echo -n $'! U1 getvar "internal_wired.ip_timeout.value"\n'; sleep 5; } | telnet ${i} 9100
done
so here is what I suggest to use for the telnet part. Connect is a function being called later in a while loop, which will run over IPs ready from a file.
Connect()
{
(
sleep 10 # depending upon your network and device response, better to keep this first sleep value a little high
echo "command 1"
sleep 2
echo "command 2"
sleep 2
) | telnet $1 9100 | tee -a ${1}.log
}
while read -r IP
do
Connect $IP
done < filewithIPs

Creating a shell script to check network connectivity

I'm making a simple shell script that runs an infinite loop, then if the output of the ping command contains "time" (indicating that it pinged successfully) it should echo "Connected!", sleep 1, and clear. However, I get no output from my script.
#!/bin/bash
while :
do
if [[ $(ping google.com) == *time* ]];
then
echo -en '\E[47;32m'"\033[1mS\033[0m"
echo "Connected!"
else
echo -en '\E[47;31m'"\033[1mZ\033[0m"
echo "Not Connected!"
fi
clear
sleep 1
done
Your script is not giving output because ping never terminates. To get ping to test your connectivity, you'll want to give it a run count (-c) and a response timeout (-W), and then check its return code:
#!/bin/bash
while true
do
if ping -c 1 -W 5 google.com 1>/dev/null 2>&1
then
echo "Connected!"
break
else
echo "Not Connected!"
sleep 1
fi
done
ping will return 0 if it is able to ping the given hostname successfully, and nonzero otherwise.
It's also worth noting that an iteration of this loop will run for a different period of time depending on whether ping succeeds quickly or fails, for example due to no network connection. You may want to keep the iterations to a constant length of time -- like 15 seconds -- using time and sleep.

BASH catching <&3 into var

Trying to understand how the <&3 redirect can be caught into a var in bash.
When I run the code below it stops at line 9 with the MSG to SEND being printed as if the cat <&3 is being run without catching it in the var myRcvMsg. If I open another shell and nc on the port it continues to printout each line sent (this is what I want, just need to trap it rather than print it)
If I change line to myRcvMsg="$(<&3)" then lines 10,11 execute but no var prints out.
what am I doing wrong?
thx
Art
#!/bin/bash
echo "Starting Script"
exec nc -p 18000 -l &
sleep 1 # wait for port to open
exec 3<>/dev/tcp/192.168.1.1/18000
echo "MSG to SEND" >&3
echo "MSG has been sent"
myRcvMsg="$(cat <&3)"
echo "MSG should have been RCV'd"
echo "This is the RCV msg:${myRcvMsg}"
The execution of nc puts it into listen mode, but it will write its output to stdout instead of echoing it back through the net. See Echo server with bash for ideas how to make it into an echo server.
On my machine I had to use 127.0.0.1 to get a connection.
Next problem is that you have to make sure that your message is not stuck in a buffer. While you wait on the out-end of &3 for nc to echo something, nc may not have actually seen your message. In my test the $(cat <&3) just hangs.
The following kind of works.
#!/bin/bash
set -x
echo "Starting Script"
exec nc -p 18000 -l -c "xargs -n1 echo" &
sleep 1 # wait for port to open
exec 3<>/dev/tcp/127.0.0.1/18000
cat <&3 >ttt &
sleep 1
echo "MSG to SEND" >&3
echo "MSG has been sent"
sleep 1
myRcvMsg=$(cat ttt)
echo "MSG should have been RCV'd"
echo "This is the RCV msg:\"${myRcvMsg}\""
For more information, see http://www.ict.griffith.edu.au/anthony/info/shell/co-processes.hints, where the buffering problem is highlighted in 3/ Buffered Output problem.
Try to read from
myRcvMsg="$(cat /dev/fd/3)"

Bash script: `exit 0` fails to exit

So I have this Bash script:
#!/bin/bash
PID=`ps -u ...`
if [ "$PID" = "" ]; then
echo $(date) Server off: not backing up
exit
else
echo "say Server backup in 10 seconds..." >> fifo
sleep 10
STARTTIME="$(date +%s)"
echo nosave >> fifo
echo savenow >> fifo
tail -n 3 -f server.log | while read line
do
if echo $line | grep -q 'save complete'; then
echo $(date) Backing up...
OF="./backups/backup $(date +%Y-%m-%d\ %H:%M:%S).tar.gz"
tar -czhf "$OF" data
echo autosave >> fifo
echo "$(date) Backup complete, resuming..."
echo "done"
exit 0
echo "done2"
fi
TIMEDIFF="$(($(date +%s)-STARTTIME))"
if ((TIMEDIFF > 70)); then
echo "Save took too long, canceling backup."
exit 1
fi
done
fi
Basically, the server takes input from a fifo and outputs to server.log. The fifo is used to send stop/start commands to the server for autosaves. At the end, once it receives the message from the server that the server has completed a save, it tar's the data directory and starts saves again.
It's at the exit 0 line that I'm having trouble. Everything executes fine, but I get this output:
srv:scripts $ ./backup.sh
Sun Nov 24 22:42:09 EST 2013 Backing up...
Sun Nov 24 22:42:10 EST 2013 Backup complete, resuming...
done
But it hangs there. Notice how "done" echoes but "done2" fails. Something is causing it to hang on exit 0.
ADDENDUM: Just to avoid confusion for people looking at this in the future, it hangs at the exit line and never returns to the command prompt. Not sure if I was clear enough in my original description.
Any thoughts? This is the entire script, there's nothing else going on and I'm calling it direct from bash.
Here's a smaller, self contained example that exhibits the same behavior:
echo foo > file
tail -f file | while read; do exit; done
The problem is that since each part of the pipeline runs in a subshell, exit only exits the while read loop, not the entire script.
It will then hang until tail finds a new line, tries to write it, and discovers that the pipe is broken.
To fix it, you can replace
tail -n 3 -f server.log | while read line
do
...
done
with
while read line
do
...
done < <(tail -n 3 -f server.log)
By redirecting from a process substitution instead, the flow doesn't have to wait for tail to finish like it would in a pipeline, and it won't run in a subshell so that exit will actually exits the entire script.
But it hangs there. Notice how "done" echoes but "done2" fails.
done2 won't be printed at all since exit 0 has already ended your script with return code 0.
I don't know the details of bash subshells inside loops, but normally the appropriate way to exit a loop is to use the "break" command. In some cases that's not enough (you really need to exit the program), but refactoring that program may be the easiest (safest, most portable) way to solve that. It may also improve readability, because people don't expect programs to exit in the middle of a loop.

Resources