Created
September 14, 2025 14:59
-
-
Save fragtion/7507ccfc270acef5c1cc7a7ff3ac0943 to your computer and use it in GitHub Desktop.
ifstat script for WSL1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| ifstat.py — WSL1 network traffic monitor (via Windows PowerShell) | |
| WSL1 does not report interface stats properly via ifconfig or ifstat. | |
| This script mimics the behavior of `ifstat` by displaying per-interface | |
| network I/O statistics (KB/s in and KB/s out) updated once per second. | |
| It leverages a persistent PowerShell process to poll `Get-Counter` once per second. | |
| Credits: https://github.com/fragtion | |
| """ | |
| import subprocess | |
| import time | |
| import sys | |
| import json | |
| import textwrap | |
| def powershell_stream(): | |
| ps_cmd = """ | |
| while ($true) { | |
| Get-NetAdapterStatistics | | |
| Select-Object Name,ReceivedBytes,SentBytes | | |
| ConvertTo-Json -Compress; | |
| Start-Sleep -Seconds 1 | |
| } | |
| """ | |
| return subprocess.Popen( | |
| ["/mnt/c/Windows/SysWOW64/WindowsPowerShell/v1.0/powershell.exe", "-Command", ps_cmd], | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.DEVNULL, | |
| text=True, | |
| bufsize=1 | |
| ) | |
| def parse_stats(line): | |
| try: | |
| data = json.loads(line.strip()) | |
| if isinstance(data, dict): | |
| data = [data] | |
| stats = {} | |
| for entry in data: | |
| adapter = entry["Name"] | |
| received = int(entry["ReceivedBytes"]) | |
| sent = int(entry["SentBytes"]) | |
| stats[adapter] = (received, sent) | |
| return stats | |
| except Exception: | |
| return {} | |
| proc = powershell_stream() | |
| # wait for first sample | |
| first_line = proc.stdout.readline() | |
| prev_stats = parse_stats(first_line) | |
| if not prev_stats: | |
| print("No adapters found or error occurred.") | |
| proc.terminate() | |
| sys.exit(1) | |
| adapters = list(prev_stats.keys()) | |
| col_width = 10 | |
| label_width = col_width * 2 | |
| wrapped_names = [] | |
| max_lines = 0 | |
| for adapter in adapters: | |
| wrapped = textwrap.wrap(adapter, label_width) | |
| wrapped_names.append(wrapped) | |
| if len(wrapped) > max_lines: | |
| max_lines = len(wrapped) | |
| print("Starting network traffic monitoring. Press Ctrl+C to stop.") | |
| print("Note: First sample is baseline. Rates start from second sample.\n") | |
| for line in range(max_lines): | |
| parts = [] | |
| for wrapped in wrapped_names: | |
| text = wrapped[line] if line < len(wrapped) else "" | |
| parts.append(f"{text:^{label_width}}") | |
| print(" ".join(parts)) | |
| subheader = " ".join( | |
| f"{'KB/s in':>{col_width}}{'KB/s out':>{col_width}}" for _ in adapters | |
| ) | |
| print(subheader) | |
| prev_time = time.time() | |
| try: | |
| for line in proc.stdout: | |
| current_stats = parse_stats(line) | |
| if not current_stats: | |
| continue | |
| current_time = time.time() | |
| delta_time = current_time - prev_time | |
| line_parts = [] | |
| for adapter in adapters: | |
| if adapter in current_stats and adapter in prev_stats: | |
| prev_received, prev_sent = prev_stats[adapter] | |
| curr_received, curr_sent = current_stats[adapter] | |
| delta_received = (curr_received - prev_received) / delta_time / 1024 | |
| delta_sent = (curr_sent - prev_sent) / delta_time / 1024 | |
| rx_str = f"{delta_received:>{col_width}.2f}" | |
| tx_str = f"{delta_sent:>{col_width}.2f}" | |
| line_parts.append(f"{rx_str}{tx_str}") | |
| else: | |
| line_parts.append(f"{'0.00':>{col_width}}{'0.00':>{col_width}}") | |
| print(" ".join(line_parts)) | |
| prev_stats = current_stats | |
| prev_time = current_time | |
| except KeyboardInterrupt: | |
| proc.terminate() | |
| print("\nMonitoring stopped.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment