`neofetch` is Dead, Long Live... My Own Fetch Tools!

(edited)

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.

1. fetch_pretty.sh - The Lightweight Bash Version

First 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.

The Output:

Simple Bash Utility

The Code (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"

2. pyfetch.py - The "Richer" Python Version

Next, 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.

The Output:

Python Fetch Utility

The Code (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

0.16839185 BEE
3 comments

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. 😁 🙏 💚 ✨ 🤙

0.00118581 BEE

Congratulations @thecrazygm! You have completed the following achievement on the Hive blockchain And have been rewarded with New badge(s)

You published more than 400 posts.
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:

0.00000000 BEE

Check out fastfetch,the neofetch replacement.

-0.18368752 BEE

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

  • STOP THE BAD DOWNVOTES
  • STOP PLAYING WITH PEOPLE’S LIVELIHOODS

Key Issues That Demand Immediate Attention:

The problems are glaring, undeniable, and corrosive to the Hive ecosystem. They must be addressed without delay:

  • Downvote abuse
  • Farming schemes
  • Speaking disrespectfully to others
  • Encouraging people to avoid certain users out of personal dislike
  • Self-voting with alt accounts
  • Self-funding projects through self-votes
  • Promoting games that don’t even exist (LOL)

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:

  • HIVE (Primary Token): 0.012
  • Staked HIVE (HP): 66,400.611
  • Total HP: 2,421,539.226
  • Delegated HIVE: +2,355,138.615 HP

@usainvote Wallet:

  • HIVE (Primary Token): 0.066
  • Staked HIVE (HP): 138,123.296
  • Total HP: 715,745.407
  • Delegated HIVE: +577,622.111 HP

@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:

  • Sent to alpha-5,196.000 HIVE (21 hours ago)
  • Sent to hiveswap-1,000.000 HIVE (2 days ago)
  • Withdraw vesting from @proposalalert to @ipromote 0.447 HIVE (3 days ago)
  • Received from proposalalert 4.003 HIVE (5 days ago)
  • Received from themarkymark 1,775.684 HIVE (9 days ago)
  • Sent to alpha-4,245.000 HIVE (9 days ago)
  • Received from themarkymark 4,280.527 HIVE (17 days ago)

@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:

  • Sent to ipromote -2,459.000 HIVE (22 days ago)
  • Sent to ipromote -2,486.200 HIVE (Apr 1, 2025)
  • Received from yabapmatt 20,000.000 HIVE (Apr 1, 2025)
  • Sent to ipromote -2,130.400 HIVE (Mar 8, 2025)
  • Sent to ipromote -2,248.000 HIVE (Feb 2, 2025)
  • Sent to yabapmatt -5,000.000 HIVE (Jan 25, 2025)

@proposalalert Wallet:

  • Author Rewards: 639.99
  • Curation Rewards: 0.00
  • Staked HIVE (HP): 6.03
  • Rewards/Stake Co-efficient (KE): 106.12

Recent Activity:


@stemgeeks Wallet:

  • Author Rewards: 4,391.77
  • Curation Rewards: 304.26
  • Staked HIVE (HP): 0.00
  • Rewards/Stake Co-efficient (KE): 4,696,032.00 (Extremely Suspicious)

Recent Activity:

  • Sent to themarkymark -1.556 HBD (Jun 14, 2024)
  • Claim rewards: 1.556 HBD, 5.912 HP (Jun 14, 2024)
  • Withdraw vesting from @stemgeeks to @ipromote 6.160 HIVE (Jun 8, 2024)
  • Sent to themarkymark -1.601 HBD (Jun 7, 2024)
  • Withdraw vesting from @stemgeeks to @ipromote 6.157 HIVE (Jun 1, 2024)
  • Sent to ipromote -1.618 HBD (May 31, 2024)

@theycallmemarky Wallet:

  • Author Rewards: 458.89
  • Curation Rewards: 0.00
  • Staked HIVE (HP): 0.00
  • Rewards/Stake Co-efficient (KE): 458,886.00 (Highly Suspicious)

Recent Activity:


@apeminingclub Wallet:

  • Author Rewards: 432.57
  • Curation Rewards: 2,829.11
  • Staked HIVE (HP): 30.51
  • Rewards/Stake Co-efficient (KE): 106.90

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:

  • Author Rewards: 619.76
  • Curation Rewards: 99.30
  • Staked HIVE (HP): 0.00
  • Rewards/Stake Co-efficient (KE): 719,063.00 (Highly Suspicious)

Recent Activity:


@empoderat Wallet:

  • HIVE (Primary Token): 300.000
  • Staked HIVE (HP): 5,000.602
  • Total: 10.001
  • Delegated HIVE: -4,990.601

Recent Activity:

  • Received from acidyo 300.000 HIVE (5 hours ago)
  • Sent to bdhivesteem -10,000.808 HIVE (a day ago)
  • Received from black-mountain 387.048 HIVE (a day ago)
  • Received from hiveswap 3,384.700 HIVE (a day ago)
  • Powered up 500 HP (a day ago)
  • Stopped power down 0 HP (a day ago)
  • Received from acidyo 5,200.000 HIVE (a day ago)
  • Withdrew from vesting 798.346 HIVE (3 days ago)
  • Claimed rewards: 2.914 HBD, 17.291 HP (6 days ago)

@gogreenbuddy Wallet:

  • HIVE (Primary Token): 472.407
  • Staked HIVE (HP): 273,472.276
  • Total: 246,351.982
  • Delegated HIVE: -27,120.294

Recent Activity:

  • Received from @punkteam -0.172 HBD (Jan 20, 2024)
  • Received from @punkteam -327.485 HIVE (Jan 20, 2024)
  • Received from @punkteam -248.912 HIVE (Jan 9, 2024)
  • Received from @punkteam -248.772 HIVE (Dec 28, 2023)
  • Received from @punkteam -3.155 HBD (Dec 26, 2023)
  • Received from @punkteam -497.127 HIVE (Dec 26, 2023)
  • Received from @punkteam -248.356 HIVE (Dec 9, 2023)

@rollingbones Wallet:

  • Author Rewards: 212.88
  • Curation Rewards: 29.09
  • Staked HIVE (HP): 0.08
  • Rewards/Stake Co-efficient (KE): 2,880.63

Recent Activity:

  • Sent to ipromote -0.014 HIVE (Aug 19, 2021)
  • Sent to ipromote -0.449 HIVE (Jul 27, 2021)
  • Sent to ipromote -0.265 HIVE (Jul 10, 2021)
  • Sent to ipromote -1.202 HIVE (Jul 3, 2021)
  • Sent to ipromote -0.479 HIVE (Jun 8, 2021)
  • Sent to ipromote -2.945 HIVE (Jun 1, 2021)
  • Sent to ipromote -4.290 HIVE (Apr 20, 2021)
  • Sent to honey-swap -1.107 HIVE (Feb 10, 2021)
  • Sent to ipromote -0.895 HIVE (Jan 11, 2021)
  • Sent to ipromote -12.025 HIVE (Mar 7, 2020)

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

0.00000000 BEE