Discovering public IP programatically in bash on Linux - bash

I found a post that does what I need, but on Windows:
Discovering public IP programmatically
> tracert -d www.yahoo.com
`Tracing route to www-real.wa1.b.yahoo.com [69.147.76.15]
over a maximum of 30 hops:`
1 <1 ms <1 ms <1 ms 192.168.14.203
2 * * * Request timed out.
3 8 ms 8 ms 9 ms 68.85.228.121
4 8 ms 8 ms 9 ms 68.86.165.234
5 10 ms 9 ms 9 ms 68.86.165.237
6 11 ms 10 ms 10 ms 68.86.165.242
The 68.85.228.121 is a Comcast (my provider) router. We can ping that:
ping -r 9 68.85.228.121 -n 1
Pinging 68.85.228.121 with 32 bytes of data:
Reply from 68.85.228.121: bytes=32 time=10ms TTL=253
Route: 66.176.38.51 ->
68.85.228.121 ->
68.85.228.121 ->
192.168.14.203
Voila! The 66.176.38.51 is my public IP.
This (third) answer shows a way to get my ISP's IP and then to use ping to get my IP.
It doesn't work unmodified on Linux. Traceroute works instead of tracert, but because its output is unpredictable, I'm not sure how to parse it.
I got as far as
IP="$(traceroute -d www.yahoo.com | grep ' 2 ' | sed -e 's/.*(\(.*\)).*/\1/')"
but the grep is (poorly) hard coded. I didn't see how to get ping to work as in the example.
Any input would be appreciated.

Personally, I'd run this command:
wget -qO- whatismyip.org

I just combined what #pizza said with this answer and produced the following working script. It's not as nice as using something like traceroute, but it's a lot less complicated.
#!/bin/bash
content="$(wget http://checkip.dyndns.org/ -q -O -)"
myip="$(<<< "$content" sed -e 's/.*Current IP Address: //' -e 's/<.*//')"
echo "myip = [${myip}]"
The wget command retrieves the result of asking dyndns for my IP. Then, sed chops off everything before and after the IP address dyndns returns.
As noted elsewhere, websites such as dyndns may block such requests if they are made too frequently, but since your IP should remain constant in most situations for at least the duration of your session, if not for many days, it shouldn't be necessary to run this script very often.

This is not a reliable solution, a passive way to do this is to write a script to pull your own router's "WAN" status page. You can do that as many times as you want and nobody will complain about excessive probing.

ok, i know this is an old thread, but this posting revealed some things to me, and combining it with what I've already learned, I think I've come up with a solid solution.
The grep you want is:
grep -m 1 -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
My personal solution to find my external IP used to be:
curl icanhazip.com
Now it is:
ISP=`traceroute -M 2 -m 2 8.8.8.8 | grep -m 1 -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'` | ping -R -c 1 -t 1 -s 1 $ISP | grep RR | grep -m 1 -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' > .extIP
and then
cat .extIP
Have fun!

IF what you want is simplicity, AND you don't mind relying on other servers (or services) try:
dig +short myip.opendns.com #resolver1.opendns.com
That will spit out your address only
or,
#!/bin/bash
myip="$(dig +short myip.opendns.com #resolver1.opendns.com)"
echo "myip = [${myip}]"

Related

How to add something after a line grep found?

I would like to see whenever the ping to a specific server goes down.
So at the moment I have something like this: ping 8.8.8.8 | grep time=11 && date
This pings google and prints every line that has a ping of 11.x that is just for testing so that I have some output that is not there all the time but often enough that I can test this. And then I have && date that I thought would print the date when grep finds the string.. But it does not. So my question now Is how can I get it so that in every line that grep spits out I can append the current timestamp. Or what would be even better is to have it as a prefix in front of every line that grep spits out. Is this possible with one command? Or do I need a simple shell script?
Thanks in advance. I hope I get an answer soon. :)
Also merry x-mas to everyone!
See if it works for you:
ping 8.8.8.8 | while read line; do echo "$(date): $line"; done | grep time=13
You can also turn the setup around to check when a remote host becomes available by using while ! ping ... Example, in case your path to 8.8.8.8 goes down:
declare -i cnt=0; while ! ping -c 1 8.8.8.8; do ((cnt++)); echo "test '$cnt' - network down on $(date)"; sleep 60; done
Which will check once per-minute if the host 8.8.8.8 is available and report, for example:
test '10' - network down on Sat Dec 26 16:38:48 CST 2015
The script will terminate on its own once the network path is established.

Can't capture the ouput of the paste bash command

