Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Subnet Scan Bash Script
# Requires: ifconfig, ping, arp, date, wc, jq, and jc
if [[ $1 == "" ]]; then
echo "Please enter the interface name as command argument."
echo "Options:"
# Only show interfaces with an assigned IP address
jc ifconfig | jq -r '.[] | select(.ipv4_addr != null) | .name'
exit 1
tempName=$(basename "$0")
tempFile=$(mktemp /tmp/"$tempName".XXXXXX) || exit 1
# Grab interface IP and Subnet Mask
interfaceInfo=$(jc ifconfig "$1") || exit 1
ip=$(jq -r '.[0].ipv4_addr' <<<"$interfaceInfo")
mask=$(jq -r '.[0].ipv4_mask' <<<"$interfaceInfo")
# Grab detailed subnet information for the IP/Mask
ipInfo=$(jc --ip-address <<<"$ip/$mask")
network=$(jq -r '.network' <<<"$ipInfo")
numHosts=$(jq -r '.hosts' <<<"$ipInfo")
cidrMask=$(jq -r '.cidr_netmask' <<<"$ipInfo")
firstHostIp=$(jq -r '.first_host' <<<"$ipInfo")
lastHostIp=$(jq -r '.last_host' <<<"$ipInfo")
firstHost=$(jq -r '.int.first_host' <<<"$ipInfo")
lastHost=$(jq -r '.int.last_host' <<<"$ipInfo")
if [[ $numHosts -gt 1022 ]]; then
echo "Subnet is too large ($numHosts IPs). Exiting."
exit 1
# Grab the start time in ISO and Unix Epoch format
startTime=$(jc date)
startTimeIso=$(jq -r '.iso' <<<"$startTime")
startTimeEpoch=$(jq -r '.epoch' <<<"$startTime")
echo "My IP: $ip/$cidrMask"
echo "Sending ICMP requests to $numHosts IPs: $firstHostIp - $lastHostIp"
echo "Start Time: $startTimeIso"
# Loop through each IP. Use decimal value to easily iterate and convert to
# standard notation when using the ping command
for (( ipDecimal=firstHost; ipDecimal<=lastHost; ipDecimal++ )); do
# Do each ping in the background for parallel processing
# grab the packets received and rtt values from the ping output
thisIp=$(jc --ip-address <<<"$ipDecimal" | jq -r '.ip')
pingResult=$(ping -c1 "$thisIp" | jc --ping)
packetsReceived=$(jq -r '.packets_received' <<<"$pingResult")
rtTime=$(jq -r '.round_trip_ms_max' <<<"$pingResult")
if [[ $packetsReceived -gt 0 ]]; then
# Grab the MAC address and name for each alive host from the arp command
thisIpArpInfo=$(arp -a | jc --arp | jq --arg myip "$thisIp" '.[] | select(.address == $myip)')
thisIpMac=$(jq -r '.hwaddress // empty' <<<"$thisIpArpInfo")
thisIpName=$(jq -r '.name // empty' <<<"$thisIpArpInfo")
printf "%9.3f ms %-16s%-20s%s\n" "$rtTime" "$thisIp" "$thisIpMac" "$thisIpName" | tee -a "$tempFile"
} &
# Grab the end time in ISO and Unix Epoch format (for easy calculation)
endTime=$(jc date)
endTimeIso=$(jq -r '.iso' <<<"$endTime")
endTimeEpoch=$(jq -r '.epoch' <<<"$endTime")
# Grab the total lines of the temp output file, which equals the
# number of alive hosts
totalAlive=$(jc wc "$tempFile" | jq '.[0].lines')
rm "$tempFile"
echo "Scanned $network/$cidrMask subnet in $totalTime seconds."
echo "$totalAlive alive hosts found."
echo "End Time: $endTimeIso"
Copy link

cClaude commented Nov 3, 2022

  • Line 1:
 ^-- SC1113: Use #!, not just #, for the shebang.
  • Line 48:
for (( ipDecimal=$firstHost; ipDecimal<=$lastHost; ipDecimal++ )); do
                 ^--------^ SC2004: $/${} is unnecessary on arithmetic variables.
                                        ^-------^ SC2004: $/${} is unnecessary on arithmetic variables.
  • Line 73:
             ^-----------^ SC2004: $/${} is unnecessary on arithmetic variables.
                           ^-------------^ SC2004: $/${} is unnecessary on arithmetic variables.

Copy link

Thank you for the fixes, @cClaude!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment