Hey everyone,
If you're a Linux user who spends any amount of time in the terminal, you've probably heard the news: the incredibly popular system information tool, neofetch
, has been archived and is no longer being actively maintained.
Now, the community has plenty of fantastic alternatives, with fastfetch
being one of my personal favorites. But you know me... why just use someone else's tool when you can write your own and learn some stuff in the process?
So, in that spirit, I wanted to share not one, but two simple "fetch" utilities I've put together: one in pure Bash, and a more feature-rich version in Python.
fetch_pretty.sh
- The Lightweight Bash VersionFirst up is a simple script written entirely in Bash. It doesn't have a million features, but it's lightweight and does the basics: it grabs your OS, kernel version, current shell, and desktop environment/window manager, then prints it out with a splash of color. It's a great example of how much you can do with just standard shell commands.
fetch_pretty.sh
):#!/bin/bash
# Get the operating system name and version
os=$(lsb_release -ds)
# Get the kernel version
kv=$(uname -r)
# Get the name of the current shell
sh=${SHELL##*/}
# Initialize the user interface variable to 'unknown'
ui='unknown'
# Determine the user interface (desktop environment or window manager)
if [ -n "${DE}" ]; then
ui="${DE}" # If DE variable is set, use it
elif [ -n "${WM}" ]; then
ui="${WM}" # If WM variable is set, use it
elif [ -n "${XDG_CURRENT_DESKTOP}" ]; then
ui="${XDG_CURRENT_DESKTOP}" # Use XDG_CURRENT_DESKTOP if set
elif [ -n "${DESKTOP_SESSION}" ]; then
ui="${DESKTOP_SESSION}" # Use DESKTOP_SESSION if set
elif [ -n "${rcwm}" ]; then
ui="${rcwm}" # Use rcwm if set
elif [ -n "${XDG_SESSION_TYPE}" ]; then
ui="${XDG_SESSION_TYPE}" # Use XDG_SESSION_TYPE if set
fi
# Get the base name of the user interface variable
ui="$(basename "${ui}")"
# Function to set text color based on input argument
color() {
case "$1" in
red) printf '\033[1;31m' ;; # Set color to red
green) printf '\033[1;32m' ;; # Set color to green
yellow) printf '\033[1;33m' ;; # Set color to yellow
blue) printf '\033[1;34m' ;; # Set color to blue
magenta) printf '\033[1;35m' ;; # Set color to magenta
cyan) printf '\033[1;36m' ;; # Set color to cyan
gray) printf '\033[1;30m' ;; # Set color to gray
white) printf '\033[1;37m' ;; # Set color to white
reset) printf '\033[0m' ;; # Reset color to default
esac
}
# Initialize output variable with a newline
output="\n"
# Append formatted OS information to output
output+=$(printf "%b" "$(color red) os - $(color reset)$os\n")"\n"
# Append formatted kernel version to output
output+=$(printf "%b" "$(color yellow) kr - $(color reset)$kv\n")"\n"
# Append formatted shell information to output
output+=$(printf "%b" "$(color green) sh - $(color reset)$sh\n")"\n"
# Append formatted window manager information to output
output+=$(printf "%b" "$(color blue) wm - $(color reset)$ui\n")"\n\n"
# Convert output to lowercase efficiently
output=$(tr '[:upper:]' '[:lower:]' <<< "$output")
# Print the final output
printf "%b" "$output"
# Print a modern, colorful bar using solid block characters (optimized)
for color in red green yellow blue magenta cyan gray white; do
printf "%s\u2588\u2588 %s" "$(color "$color")" "$(color reset)"
done
printf "\n"
pyfetch.py
- The "Richer" Python VersionNext, I decided to build a more comprehensive version in Python, leveraging the amazing rich
library to create a nicely formatted panel. This one digs a bit deeper, fetching more information like CPU/GPU models, memory usage, uptime, and even package counts for various systems (Debian, Arch, and RPM-based distros).
The end result is a clean, modern-looking info panel right in your terminal.
pyfetch.py
):#!/usr/bin/env -S uv run --quiet --script
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "rich",
# ]
#
# ///
"""
A pretty system fetch utility inspired by fetch.sh and fetch_pretty.sh, using the rich library for output.
"""
import os
import platform
import socket
import subprocess
from datetime import timedelta
from datetime import datetime
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
console = Console()
def get_os(): #
try:
if os.path.exists("/etc/os-release"):
with open("/etc/os-release") as f:
for line in f:
if line.startswith("PRETTY_NAME="):
return line.strip().split("=", 1)[1].strip('"')
return platform.system()
except Exception:
return platform.system()
def get_kernel(): #
return platform.release()
def get_user_host(): #
return f"{os.getenv('USER') or os.getenv('LOGNAME')}@{socket.gethostname()}"
def get_uptime(): #
try:
with open("/proc/uptime") as f:
uptime_seconds = float(f.readline().split()[0])
uptime_td = timedelta(seconds=int(uptime_seconds))
days = uptime_td.days
hours, remainder = divmod(uptime_td.seconds, 3600)
minutes, _ = divmod(remainder, 60)
return f"{days}D {hours}H {minutes}M"
except Exception:
return "Unknown"
def get_shell(): #
return os.path.basename(os.getenv("SHELL", "Unknown"))
def get_desktop(): #
# Try common environment variables
for var in [
"XDG_CURRENT_DESKTOP",
"DESKTOP_SESSION",
"DE",
"WM",
"XDG_SESSION_TYPE",
]:
val = os.getenv(var)
if val:
return val
# Try to detect from running processes
try:
for pid in os.listdir("/proc"):
if pid.isdigit():
try:
with open(f"/proc/{pid}/comm") as f:
proc = f.read().strip()
if any(
wm in proc.lower()
for wm in [
"awesome",
"xmonad",
"qtile",
"sway",
"i3",
"openbox",
"fluxbox",
"bspwm",
"wm",
]
):
return proc
except Exception:
continue
except Exception:
pass
return "Unknown"
def get_package_count(): #
# Try dpkg (Debian/Ubuntu)
try:
result = subprocess.run(["dpkg", "-l"], capture_output=True, text=True)
count = sum(1 for line in result.stdout.splitlines() if line.startswith("ii"))
return str(count)
except Exception:
pass
# Try rpm (Fedora/openSUSE/RHEL)
try:
result = subprocess.run(["rpm", "-qa"], capture_output=True, text=True)
count = len(result.stdout.splitlines())
return str(count)
except Exception:
pass
# Try pacman (Arch/Manjaro/Artix)
try:
result = subprocess.run(["pacman", "-Q"], capture_output=True, text=True)
count = len(result.stdout.splitlines())
return str(count)
except Exception:
pass
return "N/A"
def get_memory(): #
try:
with open("/proc/meminfo") as f:
lines = f.readlines()
mem_total = (
int([line for line in lines if line.startswith("MemTotal:")][0].split()[1])
// 1024
)
mem_free = (
int(
[line for line in lines if line.startswith("MemAvailable:")][0].split()[
1
]
)
// 1024
)
return f"{mem_free}MiB / {mem_total}MiB"
except Exception:
return "Unknown"
def get_cpu(): #
model = None
cores = 0
try:
with open("/proc/cpuinfo") as f:
for line in f:
if line.startswith("model name") and model is None:
model = line.strip().split(":", 1)[1].strip()
if line.startswith("processor"):
cores += 1
if model and cores:
return f"{model} (x {cores} cores)"
elif model:
return model
else:
return platform.processor() or "Unknown"
except Exception:
return "Unknown"
def get_gpu(): #
try:
result = subprocess.run(["lspci"], capture_output=True, text=True)
gpus = []
for line in result.stdout.splitlines():
if any(
x in line.lower()
for x in ["vga compatible controller", "3d controller"]
):
# Get the part after the last colon (the model)
if ":" in line:
gpu_model = line.split(":")[-1].strip()
gpus.append(gpu_model)
if gpus:
return "; ".join(gpus)
else:
return "Unknown"
except Exception:
return "Unknown"
def main():
info = {
"OS": get_os(),
"Kernel": get_kernel(),
"CPU": get_cpu(),
"GPU": get_gpu(),
"Uptime": get_uptime(),
"Shell": get_shell(),
"Desktop": get_desktop(),
"Packages": get_package_count(),
"Memory": get_memory(),
}
table = Table(show_header=False, show_edge=False, box=None, padding=(0, 1))
colors = [
"cyan",
"magenta",
"green",
"yellow",
"blue",
"red",
"white",
"bright_black",
]
for idx, (k, v) in enumerate(info.items()):
label = Text(f"{k}", style=f"bold {colors[idx % len(colors)]}")
value = Text(v, style="bold white")
table.add_row(label, value)
# Use the current local time
current_time = datetime.now().astimezone()
formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S %Z")
panel = Panel(
table,
title=f"[bold green]{get_user_host()}[/bold green]",
border_style="bright_magenta",
padding=(0, 1),
width=80,
subtitle=f"[dim]{formatted_time}[/dim]",
)
console.print(panel)
if __name__ == "__main__":
main()
It was a fun little project to see what I could pull together. As I've said before, reinventing the wheel is one of the best ways to learn the ins and outs of your system and the tools you use.
As always,
Michael Garcia a.k.a. TheCrazyGM
That's so sad to hear about neofetch. I loved that tool. I still haven't tried fastfetch, but I'll take a look at it. Thank you for creating not one, but two replacements. I appreciate that you're running the xanmod kernel. I was running different iterations of that for a while. I liked it a lot. 😁 🙏 💚 ✨ 🤙
Congratulations @thecrazygm! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)
Your next target is to reach 450 posts.
You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word
STOP
Check out our last posts:
Check out fastfetch,the neofetch replacement.
The Bilpcoin team is here to support Hive and expose the truth. We genuinely want to see Hive grow and flourish, but we must address the issues holding it back. We have already highlighted:
TURN OFF THE BUILDAWHALE SCAM FARM
Key Issues That Demand Immediate Attention:
The problems are glaring, undeniable, and corrosive to the Hive ecosystem. They must be addressed without delay:
These practices harm not just individual users—they undermine the very foundation of Hive, eroding trust and poisoning the community. Such actions are unethical and outright destructive.
@buildawhale Wallet:
@usainvote Wallet:
@buildawhale/wallet | @usainvote/wallet
@ipromote Wallet:
Author Rewards: 2,181.16
Curation Rewards: 4,015.61
Staked HIVE (HP): 0.00
Rewards/Stake Co-efficient (KE): NaN
HIVE: 25,203.749
Staked HIVE (HP): 0.000
Delegated HIVE: 0.000
Estimated Account Value: $6,946.68
Recent Activity:
@leovoter Wallet:
Author Rewards: 194.75
Curation Rewards: 193.88
Staked HIVE (HP): 0.00
Rewards/Stake Co-efficient (KE): 388,632.00 (Suspiciously High)
HIVE: 0.000
Staked HIVE (HP): 0.001
Total: 16.551
Delegated HIVE: +16.550
Recent Activity:
@abide Wallet:
Recent Activity:
@proposalalert Wallet:
Recent Activity:
@stemgeeks Wallet:
Recent Activity:
@theycallmemarky Wallet:
Recent Activity:
@apeminingclub Wallet:
Recent Activity:
Scheduled unstake (power down): ~2.351 HIVE (in 4 days, remaining 7 weeks)
Total Staked HIVE: 1,292.019
Delegated HIVE: +1,261.508
Withdraw vesting from @apeminingclub to @blockheadgames 2.348 HIVE (10 days ago)
Claim rewards: 0.290 HP (10 days ago)
@blockheadgames Wallet:
Recent Activity:
@empoderat Wallet:
Recent Activity:
@gogreenbuddy Wallet:
Recent Activity:
@rollingbones Wallet:
Recent Activity:
The blockchain data tells the story plainly and clearly. We are not fabricating these claims; we are merely presenting what is already visible for all to see. As we’ve repeatedly urged @themarkymark & Co—the solution is simple: STOP.
SO PLEASE STOP. It’s time to do what’s right for Hive and its community. Why cling to practices that harm others? Power down, step away, and let Hive thrive as it was meant to. You bring nothing positive to this ecosystem.
THOSE WHO ARE WATCHING—THIS COULD HAPPEN TO YOU.
PLEASE STOP.
We’ve shown that we’re here to stay and have exposed the truth. So why has nothing been done? Do @themarkymark and @blocktrades own the Hive Blockchain? Is that why no action is being taken?
https://peakd.com/hive-122609/@bilpcoinbpc/themarkymark-and-co-you-seem-to-enjoy-spreading-lies-but-here-s-the-truth-we-have-no-reason-to-lie
https://peakd.com/hive-167922/@bilpcoinbpc/themarkymark-and-co-you-claim-we-re-the-number-one-spammers-on-hive-but-all-we-do-is-share-the-truth-lol
https://peakd.com/hive-133987/@bpcvoter3/isn-t-it-funny-how-themarkymark-and-co-are-suddenly-talking-about-ke-levels-on-hive-well-look-what-we-found-some-of-the-accounts
https://peakd.com/hive-178265/@bpcvoter1/themarkymark-and-co-the-same-old-tactics-are-at-play-calling-people-names-and-telling-others-to-avoid-them-lol-keep-digging-and
https://peakd.com/hive-122609/@bpcvoter2/hive-community-are-we-really-going-to-allow-a-small-group-of-individuals-to-make-hive-look-like-a-joke-we-ve-done-the-work-we-ve
https://peakd.com/hive-196233/@bpcvoter1/you-call-it-sound-advice-coming-from-themarkymark