Bash scripting assign ALL output from command to variable - bash

I'm trying to get both successful and unsuccessful responses from a ping in a bash script but am unable to thus far.
My code looks like this...
ping_results=$(ping -c 4 -q google.com)
This works when the ping is successful, but if I don't have an internet connection and I get the result
ping: unknown host google.com
It is printed to the console, and my script appears to exit.
I want both the ping result or error to be stored in the ping_results variable.
Any help will be appreciated.

Okay, the simple answer to your question is to redirect stderr to stdout. as what Fredik Phil mentioned in the comments.
instead of:
ping_results=$(ping -c 4 -q google.com);
use:
ping_results=$(ping -c 4 -q google.com 2>&1);
or something similar...
However, depending on what you're doing, it might be better to test if the exit code of the ping command is 1 (indicating that the ping is ending in an error), or 0 (indicating that the ping is successful).
the exit code is stored in the variable "$?".

Related

How to check remote servers can ping each other using a script?

I need to write a script which checks whether 3 servers can ping each other. I run the script at my local linux host.
Here is what I plan to do:
ssh root#10.238.155.155 "ping -c 1 10.20.77.1"
echo $?
0
In the above example, 10.238.155.155 is one server, the command login to this server and ping 10.20.77.1 which is an interface at another server.
Then I check the command return value, $?, if it is 0, then it means ping is good.
ssh root#10.238.155.155 "ping -c 1 10.20.77.9"
echo $?
1
In this example, 10.20.77.9 does not exist, we can see $? is 1.
My script basically repeats running SSH login to each server, ping other servers and checks $?.
Do you think this is a reliable solution?
With echo $? you are checking the return of ssh, which is not what you want.
Try capturing the output of the compound ssh command in a variable, then parsing that variable as needed
myvar=$(ssh root#place.com "ping -c 5 server1.place.com")

Bash script to ping host with while condition

First, I would like to say that I know nothing about bash but I am trying to learn through practice.
So, I am trying to make a script which will send a magic packet to a remote host. While the remote host is starting I would like to print dots on the display.
I really don't have a problem with the wakeonlan part and of course I don't really need a script to do that. However, in order to learn something useful I try to make a script.
So my code is:
#!/bin/bash
! timeout 0.5 ping -c1 8.8.8.8 > /dev/null 2>&1
if [ $? -eq 1 ]
then
echo "Host is up"
else
wakeonlan FF:FF:FF:FF:FF:FF
echo "Host is starting ..."
while ! ping -c1 -w1 -n 8.8.8.8 > /dev/null 2&>1
do
printf "%c" "."
done
echo "Host is up!\n\n"
fi
exit 0
Now when the host is already up the while loop exits without printing. But when the host is down it outputs infinetely dots on the display, even if the host is up.
I really don't understand why the while loop does not stop when the condition is met.
I would appreciate your answer, especially if its aligned with my implementation.
Ok, I think I've figured out what's going on here. In the command
while ! ping -c1 -w1 -n 8.8.8.8 > /dev/null 2&>1
the & and > are in the wrong order. This causes a chain reaction of confusion and unexpected interpretations.
Specifically, bash parses 2&>1 as the argument "2" followed by the redirect &>1 which sents both stdout and stderr into a file named "1". So it runs this command, with all output to "1":
ping -c1 -w1 -n 8.8.8.8 2
Some versions of ping will give an error/usage message here, because they only accept a single target IP address (and then exit with an error status, making your loop run forever).
Other versions will interpret 8.8.8.8 as a hop to use on the way to the final destination 2, which is an old shorthand for the IP address 0.0.0.2. Which doesn't actually exist on the Internet. Which means the ping will fail and exit with an error status, making your loop run forever.
I suspect if you look in the file "1", you'll be able to tell which of these (or possibly something else) is happening.
There are some scripting lessons to be learned here:
All those cryptic piles of symbols matter, and if you get them a little bit wrong you can wind up doing something very different from what you intended.
Discarding output (especially error output) is a good way to hide what's going wrong. If a script is having trouble, capture & examine any errors its components produce.
set -x tells the shell to print what it thinks it's executing as it runs commands, and putting that before problem sections in scripts (and set +x afterward to turn it off) is a good say to find out what's going on. This is how I figured out that bash was treating "2" as an argument rather than part of the redirect.
shellcheck.net is handy!

How to use ping -f until file = false

I would like to execute the ping -f command until cat ~/.test = false, but
until [[ `cat ~/.test` == false ]]; do sudo ping -f 10.0.1.1; done
only checks one time. How to kill command automatically when the file changes?
This approach will not work for two reasons:
The ping command runs until it is interrupted. In other words: There will only be one loop iteration ever, because you will be stuck in the loop.
cat ~/.test will always be "true" (i.e. successful), as long as the file exists. It will only be "false" (i.e. exit with a non-zero error code), if the file does not exist (any more). cat is not suited for checking file changes - unless that change is creating or deleting the file.
With that in mind, you should probably try something along these lines:
#!/bin/bash
# launch the ping process and leave it running in the background
ping -f 10.0.1.1 &
# get the process ID of the previous command's process
PING_PID=$!
# until the file ~/.test does not exist any more,
# do the stuff in the loop ...
until ! test -f ~/.test; do
# sleep for one second
sleep 1
done
# kill the ping process with the previously stored process ID
kill $PING_PID
The script is untested and may not work completely, but it should give you an idea how to solve your problem.
Edit:
If it does not need to be a flood ping, you can use this simpler script:
#!/bin/bash
# As long as the file ~/.test exists,
# send one ping only to the target.
while test -f ~/.test; do
ping -c 1 10.0.1.1
done
This approach was suggested by twalberg.
Another advantage of this approach (besides the simpler script) is that you do not need to sudo the ping command any more, because unlike flood pings the "normal" pings do not need root privileges.

Netcat pipe , redirect out put of cmd to other telented machine

I have got a windows machine and a netcat, basically what i did is, I got into the command line and ran : nc -vv -l -p 1234 | cmd so every connection that comes in every thing that the user writes it pipes it into cmd, thing is the other telneted machine cannot see the output of cmd for some reason, does anybody know an answer to that? , maybe i need to make the cmd output as a stdin. idk
Your question isn't quite clear to me.
Do you want to send the output of a command to a new instance?
ping localhost | cmd
(Note: this will give you syntax errors - just to show the prinicple)
Or do you want to send the command itself to a new instance?
echo ping localhost | cmd

If telnet output contains something execute action

I am trying to write a small script in bash. My target is that a Phone Call on my FritzBox will mute or pause my TV.
I get the information of a call via telnet (telnet fritz.box 1012) on my RaspberryPi and if there is coming a phone call in I get this output:
24.08.15 14:03:05;RING;0;017mobilephonenumber;49304myhomenumber;SIP0;
The only Part that is every time the same is the RING before the calling number. What I need is a script that checks the output of the telnet and if in the telnet output is a ring than execute an action in my case i just need to do a http request on an internal site or start another script.
This is what I have tried is this:
#!/bin/bash
#string='echo "My string"'
string=$(telnet –e p fritz.box 1012)
for reqsubstr in 'alt' 'RING';do
if [ -z "${string##*$reqsubstr*}" ] ;then
echo "String '$string' contain substring: '$reqsubstr'."
else
echo "String '$string' don't contain substring: '$reqsubstr'."
fi
done
But I don`t get the output of my telnet session into the string. Anyone who can help me?
After you latest comment, and reading your question again, I think we can go for something simpler based on nc (netcat).
Let assume we create a bash script called action.sh
#!/bin/bash
while true;
do
read logline
for substr in 'alt' '\;RING\;'
do
if [[ "$logline" = *${substr}* ]]; then
echo "Got a match"
fi
done
done
Make the script executable, chmod +x action.sh, then start it with nc as follow:
nc -l fritz.box 1012 | ./action.sh
It will listen infinitely for incoming traffic from the box and should act as you want. I don't have your box obviously and I tested by starting it as nc -l 127.0.0.1 12000 and then provided input via telnet 127.0.0.1 12000, providing your RING sample. It seems to work.
$ nc -l 127.0.0.1 12000 | ./action.sh
Got a match
017mobilephonenumber
Got a match
017mobilephonenumber
Would this be more acceptable to you ? without expect (I am not an expect wizard anyway).
I hope this answer will help you more.
do you have access to expect? It is meant exactly for such kind of scenario. man expect is your friend, and I found a fair tutorial via google. If you google for modem expect you can find sample script for such scenario (I can find some for FritzBox too but not sure it's the same box as yours). I haven't used it in ages, though.
Here my short conclusion:
To see incomming calls on your FritzBox you can simply execute:
telnet fritz.box 1012
in your terminal.
The output should look like that:
24.08.15 14:03:05;RING;0;017mobilephonenumber;49304myhomenumber;SIP0;
I my case I want the execute an command the mutes my TV.
Here are the instructions how I controll my TV via Web/RaspberryPi (Philips Harmony Hub) if some one is interested.
To execute a command on an incomming call create a script called action.sh
#!/bin/bash
while true;
do
read logline
for substr in 'alt' '\;RING\;'
do
if [[ "$logline" = *${substr}* ]]; then
echo "Got a match"
#here you can put the command you want to execute on an incoming call
fi
done
done
After you create it make it executable using chmod +x action.sh
After that you can start the script using nc 192.168.1.1 1012 | action.sh
And that's how you execute a script on an incomming call!
Special thanks to tgo!!

Resources