How to Access System Information in Bash

How to Access System Information in Bash

Whenever I’m debugging a slow server, writing a health-check script, or just building a status dashboard, the first thing I reach for is a handful of Bash commands that expose everything about the system: CPU, memory, disk, network, and processes. Linux exposes almost all of this through plain text files in /proc and /sys, plus a set of standard utilities, which makes system information one of the easiest things to work with in Bash once you know where to look. This article covers exactly how I gather and use that information.

Where System Information Lives

On Linux, most system information comes from two places:

  • /proc: a virtual filesystem exposing kernel and process information as plain text files (e.g., /proc/cpuinfo, /proc/meminfo).
  • Standard utilities: commands like uname, uptime, df, free, top, ps, and lscpu that wrap and format the raw data from /proc and /sys in a more human-friendly way.

Beginner Example: Basic System Info

#!/usr/bin/env bash

echo "Hostname: $(hostname)"
echo "Kernel: $(uname -r)"
echo "OS: $(uname -o)"
echo "Architecture: $(uname -m)"
echo "Uptime: $(uptime -p)"

Example output:

Hostname: web-server-01
Kernel: 6.8.0-45-generic
OS: GNU/Linux
Architecture: x86_64
Uptime: up 3 weeks, 2 days, 4 hours

uname reads directly from kernel data structures; -r gives the kernel release, -o the operating system name, and -m the machine hardware name (architecture).

CPU Information

echo "CPU model: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d ':' -f2 | xargs)"
echo "CPU cores: $(nproc)"
  • /proc/cpuinfo lists one block per logical CPU core, so grep + head -1 grabs just the first entry since the model name repeats identically for every core on most systems.
  • cut -d ':' -f2 splits on the colon and takes everything after it, then xargs trims leading/trailing whitespace.
  • nproc is a dedicated Coreutils command that reports the number of available processing units, which is more reliable than manually counting lines in /proc/cpuinfo.

To see current CPU load:

uptime

Output:

14:32:01 up 22 days,  3:14,  2 users,  load average: 0.15, 0.22, 0.31

The three numbers are the load average over the last 1, 5, and 15 minutes respectively — a widely used, quick indicator of how busy the system has been.

Memory Information

free -h

Output:

               total        used        free      shared  buff/cache   available
Mem:            15Gi       4.2Gi       6.1Gi       210Mi       5.0Gi        10Gi
Swap:          2.0Gi          0B       2.0Gi

-h prints sizes in human-readable units (GiB, MiB) instead of raw kilobytes. Internally, free is just a formatted view of /proc/meminfo, which you can also read directly:

grep -E 'MemTotal|MemFree|MemAvailable' /proc/meminfo

Disk Usage

df -h

Output:

Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        50G   32G   16G  67% /

For directory-level breakdown of what’s actually consuming space:

du -sh /var/log/*

du -sh summarizes (-s) the total size of each item in human-readable form (-h), which I use constantly when hunting down what’s filling up a disk.

Network Information

ip addr show

For a quick summary of just IPv4 addresses:

ip -4 addr show | grep inet | awk '{print $2}'

To check open listening ports:

ss -tuln
  • -t shows TCP sockets, -u shows UDP sockets, -l shows only listening sockets, and -n shows numeric addresses/ports instead of resolving service names, which is both faster and avoids DNS lookups.

Process Information

ps aux --sort=-%mem | head -10

This lists all processes (aux), sorted by memory usage descending (--sort=-%mem), and shows the top 10 — one of the most useful one-liners I run when a server feels sluggish.

Step-by-Step: Building a System Info Script

Here’s a complete script that pulls together everything above into a single readable report:

#!/usr/bin/env bash
set -euo pipefail

echo "===== System Information Report ====="
echo "Generated: $(date)"
echo ""

echo "--- Host ---"
echo "Hostname: $(hostname)"
echo "OS: $(uname -o)"
echo "Kernel: $(uname -r)"
echo "Uptime: $(uptime -p)"
echo ""

echo "--- CPU ---"
echo "Model: $(grep 'model name' /proc/cpuinfo | head -1 | cut -d ':' -f2 | xargs)"
echo "Cores: $(nproc)"
echo "Load average: $(cut -d ' ' -f1-3 /proc/loadavg)"
echo ""

echo "--- Memory ---"
free -h
echo ""

echo "--- Disk ---"
df -h --output=source,size,used,avail,pcent,target | grep -v tmpfs
echo ""

echo "--- Top 5 Processes by Memory ---"
ps aux --sort=-%mem | head -6
echo ""

echo "--- Network Interfaces ---"
ip -4 addr show | grep inet | awk '{print $2, $NF}'

How This Works Internally

  • Every value here ultimately traces back to either a /proc file that the kernel updates in real time, or a syscall wrapped by a utility like ps or ip.
  • --output= on df lets you pick exactly which columns to display, which is more scriptable than parsing the default table layout with awk.
  • ps aux reads process information from /proc/[pid]/stat and /proc/[pid]/status for every running process, then formats it as a table.

Real-World Use Case: A Health-Check Script for Monitoring

#!/usr/bin/env bash
set -euo pipefail

DISK_THRESHOLD=85
MEM_THRESHOLD=90

disk_usage=$(df / --output=pcent | tail -1 | tr -dc '0-9')
mem_usage=$(free | awk '/Mem:/ {printf "%.0f", $3/$2 * 100}')

if [ "$disk_usage" -ge "$DISK_THRESHOLD" ]; then
    echo "WARNING: Disk usage at ${disk_usage}%"
fi

if [ "$mem_usage" -ge "$MEM_THRESHOLD" ]; then
    echo "WARNING: Memory usage at ${mem_usage}%"
fi

I run this every few minutes via cron and pipe warnings into the email script covered elsewhere in this series.

Automation Example: Logging System Metrics Over Time

#!/usr/bin/env bash
set -euo pipefail

LOG_FILE="/var/log/system_metrics.csv"

if [ ! -f "$LOG_FILE" ]; then
    echo "timestamp,cpu_load,mem_used_pct,disk_used_pct" > "$LOG_FILE"
fi

timestamp=$(date +%Y-%m-%dT%H:%M:%S)
cpu_load=$(cut -d ' ' -f1 /proc/loadavg)
mem_used_pct=$(free | awk '/Mem:/ {printf "%.1f", $3/$2 * 100}')
disk_used_pct=$(df / --output=pcent | tail -1 | tr -dc '0-9.')

echo "${timestamp},${cpu_load},${mem_used_pct},${disk_used_pct}" >> "$LOG_FILE"

Run every minute via cron to build a simple CSV time series I can later graph.

Security Considerations

  • Some information exposed by /proc (like full command lines of other users’ processes via ps aux) can reveal sensitive details, such as passwords accidentally passed as command-line arguments. Be cautious about who has read access to system monitoring output.
  • Restrict access to scripts and logs that capture system information, especially if they include IP addresses, hostnames, or process details that could aid an attacker mapping out your infrastructure.
  • If exposing any of this data through a web dashboard, never do so without authentication — system information is valuable reconnaissance for anyone attempting to attack a server.

Optimization Tips

  • Reading directly from /proc files is faster than shelling out to formatted utilities like free or df when you need to poll metrics very frequently (e.g., every second), since it avoids the overhead of spawning a new process for the formatting tool.
  • Cache values that don’t change often (like CPU model or total memory) instead of re-querying them on every loop iteration in a monitoring script.
  • For very frequent polling, consider a purpose-built monitoring agent (like node_exporter for Prometheus) instead of a custom Bash loop, which will be more efficient at scale.

Troubleshooting

  • “nproc: command not found”: install coreutils, though this is extremely rare since nproc ships by default on virtually all Linux distributions.
  • ps aux truncates long command lines: use ps auxww to disable line-width truncation.
  • df shows confusing tmpfs/overlay entries: filter them out with grep -v tmpfs or use df -h -x tmpfs -x devtmpfs.
  • Load average looks high but CPU seems idle: load average includes processes waiting on I/O, not just CPU-bound processes, so high disk I/O wait can inflate this number without heavy CPU usage.

Common Mistakes to Avoid

  • Confusing “used memory” with “available memory” — modern Linux uses free RAM aggressively for disk caching, so free‘s “used” column can look alarmingly high even when plenty of memory is actually available (check the available column instead).
  • Parsing ps/df/free output with fragile column-position assumptions instead of using their built-in formatting flags (--output=, custom format strings).
  • Running frequent system-info scripts as root unnecessarily when the same data is readable as a normal user.

FAQs

Does this approach work the same way on macOS? Partially. macOS doesn’t have /proc, so tools like sysctl, vm_stat, and top are used instead of reading /proc/cpuinfo or /proc/meminfo directly.

What’s the difference between uptime‘s load average and actual CPU usage percentage? Load average is a count of processes wanting CPU (or waiting on I/O) averaged over time, not a direct percentage of CPU utilization. A load average of 4 on a 4-core system roughly means full utilization; the same number on a 16-core system means the system is much less loaded.

How can I get information about a Docker container’s resource usage from Bash? docker stats gives a live view, or read the container’s own cgroup files under /sys/fs/cgroup/ from inside the container for programmatic access.

Is there a single command that gives an overview of everything at once? Tools like htop, glances, or neofetch provide nice interactive overviews, but for scripting purposes, combining the individual commands shown above gives you full control over formatting and automation.

Summary

Nearly everything you’d want to know about a Linux system — CPU, memory, disk, network, and processes — is available through a small set of standard commands and the /proc virtual filesystem. Once I understood that these tools are really just structured views into files the kernel maintains in real time, building health checks, monitoring scripts, and system reports in Bash became straightforward and fast, without needing any external monitoring agent for simple use cases.

References

  • GNU Coreutils manual: https://www.gnu.org/software/coreutils/manual/coreutils.html
  • Linux proc(5) man page: https://man7.org/linux/man-pages/man5/proc.5.html
  • procps-ng project (source of ps, free, uptime, top): https://gitlab.com/procps-ng/procps
  • iproute2 documentation (ip command): https://wiki.linuxfoundation.org/networking/iproute2
Total
2
Shares

Leave a Reply

Previous Post
How to Work with Dates and Times in Bash

How to Work with Dates and Times in Bash

Next Post
How to Send Email from Bash

How to Send Email from Bash

Related Posts