115

How can I get my own IP address and save it to a variable in a shell script?

Mat
  • 52,586
The Student
  • 3,529
  • 7
    A word of caution: There are a lot of "works on my system" answers here that may not be portable to other environments. You have to decide if you want something that works for you or something that can be shared. Every system has multiple IPs. A portable solution answers the Q: "what IP do I use to reach blah?" A works on my system solution answers the Q: "what's my IP?" with the A: "I think you mean this one…" This should be portable https://unix.stackexchange.com/a/402160/9745 – Bruno Bronosky Apr 01 '18 at 17:42
  • This provides a way to obtain IP address form /proc: https://unix.stackexchange.com/questions/365225/how-to-get-the-ipv4-address-for-an-interface-from-proc – Pierz Sep 19 '19 at 16:12
  • export IP=$(curl ifconfig.io) – Constantin De La Roche May 29 '21 at 18:06

25 Answers25

103

I believe the "modern tools" way to get your ipv4 address is to parse ip rather than ifconfig, so it'd be something like:

ip4=$(/sbin/ip -o -4 addr list eth0 | awk '{print $4}' | cut -d/ -f1)
ip6=$(/sbin/ip -o -6 addr list eth0 | awk '{print $4}' | cut -d/ -f1)

or something like that.

jsbillings
  • 24,406
  • How common is /sbin/ip? I don't have it on any of my computers – Michael Mrozek Mar 03 '11 at 15:03
  • 5
    ip is available on all the Red Hat and Fedora distros I've used. ip is part of the iproute2 package (http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2). ifconfig and route are supposedly deprecated, although they continue to be used by a lot of people, particularly in scripts. ip is much more parsable, in my opionion. – jsbillings Mar 03 '11 at 15:13
  • 3
    ip is also available on all Debian and Debian based distros I've seen. It's part of the iproute package which is marked as important. – Arrowmaster Mar 03 '11 at 23:43
  • I'm using an embeded system, and it don't have this command. But it's a nice way for the systems have the command. – The Student Mar 04 '11 at 12:57
  • 3
    This is absolutely how it should be done, I would downvote the other answers for their use of ifconfig. ip is part of the iproute2 package which is many a distro by default. It's in most good repos to. http://en.wikipedia.org/wiki/Iproute2 – Baldrick Sep 15 '12 at 12:13
  • 3
    @jsbillings Why /sbin/ip instead of ip? – l0b0 Jan 15 '13 at 13:02
  • 7
    This is an excellent answer! The combination of awk and cut is mildly amusing to me, here's a possible alternative which may not actually be better: ip -o -4 addr show eth0 | awk '{ split($4, ip_addr, "/"); print ip_addr[1] }' – Swiss Jun 15 '17 at 21:21
  • Thanks guys, this command worked for me: ip -o -4 addr show eth0 | tr -s ' ' | cut -d' ' -f4 | cut -d'/' -f1 – pacoverflow Sep 22 '21 at 08:05
  • 1
    Inspired by the answer from @Swiss that uses awk, this is how it works when sed is used: ip -o -4 addr show enth | sed -n 's/.* inet \(.*\)\/.*/\1/p'. It uses a more strict matching pattern, so it might catch errors better and output nothing. – Axel Heider Aug 11 '22 at 13:00
  • A safer, less hacky way is to use JSON and parse it: ip -o -4 -br -j addr show enp0s25 | jq -r .[0].addr_info[0].local (replace enp025 with the interface you need, such as wlo1 or eth0). If you don't know which interface, see Tom's answer. – geekley Mar 07 '24 at 23:23
56

Why not simply do IP=$(hostname -I) ?