( ping -n apple.com | ack -o --flush '((?<=icmp_seq=)[0-9]+|(?<=time=)[0-9]+[.][0-9]+)' | paste - - ) > ~/Desktop/ping_ouput.txt
Doesn't seem to be working for some reason, whereas if I take away the
| paste - -
section, it works just fine. I need to join every other line together with a tab instead of newline. Any ideas are appreciated.
Update:
By default, ping produces output indefinitely, until terminated, so your pipe will keep producing output and growing the output file indefinitely, and your command will never finish by itself.
Thus, you need to limit the number of pings performed; e.g.:
using the -c count option to stop after the specified number of pings.
alternatively, on BSD-like systems, you can use -o to stop after the first successful ping.
alternatively, you can stop after a specified timeout in seconds, regardless of how many packets were received:
Linux: -w timeout
BSD-like systems: -t timeout
Incidentally, in order to redirect a pipeline to a file, there's no need to enclose it in (...) as a whole, which needlessly creates a(nother) subshell.
The following was written before (a) the specific symptom was known and (b) before the OP revealed in a comment that their platform is OSX. The commands below show alternatives to the OP's extraction command; -c 2 has been added to limit ping to 2 attempts.
tivn's answer has found at least potential problem in your command: the assumption that time values always have a decimal point, which does not hold on Linux platforms; on BSD/OSX platforms, however, there are always 3 decimal places.
You can bypass this issue as well as the need to merge consecutive lines as follows (I'm using 127.0.0.1 instead of apple.com for easier testing):
Using either GNU sed (Linux) or BSD sed (BSD-like systems, including OSX):
ping -c 2 -n 127.0.0.1 |
sed -E '1d; s/^.* icmp_seq=([^ ]+).* time=([^ ]+).*$/\1'$'\t''\2/'
Alternative solutions, using awk:
ping -c 2 -n 127.0.0.1 | awk -F'[ =]' -v OFS='\t' 'NR>1 { print $6, $10 }'
If you'd rather not rely on field indices, here's an alternative (still relies on field icmp_seq to come before time, and for both to be preceded by a space):
ping -c 2 -n 127.0.0.1 | awk -F' (icmp_seq|time)=' -v OFS='\t' '
NR>1 { sub(" .+$", "", $2); sub(" .+$", "", $3)
print $2, $3 }'
You don't provide sample input you got from the ping, so it is not clear what your problem is. But I guess your issue is because the time part from ping result may not always contains dot ., for example: 64 bytes from x.x.x.x: icmp_seq=1 ttl=63 time=137 ms . You may want to try with the following command :
( ping -n yahoo.com \
| ack -o --flush '((?<=icmp_seq=)\d+|(?<=time=)[\d.]+)' \
| stdbuf -o0 paste - - \
) > ~/Desktop/ping_ouput.txt
Edit: after comment from OP, the issue may actually come from buffered paste command. Please try with adding stdbuf -o0 to the paste command.

Get Macbook screen size from terminal/bash

Does anyone know of any possible way to determine or glean this information from the terminal (in order to use in a bash shell script)?
On my Macbook Air, via the GUI I can go to "About this mac" > "Displays" and it tells me:
Built-in Display, 13-inch (1440 x 900)
I can get the screen resolution from the system_profiler command, but not the "13-inch" bit.
I've also tried with ioreg without success. Calculating the screen size from the resolution is not accurate, as this can be changed by the user.
Has anyone managed to achieve this?
I think you could only get the display model-name which holds a reference to the size:
ioreg -lw0 | grep "IODisplayEDID" | sed "/[^<]*</s///" | xxd -p -r | strings -6 | grep '^LSN\|^LP'
will output something like:
LP154WT1-SJE1
which depends on the display manufacturer. But as you can see the first three numbers in this model name string imply the display-size: 154 == 15.4''
EDIT
Found a neat solution but it requires an internet connection:
curl -s http://support-sp.apple.com/sp/product?cc=`system_profiler SPHardwareDataType | awk '/Serial/ {print $4}' | cut -c 9-` |
sed 's|.*<configCode>\(.*\)</configCode>.*|\1|'
hope that helps
The next script:
model=$(system_profiler SPHardwareDataType | \
/usr/bin/perl -MLWP::Simple -MXML::Simple -lane '$c=substr($F[3],8)if/Serial/}{
print XMLin(get(q{http://support-sp.apple.com/sp/product?cc=}.$c))->{configCode}')
echo "$model"
will print for example:
MacBook Pro (13-inch, Mid 2010)
Or the same without perl but more command forking:
model=$(curl -s http://support-sp.apple.com/sp/product?cc=$(system_profiler SPHardwareDataType | sed -n '/Serial/s/.*: \(........\)\(.*\)$/\2/p')|sed 's:.*<configCode>\(.*\)</configCode>.*:\1:')
echo "$model"
It is fetched online from apple site by serial number, so you need internet connection.
I've found that there seem to be several different Apple URLs for checking this info. Some of them seem to work for some serial numbers, and others for other machines.
e.g:
https://selfsolve.apple.com/wcResults.do?sn=$Serial&Continue=Continue&num=0
https://selfsolve.apple.com/RegisterProduct.do?productRegister=Y&country=USA&id=$Serial
http://support-sp.apple.com/sp/product?cc=$serial (last 4 digits)
https://selfsolve.apple.com/agreementWarrantyDynamic.do
However, the first two URLs are the ones that seem to work for me. Maybe it's because the machines I'm looking up are in the UK and not the US, or maybe it's due to their age?
Anyway, due to not having much luck with curl on the command line (The Apple sites redirect, sometimes several times to alternative URLs, and the -L option doesn't seem to help), my solution was to bosh together a (rather messy) PHP script that uses PHP cURL to check the serials against both URLs, and then does some regex trickery to report the info I need.
Once on my web server, I can now curl it from the terminal command line and it's bringing back decent results 100% of the time.
I'm a PHP novice so I won't embarrass myself by posting the script up in it's current state, but if anyone's interested I'd be happy to tidy it up and share it on here (though admittedly it's a rather long winded solution to what should be a very simple query).
This info really should be simply made available in system_profiler. As it's available through System Information.app, I can't see a reason why not.
Hi there for my bash script , under GNU/Linux : I make the follow to save
# Resolution Fix
echo `xrandr --current | grep current | awk '{print $8}'` >> /tmp/width
echo `xrandr --current | grep current | awk '{print $10}'` >> /tmp/height
cat /tmp/height | sed -i 's/,//g' /tmp/height
WIDTH=$(cat /tmp/width)
HEIGHT=$(cat /tmp/height)
rm /tmp/width /tmp/height
echo "$WIDTH"'x'"$HEIGHT" >> /tmp/Resolution
Resolution=$(cat /tmp/Resolution)
rm /tmp/Resolution
# Resolution Fix
and the follow in the same script for restore after exit from some app / game
in some S.O
This its execute command directly
ResolutionRestore=$(xrandr -s $Resolution)
But if dont execute call the variable with this to execute the varible content
$($ResolutionRestore)
And the another way you can try its with the follow for example
RESOLUTION=$(xdpyinfo | grep -i dimensions: | sed 's/[^0-9]*pixels.*(.*).*//' | sed 's/[^0-9x]*//')
VRES=$(echo $RESOLUTION | sed 's/.*x//')
HRES=$(echo $RESOLUTION | sed 's/x.*//')

Another parsing bash script output to variables

I know this has been asked before, and I have looked at the previous posts, but can't seem to get what I am trying to do working.
I know it's really basic, so I'm hoping this will be easy for most people on SO.
I want to create a simple bash script that pings an address, and then inserts the ping output into a csv file (or rather, into a file, separated with commas).
The ping command would be
ping -D google.com
and the output looks like
PING google.com (74.125.239.103) 56(84) bytes of data.
[1393992465.052723] 64 bytes from nuq05s01-in-f7.1e100.net (74.125.239.103): icmp_req=1 ttl=55 time=2.66 ms
I want to insert epoch time, ttl, and time into a file, separated by commas.
If it's easy enough, then it wouldn't hurt to convert to the epoch time to a date also, but this can be my next step in this simple project.
I figured out the command to convert epoch time to human format is
date -d #[epoch_time]
Side trivia - why do I need the #?
ping -D google.com | sed -n -e 2p -e 2q |
while read epoch b64 bytes from host ip icmp ttl time ms
do
date=$(date -d #$(sed 's/[][]//g' <<< "$epoch"))
ttl=$(sed 's/ttl=//' <<< "$ttl")
time=$(sed 's/time=//' <<< "$time")
echo "$date,$ttl,$time"
done >> file.csv
Clearly, you can extend the number of lines you analyze by adjusting the first sed command. You can probably do the editing within the loop with ${var/x/y} notation, but I don't think of doing that until I'm writing up at the end because I've spent longer working with shells where that wasn't an option than I have with bash. Are you sure you don't want the host or IP address information?
In the cold light of morning, there's an easy way to improve the code:
ping -D google.com | sed -n -e 2p -e 2q |
while read epoch b64 bytes from host ip icmp ttl time ms
do
epoch=${epoch#[}
epoch=${epoch%]}
date=$(date -d #${epoch})
echo "$date,${ttl#ttl=},${time#time=}"
done >> file.csv
It's a matter of choice whether you create the date variable or simply embed the $(date -d #${epoch}) directly in the echo argument.

Mac ping but only display avg ms

What I want is to ping a server but only want the avg time to be returned, I think grep or something like that would help but a google search doesn't return much useful.
This is to go into a geektool script that will display a servers ping for me to see if its up.
Needs to be a script / command that will work in terminal as I need to pull it over to python as well.
Thanks
Somehow on my system ping command doesn't output round-trip but rtt.
So this one is going to work better:
ping -q -c 5 google.com | tail -n 1 | cut -f 5 -d '/'
-q makes it less verbose because we don't need much of the output anyway. tail simply returns the last line of the output
How about something like:
ping -c 5 google.com | grep "round-trip" | cut -f 5 -d "/"

Resources