Scripts and automation
Once you know the commands, the next question is how to use them from a script. These are short, tested patterns that avoid the usual traps: localised ipconfig output, adapter names with spaces, and output formats that change between versions.
Get just the address
# IPv4 of the adapter that has the default route (the one that reaches the internet)
$ip = (Get-NetIPConfiguration | Where-Object { $_.IPv4DefaultGateway -and $_.NetAdapter.Status -eq 'Up' }).IPv4Address.IPAddress
$ip
# by adapter name
(Get-NetIPAddress -InterfaceAlias 'Ethernet' -AddressFamily IPv4).IPAddress
# MAC of the same adapter
(Get-NetAdapter -Name 'Ethernet').MacAddress
# public IP
(Invoke-RestMethod https://ipconfig.co.uk/ip).Trim()@echo off
rem First IPv4 address that is not 169.254 (works on English Windows only; use PowerShell for anything else)
for /f "tokens=2 delims=:" %%a in ('ipconfig ^| findstr /c:"IPv4 Address"') do (
set "ip=%%a"
set "ip=!ip: =!"
goto :done
)
:done
setlocal enabledelayedexpansion
echo !ip!
rem Public IP
for /f %%a in ('curl -s https://ipconfig.co.uk/ip') do set pub=%%a
echo %pub%Batch parsing of ipconfig breaks on non-English Windows because the label is translated. Call PowerShell from batch instead: for /f %%a in ('powershell -NoProfile -Command "(Get-NetIPConfiguration | ? IPv4DefaultGateway).IPv4Address.IPAddress"') do set ip=%%a.
#!/usr/bin/env bash
# address used to reach the internet (handles multiple interfaces and VPNs correctly)
ip=$(ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1); exit}')
echo "$ip"
# by interface, with jq
ip -j -4 addr show enp3s0 | jq -r '.[0].addr_info[0].local'
# by interface, without jq
ip -4 -o addr show enp3s0 | awk '{print $4}' | cut -d/ -f1
# all addresses, space separated
hostname -I
# MAC
cat /sys/class/net/enp3s0/address
# public
curl -s https://ipconfig.co.uk/ip#!/bin/zsh
ip=$(ipconfig getifaddr en0)
[[ -z "$ip" ]] && ip=$(ipconfig getifaddr en1)
echo "$ip"
# the interface that carries the default route, then its address
if=$(route -n get default 2>/dev/null | awk '/interface:/{print $2}')
ipconfig getifaddr "$if"
# gateway and DNS
ipconfig getoption "$if" router
scutil --dns | awk '/nameserver\[0\]/{print $3; exit}'
# public
curl -s https://ipconfig.co.uk/ipAlert when the public IP changes
Useful for home servers without a static IP. Store the last value, compare, act. Run it every few minutes from Task Scheduler, cron or launchd.
#!/usr/bin/env bash
# /usr/local/bin/ipwatch.sh — run from cron: */5 * * * * /usr/local/bin/ipwatch.sh
state="$HOME/.public-ip"
new=$(curl -4 -s --max-time 10 https://ipconfig.co.uk/ip) || exit 0
[[ "$new" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || exit 0 # ignore garbage
old=$(cat "$state" 2>/dev/null)
if [[ "$new" != "$old" ]]; then
echo "$new" > "$state"
logger -t ipwatch "public IP changed: ${old:-none} -> $new"
# notify: pick one
# curl -s -d "IP changed to $new" ntfy.sh/your-topic
# printf 'Subject: IP changed\n\n%s\n' "$new" | sendmail you@example.com
# update dynamic DNS, e.g. Cloudflare:
# curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records/$REC" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' --data "{\"content\":\"$new\"}"
fi# Save as C:\Scripts\ipwatch.ps1; schedule with:
# schtasks /create /tn IPWatch /sc minute /mo 5 /tr "powershell -NoProfile -ExecutionPolicy Bypass -File C:\Scripts\ipwatch.ps1"
$state = "$env:LOCALAPPDATA\public-ip.txt"
try { $new = (Invoke-RestMethod -Uri https://ipconfig.co.uk/ip -TimeoutSec 10).Trim() } catch { exit }
if ($new -notmatch '^\d+\.\d+\.\d+\.\d+$') { exit }
$old = if (Test-Path $state) { Get-Content $state } else { '' }
if ($new -ne $old) {
Set-Content $state $new
Write-EventLog -LogName Application -Source 'Application' -EventId 1000 -Message "Public IP changed: $old -> $new" -ErrorAction SilentlyContinue
# notify: pick one
# Invoke-RestMethod -Method Post -Uri https://ntfy.sh/your-topic -Body "IP changed to $new"
# Send-MailMessage -To you@example.com -From pc@example.com -Subject 'IP changed' -Body $new -SmtpServer smtp.example.com
}Be polite to whatever service you poll: every five minutes is plenty. This site's /ip endpoint is fine for that; it returns only the address with no rate limit for reasonable use.
Export a network report for a ticket
@echo off
set out=%USERPROFILE%\Desktop\netreport-%COMPUTERNAME%.txt
(
echo === %DATE% %TIME% %COMPUTERNAME% ===
echo.
echo --- ipconfig /all --- & ipconfig /all
echo --- route print -4 --- & route print -4
echo --- arp -a --- & arp -a
echo --- netsh wlan show interfaces --- & netsh wlan show interfaces
echo --- DNS test --- & nslookup example.com & nslookup example.com 1.1.1.1
echo --- ping --- & ping -n 4 1.1.1.1 & ping -n 4 example.com
echo --- tracert --- & tracert -d -h 15 1.1.1.1
echo --- netstat -e --- & netstat -e
echo --- excluded ports --- & netsh int ipv4 show excludedportrange protocol=tcp
) > "%out%" 2>&1
echo Saved to %out%
start notepad "%out%"$out = "$env:USERPROFILE\Desktop\netreport-$env:COMPUTERNAME.txt"
& {
Get-Date; $env:COMPUTERNAME
'--- adapters ---'; Get-NetAdapter | Format-Table Name, Status, LinkSpeed, MacAddress -AutoSize
'--- config ---'; Get-NetIPConfiguration -Detailed
'--- routes ---'; Get-NetRoute -AddressFamily IPv4 | Sort-Object RouteMetric | Format-Table -AutoSize
'--- neighbours ---'; Get-NetNeighbor -AddressFamily IPv4 | Where-Object State -ne Unreachable | Format-Table -AutoSize
'--- dns ---'; Get-DnsClientServerAddress; Resolve-DnsName example.com -ErrorAction Continue
'--- tests ---'; Test-NetConnection 1.1.1.1; Test-NetConnection example.com -Port 443
'--- wifi ---'; netsh wlan show interfaces
} *> $out
Invoke-Item $out#!/usr/bin/env bash
out="$HOME/netreport-$(hostname)-$(date +%Y%m%d-%H%M).txt"
{
date; hostname; uname -a
echo '--- addresses ---'; ip addr 2>/dev/null || ifconfig -a
echo '--- routes ---'; ip route 2>/dev/null || netstat -rn
echo '--- neighbours ---'; ip neigh 2>/dev/null || arp -an
echo '--- dns ---'; resolvectl status 2>/dev/null || scutil --dns 2>/dev/null || cat /etc/resolv.conf
echo '--- link ---'; for i in $(ls /sys/class/net 2>/dev/null); do echo "$i: $(cat /sys/class/net/$i/operstate) $(cat /sys/class/net/$i/speed 2>/dev/null)Mb"; done
echo '--- wifi ---'; iw dev 2>/dev/null; nmcli dev wifi 2>/dev/null | head; sudo wdutil info 2>/dev/null
echo '--- tests ---'; ping -c 4 1.1.1.1; ping -c 4 example.com; dig +short example.com; dig +short @1.1.1.1 example.com
echo '--- path ---'; traceroute -n -m 15 1.1.1.1 2>/dev/null || tracepath -n 1.1.1.1
echo '--- public ---'; curl -s --max-time 5 https://ipconfig.co.uk/ip
} > "$out" 2>&1
echo "Saved to $out"Wait for the network before doing something
#!/usr/bin/env bash
# wait up to 60 s for a default route and working DNS
for i in $(seq 1 60); do
if ip route show default | grep -q . && getent hosts example.com >/dev/null; then
echo "network up"; break
fi
sleep 1
done
# systemd users: order the unit After=network-online.target and Wants=network-online.target instead$deadline = (Get-Date).AddSeconds(60)
while ((Get-Date) -lt $deadline) {
if ((Get-NetRoute -DestinationPrefix 0.0.0.0/0 -ErrorAction SilentlyContinue) -and (Test-Connection 1.1.1.1 -Count 1 -Quiet)) { 'network up'; break }
Start-Sleep 1
}#!/bin/zsh
ipconfig waitall # blocks until all interfaces have finished configuring
until route -n get default >/dev/null 2>&1; do sleep 1; done
echo "network up"Toggle between DHCP and a static profile
@echo off
rem usage: netmode static | netmode dhcp (run as administrator)
set NIC=Ethernet
if /i "%1"=="static" (
netsh interface ipv4 set address name="%NIC%" static 192.168.1.50 255.255.255.0 192.168.1.1
netsh interface ipv4 set dnsservers name="%NIC%" static 1.1.1.1 primary
netsh interface ipv4 add dnsservers name="%NIC%" 1.0.0.1 index=2
) else (
netsh interface ipv4 set address name="%NIC%" source=dhcp
netsh interface ipv4 set dnsservers name="%NIC%" source=dhcp
)
ipconfig | findstr /c:"IPv4" /c:"Gateway"#!/usr/bin/env bash
# create two profiles once, then switch between them
sudo nmcli con add type ethernet ifname enp3s0 con-name lan-dhcp ipv4.method auto
sudo nmcli con add type ethernet ifname enp3s0 con-name lan-static ipv4.method manual \
ipv4.addresses 192.168.1.50/24 ipv4.gateway 192.168.1.1 ipv4.dns 1.1.1.1
# switch
sudo nmcli con up lan-static
sudo nmcli con up lan-dhcp# Locations hold complete network configurations; create one with a static address, then switch:
sudo networksetup -createlocation Lab populate
sudo networksetup -switchtolocation Lab
sudo networksetup -setmanual "Ethernet" 192.168.1.50 255.255.255.0 192.168.1.1
# and back
sudo networksetup -switchtolocation AutomaticScheduled DHCP renew or adapter bounce
Occasionally a flaky driver or an ISP that requires periodic renewals justifies this. Prefer fixing the cause.
schtasks /create /tn "Renew DHCP" /sc daily /st 04:00 /ru SYSTEM /tr "ipconfig /renew"
schtasks /create /tn "Bounce Wi-Fi" /sc daily /st 04:05 /ru SYSTEM /tr "powershell -NoProfile -Command Restart-NetAdapter -Name Wi-Fi"
schtasks /delete /tn "Renew DHCP" /f# crontab -e (root)
0 4 * * * /usr/bin/nmcli device reapply enp3s0
# or a systemd timer for networkctl renew enp3s0# /Library/LaunchDaemons/local.renew.plist running: ipconfig set en0 DHCP
# or simpler, from root's crontab:
# 0 4 * * * /usr/sbin/ipconfig set en0 DHCPParsing tool output safely
- Prefer structured output.
ip -j(JSON), PowerShell objects,nmcli -t -f FIELDS(terse, colon-separated),networkctl --json=short,ipconfig getoptionon macOS. Parseipconfigorifconfigtext only as a last resort. - Assume names have spaces ("Wired connection 1", "Local Area Connection 2", "USB 10/100/1000 LAN"). Quote every variable.
- Assume more than one interface and more than one address per interface. VPNs, Docker and virtual machines add both. Pick by default route (
ip route get,Get-NetIPConfiguration | ? IPv4DefaultGateway) rather than "the first one". - Expect no output on success from Unix commands and check exit codes (
$?,$LASTEXITCODE). - Time out network calls (
curl --max-time,Invoke-RestMethod -TimeoutSec) so a script does not hang forever when the network is the very thing that is broken. - Log with timestamps.
loggeron Linux/macOS,Write-EventLogon Windows, or plaindate >> log. When a network problem is intermittent, the timestamp of the last good run is the most useful fact you will have.