Netcat Cheatsheet

Port Scanning

Use this Netcat reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

Zero-I/O mode

-z connects, reports, and closes without transferring data. Combined with -v it is a usable port check, and it is the only scanning netcat does.

nc -zv 10.0.0.5 22
nc -zv 10.0.0.5 20-25
nc -zv 10.0.0.5 22 80 443
nc -zvn -w 1 10.0.0.5 1-1024
FlagWhy
-zZero I/O, do not send or expect data
-vRequired. Without it, results are printed nowhere
-nNo DNS, otherwise every port waits on a lookup
-w 1Per-port timeout. Without it, filtered ports take minutes
-rRandomize the port order (not in Ncat)
-uScan UDP instead, but see the caveat below

Ranges are lo-hi inclusive, and a space-separated list works on OpenBSD nc and macOS nc. Output goes to stderr, which is why this looks empty:

nc -zv 10.0.0.5 1-100 | grep succeeded        # WRONG, nothing matches
nc -zv 10.0.0.5 1-100 2>&1 | grep succeeded   # right

Reading the results

Connection to 10.0.0.5 22 port [tcp/ssh] succeeded!
nc: connectx to 10.0.0.5 port 23 (tcp) failed: Connection refused
nc: connectx to 10.0.0.5 port 445 (tcp) failed: Operation timed out
ResultStateMeans
succeeded!openHandshake completed, something is listening
Connection refusedclosedHost reachable, a RST came back
Times out at -wfilteredSilently dropped by a firewall
No route to host—Local routing problem, not the target
ICMP unreachablefilteredA router refused explicitly

The distinction that matters: refused proves the host is up, filtered tells you nothing about the host. A scan that is all timeouts usually means the host is down or you are being blocked wholesale.

Scripting a scan

# Open ports only, one per line
nc -zvn -w 1 10.0.0.5 1-1024 2>&1 | grep succeeded

# Just the port numbers
nc -zvn -w 1 10.0.0.5 1-1024 2>&1 | \
  awk '/succeeded/ {print $(NF-2)}'

# Common ports across a subnet
for h in 10.0.0.{1..254}; do
  for p in 22 80 443 3389; do
    nc -zn -w 1 "$h" "$p" 2>/dev/null && echo "$h:$p open"
  done
done

# Parallel, much faster on a /24
printf '10.0.0.%s\n' {1..254} | xargs -P 50 -I{} sh -c \
  'nc -zn -w 1 {} 22 2>/dev/null && echo "{} 22 open"'

# Single check for a script or healthcheck
nc -z -w 3 db.internal 5432 && echo up || echo down

The awk field index depends on the variant's message format, so check the raw output once before trusting a parse. grep succeeded is portable.

Waiting for a port to come up

The most legitimately common use of netcat in automation, worth more than the scanning.

# Wait for Postgres, give up after 30 tries
for i in $(seq 30); do
  nc -z -w 1 db 5432 && break
  sleep 1
done

# Compact form
until nc -z -w 1 db 5432; do sleep 1; done
echo "db is accepting connections"

# With a hard deadline
timeout 60 sh -c 'until nc -z -w 1 db 5432; do sleep 1; done' \
  || { echo "db never came up"; exit 1; }

This is the entire content of most wait-for-it.sh scripts. Remember that -z success means the port accepts TCP, not that the service finished initializing. Postgres binds its port before it is ready to answer queries, so follow up with a real protocol check where it matters.

UDP "scanning" mostly does not work

nc -zvu 10.0.0.5 53
nc -zvu -w 1 10.0.0.5 100-200

UDP has no handshake, so there is nothing to succeed. What you actually learn:

ObservedInferred
ICMP port unreachableclosed, and only if not rate-limited
Nothing at allopen or filtered or the packet was lost
A reply payloadopen, and now you know the service

nc -zu reports "succeeded" for almost every port because the local send succeeded. It is not a UDP port check. Send a real payload for the protocol instead:

# A DNS query is the only reliable way to check port 53
dig +short +time=2 @10.0.0.5 example.com

# NTP, SNMP, and similar need their own probe
ntpdate -q 10.0.0.5

Linux rate-limits ICMP unreachables (roughly one per second by default), so even the closed-port signal is unreliable across a range.

When to reach for nmap instead

netcat's scan is a full TCP connect on every port, sequentially, which is slow and lands in every log on the target.

NeedTool
One port, in a scriptnc -z -w 3
Wait for a serviceuntil nc -z ...; do sleep 1; done
A range, quicklynmap -p 1-1024 10.0.0.5
Service and versionnmap -sV -p 22,80,443 10.0.0.5
SYN scan, no full handshakesudo nmap -sS 10.0.0.5
Real UDP scansudo nmap -sU -p 53,161 10.0.0.5
Host discovery on a subnetnmap -sn 10.0.0.0/24
# nmap does in one second what the netcat loop does in minutes
nmap -Pn -p 1-1024 --open 10.0.0.5

Scan only hosts you own or have written authorization to test. Unauthorized port scanning is a crime in many jurisdictions, and a full connect scan is trivially attributable because it completes the handshake from your address.