Same Job, Different Shell Part 1: Reachability and Continuous Monitoring
I spend most of my week moving between a Linux shell and a Windows one, and the number of times I have typed ping -t into a Linux terminal out of pure muscle memory is embarrassing. The commands aren’t hard. The problem is that Bash and PowerShell each have their own diagnostic toolkit, and the two overlap just enough to be dangerous: similar names, similar output, different flags, different defaults.
This is the first post in a series that pairs the two directly, task by task. Not “Linux is better” or “PowerShell is better”, just: you’re on this box, here’s the command, here’s what it actually does. I’ll start with the most basic diagnostic there is: is the other end alive. Along the way, real verification turned up something worth its own section: PowerShell itself isn’t one thing, and which version you’re running changes both the syntax and, in one case, what the tool actually tells you.
Which PowerShell are you actually running
Windows ships with Windows PowerShell 5.1 by default (PSEdition: Desktop), the one you get when you type powershell. PowerShell 7+ (PSEdition: Core) is a separate, optional install, launched with pwsh, and plenty of Windows boxes have both sitting side by side without whoever’s using them realizing it. Check which one a given session is in with:
PS> $PSVersionTable
PSVersion and PSEdition are the two lines that matter: 5.1.x / Desktop means the inbox version, 7.x / Core means the separately-installed one. This isn’t a minor version bump the way it sounds. Test-Connection, the cmdlet this whole post is about, was rewritten essentially from scratch for PowerShell 7, and the two versions don’t just differ in flags, they can report genuinely different information for the same command. More on that below.
The core tool: ping vs Test-Connection
Every OS ships a ping. Windows PowerShell also ships Test-Connection, a cmdlet-native wrapper that does the same ICMP echo but returns structured objects instead of text you have to parse.
Linux:
$ ping -c 4 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
--- 1.1.1.1 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3055ms
Windows, -ComputerName (works on both PowerShell 5.1 and 7+):
PS> Test-Connection -ComputerName 1.1.1.1 -Count 4
-ComputerName is the one to reach for as your default, it’s the portable parameter that works whichever version of PowerShell you land in. -TargetName, which I originally reached for while drafting this, only exists in PowerShell 7+ and errors outright on 5.1 with “a parameter cannot be found that matches parameter name ‘TargetName’.” If you’re writing a script that has to run on whatever PowerShell happens to be installed on a target machine, -ComputerName is the safe choice.
5.1 vs 7+: same command, a genuinely different answer
Here’s the real output from both versions, run back to back on the same machine against the same target, 1.1.1.1.
Windows PowerShell 5.1 (Desktop, the inbox default):
PS> Test-Connection -ComputerName 1.1.1.1 -Count 4
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
DESKTOP01 1.1.1.1 1.0.0.1 32 38
DESKTOP01 1.1.1.1 1.0.0.1 32 41
DESKTOP01 1.1.1.1 1.0.0.1 32 17
DESKTOP01 1.1.1.1 1.0.0.1 32 17
PowerShell 7.6.3 (Core, installed separately via pwsh):
PS> Test-Connection -ComputerName 1.1.1.1 -Count 4
Destination: 1.1.1.1
Ping Source Address Latency BufferSize Status
(ms) (B)
---- ------ ------- ------- ---------- ------
1 DESKTOP01 1.1.1.1 37 32 Success
2 DESKTOP01 1.1.1.1 26 32 Success
3 DESKTOP01 1.1.1.1 24 32 Success
4 DESKTOP01 1.1.1.1 16 32 Success
Two things worth knowing before you build anything on top of either. First, the object shape changed completely, not just cosmetically: 7+ adds a genuine per-packet Status column (Success/failure per ping, not just a final summary) that 5.1 has no equivalent of at all. Second, and this is the one that actually matters if you’re troubleshooting rather than just reading a demo: look at the Destination versus the address column on each version. Both runs targeted the literal IP 1.1.1.1. PowerShell 7.6.3’s Address column correctly shows 1.1.1.1 on every row, matching what was asked for. Windows PowerShell 5.1’s IPV4Address column shows 1.0.0.1 on every row instead, Cloudflare’s other anycast address, not the one specified.
That’s a real, reproduced discrepancy, not a typo in this post. I don’t have independent confirmation of the exact internal cause (my best guess is that 5.1’s Test-Connection, which is built on the older WMI Win32_PingStatus provider, runs its own resolution pass even against a literal IP, where 7+‘s rewritten cmdlet doesn’t), so I won’t state that as settled fact. What’s solid is the observed behavior: if you’re on Windows PowerShell 5.1 and troubleshooting a host that has multiple addresses, an anycast service, or a DNS record that’s changed recently, the address the cmdlet reports back to you is not guaranteed to be the one you actually specified. On PowerShell 7+, it is. If precise address accounting matters for what you’re diagnosing (and “which specific address actually answered” is exactly the kind of thing that matters when you’re chasing an anycast or load-balancing issue), that’s a concrete, tested reason to do the diagnostic work in pwsh rather than the inbox shell, not just a taste preference.
The classic ping.exe sidesteps this whole question, since it’s not going through either Test-Connection implementation:
C:\> ping 1.1.1.1
reports the literal address you gave it either way, same as it always has, because it isn’t resolving anything, just sending ICMP directly.
Flags that matter, side by side
| What you want | Linux ping | Test-Connection (PS 5.1 and 7+) | Test-Connection (PS 7+ only) | Windows ping.exe |
|---|---|---|---|---|
| Send N pings | -c 4 | -Count 4 | -Count 4 | -n 4 |
| Target, portable | n/a | -ComputerName <host> | -ComputerName or -TargetName <host> | n/a |
| Wait N seconds for reply | -W 2 | not exposed the same way | -TimeoutSeconds 2 | -w 2000 (ms) |
| Interval between pings | -i 0.2 | -Delay 2 (seconds, min ~1) | -Delay 2 | not exposed |
| Set packet size | -s 1400 | -BufferSize 1400 | -BufferSize 1400 | -l 1400 |
| Don’t fragment (MTU test) | -M do -s <size> | not exposed | -DontFragment | -f -l <size> |
| Boolean result only | exit code | -Quiet | -Quiet | exit code |
| Run forever / continuous | n/a | not available | -Repeat (alias -Continuous) | -t |
The interval flag is where Linux pulls ahead for anything serious. ping -i 0.2 gives you 5 pings a second, useful for spotting a brief blip that a default 1-second interval would step right over:
$ ping -i 0.2 -c 5 1.1.1.1
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
--- 1.1.1.1 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 813ms
PowerShell’s -Delay bottoms out around one second and won’t go sub-second at all, on either version.
A real finding from writing this: ICMP isn’t always the whole story
Every Linux ping above shows “100% packet loss”. That’s not a typo and it’s not the target being down. I ran these from this site’s own sandbox environment, and its outbound network policy allows TCP and DNS but drops ICMP echo entirely. curl to the same address works instantly:
$ curl -s -o /dev/null -w "HTTP %{http_code} in %{time_total}s\n" https://1.1.1.1
HTTP 200 in 0.460s
This is worth internalizing as a general rule, not a sandbox quirk: a “ping fails” report tells you almost nothing about whether a service is actually reachable. Plenty of real corporate firewalls, cloud security groups, and CDNs drop ICMP by policy while happily serving HTTPS. If ping fails, the next move on either OS is a real TCP test against the actual port the service uses, which is exactly what Part 8 of this series covers. Don’t diagnose an application outage with a tool that never touches the application’s port.
Continuous monitoring: watching a link over time
Sometimes you don’t want four pings, you want “keep pinging until I tell you to stop, and show me the moment it breaks.” Both platforms have this, but confirmed testing turned up a real gap between PowerShell versions here too.
Linux, three ways depending on what you’re doing:
# run forever, Ctrl+C to stop
$ ping 1.1.1.1
# refresh a status line every second using watch (works with anything, not just ping)
$ watch -n 1 -d 'ping -c 1 -W 1 1.1.1.1'
# log continuously to a file with timestamps, for reviewing later
$ ping 1.1.1.1 | while read line; do echo "$(date +%T) $line"; done | tee ping.log
ping with no -c runs until interrupted, which is the direct Linux equivalent of Windows’ ping -t. watch -d is the more interesting tool here: it re-runs any command on an interval and diffs the output against the previous run, highlighting what changed. It isn’t ping-specific. I use watch -n 2 -d 'ip -brief addr show' all the time when I’m changing an interface’s config and want to see the exact line that flips.
Windows, and this is the version split that actually matters:
# ping.exe -t: the one every Windows admin already knows, works everywhere
PS> ping -t 1.1.1.1
ping.exe -t is the one safe, version-independent answer. Test-Connection’s continuous option is not. I tested this directly: Test-Connection -ComputerName 1.1.1.1 -Continuous on Windows PowerShell 5.1 fails outright, “a parameter cannot be found that matches parameter name ‘Continuous’”, because 5.1’s Test-Connection has no repeat/continuous capability at all, not under any flag name. It’s not a naming difference to work around, the feature genuinely isn’t there.
# PowerShell 7+ only:
PS> Test-Connection -ComputerName 1.1.1.1 -Repeat -Delay 1
-Repeat (aliased -Continuous) only exists in the rewritten PowerShell 7+ cmdlet. If you’re writing something that needs to run continuously and might land on either PowerShell version, ping.exe -t is the one that will actually work regardless; reaching for Test-Connection -Repeat without checking $PSVersionTable first is a script that works on your machine and fails on the next admin’s.
There’s no direct PowerShell equivalent to watch for arbitrary commands on either version, only ad hoc while ($true) { <command>; Start-Sleep -Seconds 2; Clear-Host } loops, which work but don’t diff the output for you the way watch -d does.
The one-liner version of “is it up” for scripts
If you just need a boolean for a script or a scheduled task:
# Linux
ping -c 1 -W 1 1.1.1.1 >/dev/null 2>&1 && echo "UP" || echo "DOWN"
# Windows, works on both PS versions
if (Test-Connection -ComputerName 1.1.1.1 -Count 1 -Quiet) { "UP" } else { "DOWN" }
-Quiet predates the 7+ rewrite and behaves consistently on both versions, unlike -Repeat/-Continuous above. Both one-liners fall into the same ICMP-gets-blocked trap described earlier. If this check needs to reflect whether a service is up rather than whether the host answers ping, swap it for the TCP port test in Part 8 instead.
What’s next
Part 2 covers path tracing, traceroute and mtr against tracert and Test-NetConnection -TraceRoute, including a real finding from this same sandbox about why a UDP-based traceroute and a TCP-based one gave completely different answers for the same destination.
Cross-references: this series focuses on command comparison rather than deeper Linux internals; for the full ip command model see Beyond ifconfig: The ip Command Reference Every Network Engineer Needs, and for socket-level diagnostics see Replacing netstat with ss: A Network Engineer’s Diagnostic Guide.