Andrei
  • 1,753
  • 2
  • 15
  • 18
  • 7
    hostname -i gives me just 127.0.0.1, hostname -I gives me the correct IP-Adress... – student Sep 15 '12 at 12:41
  • @student yes indeed, I have tested it on a machine that has can resolve its hostname, as the manpage says -i Display the network address(es) of the host name. Note that this works only if the host name can be resolved. Avoid using this option; use hostname --all-ip-addresses – Andrei Sep 15 '12 at 12:49
  • How can I configure my machine to resolve its hostname? – student Sep 15 '12 at 12:58
  • the easiest way is adding it to /etc/hosts, along with the corresponding IP address – Andrei Sep 16 '12 at 10:06
  • 4
    No -I on FreeBSD, but you can use dig +short \hostname -f`` – Tigger Jun 03 '15 at 00:07
  • hostname: invalid option -- 'I'... – jasonwryan Dec 23 '15 at 06:59
  • 5
    hostname -I gives me two IPs, delimited by space. Using CentOS 7.3.1611 – amphibient May 16 '17 at 16:37
  • 3
    @amphibient: Yeah, that's standard. Per its documentation, it returns all of the host's configured IP addresses (except loopback and link-local addresses). The people seeing only one IP address must be running it on a host that only has one non-loopback interface. – ruakh Jun 03 '18 at 17:09
  • 2
    Hostname -I still allows for ambiguity. I have a lot of docker networks and hence I see: 192.168.0.33 192.168.1.106 192.168.16.1 172.23.0.1 172.17.0.1 172.24.0.1 172.26.0.1 172.18.0.1 172.27.0.1 2a02:8108:41bf:c4b0::3c00 2a02:8108:41bf:c4b0:94ab:8d54:b734:1cb0 2a02:8108:41bf:c4b0:d1bb:4aa7:7cce:b458 Only the first IP in this list is the one that I want, yet can I trust the order to always be correct? – k0pernikus Jul 30 '20 at 12:21
  • This command could yield multiple IP addresses, if multiple interfaces are present. – Jay Sep 08 '20 at 13:47
  • @k0pernikus Correct, as the man page says: Do not make any assumptions about the order of the output. – pacoverflow Sep 22 '21 at 07:57
  • Easiest way to remove trailing spaces is IP=$(hostname -I | xargs) - usefulon CentOS or Ubuntu. – Jarek May 18 '23 at 15:03
38

It's not so easy if you want to take into account wlan and other alternative interfaces. If you know which interface you want the address for (e.g., eth0, the first Ethernet card), you can use this:

ip="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"

In other words, get me the network configuration information, look for eth0, get that line and the next one (-A 1), get only the last line, get the second part of that line when splitting with :, then get the first part of that when splitting with space.

l0b0
  • 51,350
  • 2
    I added an additional grep to your code to ignore any interface with "eth0:" in it; this now works as I expect (giving only the "eth0" IP address and not any sub-interfaces (eth0:0, eth0:1, etc.): ip="$(ifconfig | grep -v 'eth0:' | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)" –  Feb 12 '13 at 19:49
  • 7
    You can skip the first grep by using ifconfig eth0 – Bryan Larsen Dec 01 '14 at 16:29
  • 12
    ifconfig is deprecated, you should use ip address instead – alexises Dec 23 '15 at 15:42
  • Maybe is deprecated but still more clear than ip – QkiZ Feb 26 '21 at 12:39
  • 1
    @QkiZ It's not just deprecated, it doesn't even exist on Debian. – pacoverflow Sep 22 '21 at 07:49
  • apt-get install net-tools and you have ifconfig on Debian. – QkiZ Sep 22 '21 at 11:53
  • Using ip is so much easier than ifconfig. – Seamus Mar 24 '22 at 08:20
  • See the answers using jq to parse JSON output from ip -j. They are much clearer, e.g. ip -j route get 8.8.8.8 | jq -r '.[].prefsrc' Also look at ifdata from the moreutils package. – Ian D. Allen Feb 02 '23 at 03:30
35

If you want the address of an interface, the easiest way is to install moreutils then:

anthony@Zia:~$ ifdata -pa br0
172.16.1.244

ifdata answers pretty much every question you'd be tempted to parse ifconfig output for.

If you want to find out your IP address as the outside sees it (beyond any NAT, etc.), there are plenty of services that'll do it. One is fairly easy:

anthony@Zia:~$ curl ifconfig.me
173.167.51.137
derobert
  • 109,670
27

I don't mean to be a jerk, but there really is a right way and this is it. You trim the output of ip route to get only the source IP. Depending on what IP you are trying to reach, "my own ip address" (OP's words) will be different. If you care about reaching the public internet, using Google's 8.8.8.8 DNS server is pretty standard. So...

The short answer is:

ip route get 8.8.8.8 | sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}'

Here's the detailed explanation

If I want the ip I use to reach the internet, I use this:

pi@et3:~ $ ip route get 8.8.8.8 | sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}'
10.55.0.200

If I want the ip I use to reach something on my VPN, I use this:

pi@et3:~ $ ip route get 172.31.0.100 | sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}'
172.29.0.9

This next one is really just for illustrative purposes. But, it should work on any Linux system. So, you can use this to demonstrate that, yes, all machines have multiple IP addresses at all times.

If I wanted the ip I use to reach myself, I would use this:

pi@et3:~ $ my_ip=$(getent hosts $(cat /etc/hostname) | awk '{print $1; exit}')
pi@et3:~ $ ip route get $my_ip | sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}'
127.0.0.1

More about that sed command

First let me say that when choosing unix tools, you try to choose the tools that require the fewest pipes. So, while some answers will pipe ifconfig to grep to sed to head, that is rarely ever necessary. When you see it, it should raise a red flag that you are taking advice from someone with little experience. That doesn't make the "solution" wrong. But, it probably could use some streamlining.

I have chosen sed because it is more terse than the same workflow in awk. (I have since an awk example below.) I don't think any other tool but those 2 would be appropriate.

Let's examine what sed -n '/src/{s/.*src *\([^ ]*\).*/\1/p;q}' does:

sed            # the sed executable located via $PATH
-n             # no output unless explicitly requested
'              # begin the command space
/src/          # regex match the string 'src'
{              # begin a block of commands **
s/             # begin a substitution (match)
  .*src *      # match anything leading up to and including src and any number of spaces
  \([^ ]*\)    # define a group containing any number of non spaces
  .*           # match any trailing characters (which will begin with a space because of the previous rule).
/              # begin the substitution replacement
  \1           # reference the content in the first defined group
/              # end the substitution
p              # print (explicitly, remember) the result
;              # designate the end of the command
q              # quit
}              # end the block of commands
'              # end the command space

** all of which will be performed "on match"
  - otherwise only the first command to following the match would be performed "on match"
    - any other commands would be performed whether there was a match or not

Note:

I used to use sed -n '/src/{s/.*src *//p;q}' but a commenter pointed out that some systems have trailing data after the src field.

Using awk

ip route get 8.8.8.8 | \
    awk '{gsub(".*src",""); print $1; exit}'

# or

ip route get 8.8.8.8 | \
    awk '{for(i=1; i<NF; i++){if($i=="src"){i++; print $i; exit}}}'

More about my network

My ifconfig shows that I have tun0 for my VPN and eth0 for my lan.

pi@et3:~ $ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.55.0.200  netmask 255.255.252.0  broadcast 10.55.3.255
        inet6 fe80::71e6:5d7c:5b4b:fb25  prefixlen 64  scopeid 0x20<link>
        ether b8:27:eb:b2:96:84  txqueuelen 1000  (Ethernet)

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1  (Local Loopback)

tun0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST>  mtu 1500
        inet 172.29.0.9  netmask 255.255.255.255  destination 172.29.0.10
        inet6 fe80::3a8e:8195:b86c:c68c  prefixlen 64  scopeid 0x20<link>
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 100  (UNSPEC)

wlan0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether b8:27:eb:e7:c3:d1  txqueuelen 1000  (Ethernet)
  • ip route get 8.8.8.8 | sed -n '/src/{s/.*src *//p;q}' gave me 10.1.2.3 uid 1002. So I add to append | awk '{print $1}' to keep only the IP address. – wisbucky Apr 23 '19 at 21:40
  • 2
    @wisbucky I never pipe sed/awk/grep to each other in recorded code. (I will use it on the command line when I'm in a hurry, but you will never find it written to disk other than in my ~/.bash_history) I updated the answer to use awk and it solves this issue. – Bruno Bronosky Apr 24 '19 at 15:30
  • 4
    @wisbucky I couldn't stand it. I made my sed work with your environment too. – Bruno Bronosky Apr 24 '19 at 16:05
  • Quite good. There are some systems that are isolated and cannot reach 8.8.8.8, also "cat /etc/hostname" may return without domain, getenv hosts need domain - using hostaname -f should resolve. Usually the "right way" is a cascate to attemps to sotisfy each system. – LittleEaster Oct 17 '20 at 16:46
  • ip route get 8.8.8.8. Won't work in certain scenarios where there is no default route, like VirtualBox host-only networks. – waitinforatrain May 12 '21 at 15:29
  • 1
    Note (as suggested elsewhere) jq -j route get 8.8.8.8 | jq .[].prefsrc involves just as few pipes but is much less vulnerable to subtle variations in output format and much easier to understand than the sed recipe. The only wrinkle is that busybox's version of ip doesn't support JSON output. – Tom Sep 14 '21 at 11:52
  • 1
    @Tom: nice approach! NB: please note you have a typo: the first jq -j should of course be ip -j. – Hugo Ideler Nov 27 '21 at 22:09
  • @Tom in addition to the typo, you forgot to quote the GLOB characters (my shell runs with failglob set), and by adding a -r to jq you can get rid of the unnecessary quotes around the output address: ip -j route get 8.8.8.8 | jq -r '.[].prefsrc' – Ian D. Allen Feb 02 '23 at 03:06
  • Yep, thanks for the corrections all, was doing this from memory. – Tom Feb 28 '23 at 10:21
23

To get IPv4 and IPv6 addresses, and not assume the main interface is eth0 (these days em1 is more common), try:

ips=$(ip -o addr show up primary scope global |
      while read -r num dev fam addr rest; do echo ${addr%/*}; done)
  • -o uses the one-line output format, which is easier to process with read, grep, etc.
  • up excludes devices that aren't active
  • scope global excludes private/local addresses such as 127.0.0.1 and fe80::/64
  • primary excludes temporary addresses (assuming you want an address that doesn't change)
Mikel
  • 57,299
  • 15
  • 134
  • 153
  • 2
    This has to be the best answer because 1) it doesn't assumes a device name, 2) it uses 'ip' as opposed to old school 'ifconfig', and 3) it supports ipv6 as well. – BringBackCommodore64 Jul 06 '17 at 11:31
  • 1
    I agreed, it the best start for most use cases. You can pass -4/-6 to filter address types. You can also easily get other interface parameters. Personally, I dislike while loop and I prefer grep -o 'inet6\? [0-9a-f\.:]*' | cut -f 2 -d ' ' – Jérôme Pouiller May 18 '18 at 07:42
14

Depends what you mean by own IP address. Systems have IP addresses on several subnets (sometimes several per subnet), some of which IPv4, some IPv6 using devices like ethernet adapters, loopback interfaces, VPN tunnels, bridges, virtual interfaces...

I you mean the IP address by which another given device may reach your computer, you have to find out which subnet that is, and which version of IP we're talking about. Also, bear in mind that because of NAT performed by firewall/routers, the IP address of an interface may not be the same as a remote host sees an incoming connection from your computer coming from.

When there is fancy source routing or per protocol/port routing it can be difficult to find out which interface would be used to talk to one remote computer over a given protocol and even then, there's no guarantee that the IP address of that interface may be directly addressable by the remote computer wanting to establish a new connection to your computer.

For IPv4 (probably works for IPv6 as well), a trick that works in many unices including Linux to find out the IP address of the interface used to reach a given host is to use a connect(2) on a UDP socket and use getsockname():

For instance, on my home computer:

perl -MSocket -le '
  socket(S, PF_INET, SOCK_DGRAM, getprotobyname("udp"));
  connect(S, sockaddr_in(1, inet_aton("8.8.8.8")));
  print inet_ntoa((sockaddr_in(getsockname(S)))[1]);'

Would be used to find out the IP address of the interface via which I would reach 8.8.8.8 (google's DNS server). It would return something like "192.168.1.123" which is the address of the interface for the default route to the internet. However, google wouldn't see a DNS request from my machine as coming from that IP address which is a private one, as there's NAT performed by my home broadband router.

connect() on a UDP socket doesn't send any packet (UDP is connection-less), but prepares the socket by querying the routing table.

On Linux and with recent versions of iproute2 and a json parsing tool like jq, you should be able to get something similar with:

ip    -j route get 8.8.8.8              | jq -r '.[].prefsrc' # IPv4
ip -6 -j route get 2001:4860:4860::8888 | jq -r '.[].prefsrc' # IPv6

Beware the openvpn when configured to force traffic through the tunnel adds two routes for 0.0.0.0/1 and 128.0.0.0/1 instead of one default (0.0.0.0/0) route, so the approaches that query the default route don't work in that case.

7
ipa=$(ip route get 8.8.8.8| grep src| sed 's/.*src \(.*\)$/\1/g')

ipa=$(hostname -i|cut -f2 -d ' ')
  • 1
    Using -I instead of -i is better as you would want to ignore loopback adresses, so the final command would be ipa=$(hostname -I|cut -f1 -d ' ') – Karl.S Oct 17 '17 at 22:00
  • 2
    If grep isn't enough, don't pipe it to something else. Just use the something else (sed, awk, etc.). – Bruno Bronosky Aug 11 '18 at 16:58
6

Since I don't think anyone has mentioned this option here:

$ ip -f inet -json addr list | jq -r '.[] | select(length>0) | .addr_info[].local'

Explanation:

ip can produce JSON output which you can then query with jq. .[] means "each member of the array that was returned". select(length>0) does pretty much what it says but is only really useful if you specify an interface name to ip, in which case it returns empty objects for each interface that didn't match. .addr_info[].local means print the local IP address of each addr_info member of each object that survived the select.

It's worth having a look at the output of ip -json addr list | jq . to see what other information you can query for in this way.

Edit This also returns the loopback address. If you don't want that:

$ ip -f inet -json addr list | jq -r '.[] | select(length>0) | select(.flags | index("LOOPBACK") | not) | .addr_info[].local'

However, note that many systems will still return many IP addresses and it's not obvious how to tell which is the "right" one. My system, for instance, returns about a dozen addresses, mostly created by Docker.

If by "my own IP address" you mean the IPv4 address on a local interface through which you access the internet, try this:

$ my_ip=$(ip -j route get 8.8.8.8 | jq -r '.[0].prefsrc')

ip -j route get 8.8.8.8 is asking the kernel, "Suppose I wanted to get a packet to the host at 8.8.8.8; how would you get it there?" 8.8.8.8 is Google's DNS server so it seems fairly likely you're going to get to it via your internet connection.

jq -r '.[0].prefsrc' is the source address that would be attached to the packet.

If you don't have an internet connection, my_ip will be empty after this. You can also check for this explicitly:

route_info=$(ip -j route get 8.8.8.8)
if [[ ! $? ]]; then
  echo "You have no internet connection."
  exit
fi
my_ip = $(echo ${route_info} | jq -r '.[0].prefsrc')
Tom
  • 524
  • 2
  • 5
  • 15
  • this gave me also 127.0.0.1. Worked answer https://unix.stackexchange.com/a/643507/37431 – rofrol Oct 29 '21 at 19:22
  • It's not immediately obvious why the answer you linked doesn't also return the loopback address. I'll update this answer with a better way. – Tom Nov 02 '21 at 14:55
2

Assuming that you may have various interfaces of various name but that you want the first non-localhost one and non-ipv6, you may try:

ip=`ip addr show |grep "inet " |grep -v 127.0.0. |head -1|cut -d" " -f6|cut -d/ -f1`
Nicolas
  • 21
2

You should use ip (instead of ifconfig) as it's current, maintained, and perhaps most importantly for scripting purposes, it produces a consistent & parsable output. Following are a few similar approaches:

If you want the IPv4 address for your Ethernet interface eth0:

$ ip -4 -o addr show eth0 | awk '{print $4}'
192.168.1.166/24  

As a script:

$ INTFC=eth0  
$ MYIPV4=$(ip -4 -o addr show $INTFC | awk '{print $4}') 
$ echo $MYIPV4
192.168.1.166/24

The output produced above is in CIDR notation. If CIDR notation isn't wanted, it can be stripped:

$ ip -4 -o addr show eth0 | awk '{print $4}' | cut -d "/" -f 1 
192.168.1.166  

Another option that IMHO is "most elegant" gets the IPv4 address for whatever interface is used to connect to the specified remote host (8.8.8.8 in this case). Courtesy of @gatoatigrado in this answer:

$ ip route get 8.8.8.8 | awk '{ print $7; exit }'
192.168.1.166

See NOTE 1 below

As a script:

$ RHOST=8.8.8.8  
$ MYIP=$(ip route get $RHOST | awk '{ print $7; exit }')
$ echo $MYIP
192.168.1.166

See NOTE 1 below

This works perfectly well on a host with a single interface, but more advantageously will also work on hosts with multiple interfaces and/or route specifications.

ip would be my preferred approach, but it's certainly not the only way to skin this cat. Here's another approach that uses hostname if you prefer something easier/more concise:

$ hostname --all-ip-addresses | awk '{print $1}'  

Or, if you want the IPv6 address:

$ hostname --all-ip-addresses | awk '{print $2}'  

NOTE 1: The output of ip route get has changed since this answer was posted. The ip addr is no longer at $NF; it is now located at $7.

Seamus
  • 2,925
1

Assuming you need your primary public IP as it seen from the rest of the world, try any of those:

wget http://ipecho.net/plain -O - -q
curl http://icanhazip.com
curl http://ifconfig.me/ip
Putnik
  • 886
1

Simple Command to fine IP Address with default interface.

ip route | grep src | awk '{print $NF; exit}'

Tested on All Unix OS

M.S.Arun
  • 291
1

If you're looking for a public IP address of the box, you could use the following:

  • dig @ns1.google.com -t txt o-o.myaddr.l.google.com +short | tr -d \"

You could use dig(1) options like -4 or -6 to specifically look for an IPv4 or IPv6 address; Google will provide an answer in a record of TXT type, which will have quotes around it when presented by dig; if you want to subsequently use the variable with utilities like traceroute, you gotta use something like tr(1) to remove said quotes.

Other options include whoami.akamai.net and myip.opendns.com, which answer with A and AAAA records (instead of TXT as in the above example from Google), so, they don't require having the quotes removed:

  • dig -4 @ns1-1.akamaitech.net -t a whoami.akamai.net +short

  • dig -4 @resolver1.opendns.com -t any myip.opendns.com +short

  • dig -6 @resolver1.opendns.com -t any myip.opendns.com +short

Here's a sample script that uses all the options above to set the variables:

#!/bin/sh
IPANY="$(dig @ns1.google.com -t txt o-o.myaddr.l.google.com +short | tr -d \")"
GOOGv4="$(dig -4 @ns1.google.com -t txt o-o.myaddr.l.google.com +short | tr -d \")"
GOOGv6="$(dig -6 @ns1.google.com -t txt o-o.myaddr.l.google.com +short | tr -d \")"
AKAMv4="$(dig -4 @ns1-1.akamaitech.net -t a whoami.akamai.net +short)"
CSCOv4="$(dig -4 @resolver1.opendns.com -t a myip.opendns.com +short)"
CSCOv6="$(dig -6 @resolver1.opendns.com -t aaaa myip.opendns.com +short)"
printf '$GOOG:\t%s\t%s\t%s\n' "${IPANY}" "${GOOGv4}" "${GOOGv6}"
printf '$AKAM:\t%s\n$CSCO:\t%s\t%s\n' "${AKAMv4}" "${CSCOv4}" "${CSCOv6}"

If you're looking for a private IP address, or for a set of all IP addresses assigned to the box, you could use some combination of ifconfig (on BSD and GNU/Linux), ip addr (on GNU/Linux), hostname (options -i and -I on GNU/Linux) and netstat to see what's going on.

cnst
  • 3,283
1

Shortest but not foolproof way (supposing the system have 'iproute2' installed):

ip route get 1.1.1.1 | awk '{print $7;exit}'

Explanation: Here we are trying to reach a real and reachable (Cloudflare) IP address, that latest versions of ip command should return something like:

1.1.1.1 via <your router> dev <your network device> src <your ip address!> uid <uid of your user>

So, what the one-liner mentioned above tries to do is to get the nth field, if this is the actual output you get.

As it's not garanteed you are going to have the same output in every system, we are better off catching some errors with a more robust version:

IP=$(ip route get 1.1.1.1 | awk '{print $7; exit}')

if [ -a "${IP}" ]; then echo "unable to determine the IP address" else echo "Your IP address is &quot;${IP}&quot;" fi

  • 1
    Perhaps you could add an example of the raw output of the ip command, so that people can better understand why it works (and the site's algorithms don't put it in my "Low quality post" review queue ... ;) ) – AdminBee May 29 '20 at 08:13
  • 1
    gave me 1000 instead of ip. 1.0.0.0 via 192.168.0.1 dev wlo1 src 192.168.0.16 uid 1000 – rofrol Oct 29 '21 at 19:20
  • The output of ip route get has changed. Two additional fields have been added for uid, and so now $NF returns the uid instead of the IP address. The IP address is now in $7. – Seamus Mar 24 '22 at 08:07
0
myip=$(curl -kLs "http://api.ipify.org")

or

myip=$(wget -q "http://api.ipify.org" -O -)
Zibri
  • 573
  • 5
  • 9
0

all the solutions using awk/sed/grep seem overly complex and ugly for my situation... so i came up with this really simple solution BUT beware cus it makes some assumptions, namely the assumption that the LAST interface is the one you're interested. if you're ok with that then this is pretty clean:

ifconfig | awk '/net / { x = $2 } END { print x }'

otherwise you could do some silly if statement to test for a specific prefix or whatever criteria you may have.

mad.meesh
  • 121
0

This will print the IP of the connection that is currently UP:

ip -4 -j a | jq -r '.[] | select(.operstate=="UP") | .addr_info[].local'

ip -4 -j a - show IPv4 addresses in JSON format

jq -r '.[] | select(.operstate=="UP") | .addr_info[].local' - find a object that has "operstate": "UP", and retrieve a local property value (which is in the addr_info array).

Flag -r for jq means "show raw output".

marverix
  • 171
0

This answer offered to this "duplicate" question:

1. private ip addr:

The ip command is one way to get your private IP addr:

$ ip route get 8.8.8.8      # 8.8.8.8 is Google's DNS server
8.8.8.8 via 192.168.1.1 dev wlan0 src 192.168.1.144 uid 1000
$

You can probably figure what each field means, but the one that answers your question is the IP addr following src: 192.168.1.144. The advantage of this approach is that it works on systems with more than one interface (e.g. wlan0 & eth0), and it chooses the interface & IP addr actually used to reach the destination 8.8.8.8

You could script this as follows:

...
MYIP=$(ip route get 8.8.8.8 | awk '{ print $7; exit }')
echo "Your private ip is: $MYIP" 
...

An alternative is using hostname; simpler if you know which interface is used & don't care about the route info:

hostname --all-ip-addresses
192.168.1.144

public ip addr

To get your public IP addr, you will need some "outside help" - an external server that simply returns the IP address it sees when you access it. I compiled a list of these servers on GitHub some time ago that you're welcome to try - I only tested one of them just now & it worked. Some of the other sites in the list will require some parsing to get the IP addr, but this one is straightforward:

$ curl ifconfig.me && echo
AAA.BB.CCC.DDD

Or, in a script


MYXIP=$(curl ifconfig.me)
echo "My external IP addr is: $MYXIP"

Seamus
  • 2,925
0

I use this one-liner:

IP=$(/sbin/ifconfig | grep -e "inet:" -e "addr:" | grep -v "inet6" | grep -v "127.0.0.1" | head -n 1 | awk '{print $2}' | cut -c6-)

Uses ifconfig (widely available), does not take localhost address, does not bind you to a given interface name, does not take into account IPv6 and tries to get the IP of the first network interface available.

-1

I needed to do this within an alias to start a radio server on my wired NIC. I used

ip addr | egrep -i "inet.+eth1" | awk -F[\ /] '{print $6}' | tr -d [:space:]
user208145
  • 2,485
-1

Some commands are working on centos 6 or 7, the below command working on both,

#!/bin/sh

serverip=`/sbin/ifconfig eth0 | grep "inet" | awk '{print $2}' | awk 'NR==1' | cut -d':' -f2`

echo $serverip
  • 1
    that's way too much grep | awk | awk . line can be shortened to /sbin/ifconfig eth0 | awk '$1 == "inet" { print substr($2,6); next ; } ' – Archemar Aug 06 '15 at 11:23
  • I accepted, can you check both centos6 and 7?.

    centos 7.x /sbin/ifconfig eth0 | awk '$1 == "inet" { print substr($2,6); next ; }'

    35.104.41

    centos6.x (Working fine) /sbin/ifconfig eth0 | awk '$1 == "inet" { print substr($2,6); next ; }' 192.168.0.1

    – lakshmikandan Aug 07 '15 at 04:32
-1

This snippet avoids hard-coding the device name (like 'eth0') and will use ip instead of ifconfig:

/sbin/ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/'

It will return the IP of the first active device listed in the output of ip addr. Depending on your machine, this can be an ipv4 or ipv6 address.

To store it into a variable, use:

ip=$(/sbin/ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1  -d'/')
-1

This may not be the most robust or correct solution, but unlike most of the other solutions, the command works on both Linux and Mac (BSD).

host `hostname` | awk '{print $NF}'
wisbucky
  • 3,388
-4
hostname -I >> file_name 

this will do everything you want

muru
  • 72,889
shanu
  • 3