Files
sharkfetch/sharkfetch
T
2026-07-27 08:57:43 -07:00

327 lines
13 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# https://chillcog.com
# Haiiiii - @chillcog
# Licensed Under The MIT License
clear
PRIVATE_MODE=0
show_help() {
cat << EOF
Usage: $(basename "$0") [OPTIONS]
A custom system information fetch script featuring ASCII art.
Options:
-h, --help Show this help message and exit.
-p, -private, Private mode: hides uniquely identifying information
--private (hostname, public/local IP, hardware model).
EOF
exit 0
}
for arg in "$@"; do
case "$arg" in
-h|--help)
show_help
;;
-p|-private|--private)
PRIVATE_MODE=1
;;
*)
echo "Unknown option: $arg"
echo "Use -h or --help for usage."
exit 1
;;
esac
done
c0=$'\e[0m'
blue=$'\e[34m'
c1=$'\e[36m'
c2=$'\e[35m'
OS_TYPE="$(uname -s)"
ARCH="$(uname -m)"
USER_NAME="$(whoami)"
if [ "$PRIVATE_MODE" -eq 1 ]; then
HOST_NAME="[REDACTED]"
else
HOST_NAME="$(hostname 2>/dev/null || uname -n)"
fi
user_host="${USER_NAME}@${HOST_NAME}"
get_os() {
if [ "$OS_TYPE" = "Linux" ]; then
if [ -f /etc/os-release ]; then
source /etc/os-release && echo "$NAME ($ARCH)"
else
echo "Linux ($ARCH)"
fi
elif [ "$OS_TYPE" = "Darwin" ]; then
echo "macOS $(sw_vers -productVersion 2>/dev/null) ($ARCH)"
else
echo "$OS_TYPE ($ARCH)"
fi
}
os=$(get_os)
kernel="$(uname -r)"
get_motherboard() {
if [ "$PRIVATE_MODE" -eq 1 ]; then
echo "[REDACTED]"
return
fi
if [ -f /sys/devices/virtual/dmi/id/product_name ]; then
local vendor name ver
vendor=$(cat /sys/devices/virtual/dmi/id/sys_vendor 2>/dev/null)
name=$(cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null)
ver=$(cat /sys/devices/virtual/dmi/id/product_version 2>/dev/null)
echo "${vendor} ${name} ${ver}" | xargs
elif command -v sysctl >/dev/null 2>&1 && [ "$OS_TYPE" = "Darwin" ]; then
sysctl -n hw.model 2>/dev/null
else
echo "N/A"
fi
}
host_model=$(get_motherboard)
uptime_str=$(uptime 2>/dev/null | sed -E 's/.*up[[:space:]]+([^,]+).*/\1/' | xargs)
[ -z "$uptime_str" ] && uptime_str="N/A"
get_packages() {
local pkgs=""
if command -v pacman >/dev/null 2>&1; then
pkgs="$(pacman -Qq 2>/dev/null | wc -l | xargs) (pacman)"
elif command -v dpkg >/dev/null 2>&1; then
pkgs="$(dpkg -l 2>/dev/null | grep -c '^ii') (dpkg)"
elif command -v rpm >/dev/null 2>&1; then
pkgs="$(rpm -qa 2>/dev/null | wc -l | xargs) (rpm)"
elif command -v brew >/dev/null 2>&1; then
pkgs="$(brew list --formula 2>/dev/null | wc -l | xargs) (brew)"
elif command -v nix-store >/dev/null 2>&1; then
pkgs="$(nix-store -q --requisites /run/current-system 2>/dev/null | wc -l | xargs) (nix)"
else
pkgs="N/A"
fi
echo "$pkgs"
}
packages=$(get_packages)
get_shell() {
local sh_bin
sh_bin="$(basename "${SHELL:-bash}")"
case "$sh_bin" in
bash) echo "bash $($SHELL --version 2>/dev/null | head -n1 | awk '{print $4}')" ;;
zsh) echo "zsh $($SHELL --version 2>/dev/null | awk '{print $2}')" ;;
fish) echo "fish $($SHELL --version 2>/dev/null | awk '{print $3}')" ;;
*) echo "$sh_bin" ;;
esac
}
shell_info=$(get_shell)
de="${XDG_CURRENT_DESKTOP:-${DESKTOP_SESSION:-N/A}}"
wm="${XDG_SESSION_DESKTOP:-N/A}"
[ "$OS_TYPE" = "Darwin" ] && de="Aqua" && wm="Quartz Compositor"
terminal="${TERM_PROGRAM:-${TERM:-N/A}}"
get_display() {
if command -v xrandr >/dev/null 2>&1; then
xrandr 2>/dev/null | awk '/\*/ {print $1 " @" $2 "Hz"; exit}' | tr -d '*' | xargs
elif [ "$OS_TYPE" = "Darwin" ]; then
system_profiler SPDisplaysDataType 2>/dev/null | awk -F: '/Resolution/ {print $2}' | head -n1 | xargs
else
echo "N/A"
fi
}
display_info=$(get_display)
[ -z "$display_info" ] && display_info="N/A"
get_cpu() {
if command -v lscpu >/dev/null 2>&1; then
local model cores
model=$(lscpu | awk -F: '/Model name/ {print $2}' | xargs)
cores=$(lscpu | awk -F: '/CPU\(s\):/ {print $2}' | head -n1 | xargs)
echo "${model} (${cores} threads)"
elif [ "$OS_TYPE" = "Darwin" ]; then
local model cores
model=$(sysctl -n machdep.cpu.brand_string 2>/dev/null)
cores=$(sysctl -n hw.ncpu 2>/dev/null)
echo "${model} (${cores} cores)"
else
echo "N/A"
fi
}
cpu=$(get_cpu)
[ -z "$cpu" ] && cpu="N/A"
get_gpu() {
if command -v lspci >/dev/null 2>&1; then
lspci 2>/dev/null | grep -E 'VGA|3D' | awk -F: '{print $3}' | sed 's/ (rev .*//' | head -n1 | xargs
elif [ "$OS_TYPE" = "Darwin" ]; then
system_profiler SPDisplaysDataType 2>/dev/null | awk -F: '/Chipset Model/ {print $2}' | head -n1 | xargs
else
echo "N/A"
fi
}
gpu=$(get_gpu)
[ -z "$gpu" ] && gpu="N/A"
get_memory() {
if command -v free >/dev/null 2>&1; then
free -m | awk '/Mem:/ {printf "%sMB / %sMB (%.0f%%)", $3, $2, ($3/$2)*100}'
elif [ "$OS_TYPE" = "Darwin" ]; then
local total_mem
total_mem=$(sysctl -n hw.memsize 2>/dev/null)
total_mem=$((total_mem / 1024 / 1024))
echo "${total_mem}MB Total"
else
echo "N/A"
fi
}
memory=$(get_memory)
get_swap() {
if command -v free >/dev/null 2>&1; then
free -m | awk '/Swap:/ {if ($2>0) printf "%sMB / %sMB (%.0f%%)", $3, $2, ($3/$2)*100; else print "Disabled"}'
else
echo "N/A"
fi
}
swap=$(get_swap)
disk=$(df -h / 2>/dev/null | awk 'NR==2 {print $3 " / " $2 " (" $5 ")"}')
get_temp() {
if command -v sensors >/dev/null 2>&1; then
sensors 2>/dev/null | awk '/Package id 0:|Core 0:|cpu_thermal/ {print $2; exit}' | tr -d '+'
elif [ -f /sys/class/thermal/thermal_zone0/temp ]; then
local raw
raw=$(cat /sys/class/thermal/thermal_zone0/temp 2>/dev/null)
echo "$((raw / 1000))°C"
else
echo "N/A"
fi
}
cpu_temp=$(get_temp)
[ -z "$cpu_temp" ] && cpu_temp="N/A"
load_avg=$(uptime 2>/dev/null | awk -F'load average:' '{print $2}' | xargs)
[ -z "$load_avg" ] && load_avg="N/A"
if [ "$PRIVATE_MODE" -eq 1 ]; then
local_ip="[HIDDEN]"
public_ip="[HIDDEN]"
else
get_ip() {
if command -v ip >/dev/null 2>&1; then
ip route get 1.1.1.1 2>/dev/null | awk '{print $7}'
elif command -v ifconfig >/dev/null 2>&1; then
ifconfig 2>/dev/null | awk '/inet / && !/127.0.0.1/ {print $2}' | head -n1
else
echo "N/A"
fi
}
local_ip=$(get_ip)
[ -z "$local_ip" ] && local_ip="N/A"
public_ip=$(curl -s --max-time 1 https://icanhazip.com 2>/dev/null || echo "Offline/N/A")
fi
get_battery() {
if [ -d /sys/class/power_supply ]; then
local cap status
cap=$(cat /sys/class/power_supply/BAT*/capacity 2>/dev/null | head -n1)
status=$(cat /sys/class/power_supply/BAT*/status 2>/dev/null | head -n1)
if [ -n "$cap" ]; then
echo "${cap}% [${status:-Unknown}]"
else
echo "AC Powered"
fi
elif command -v pmset >/dev/null 2>&1; then
pmset -g batt 2>/dev/null | grep -o '[0-9]*%' | head -n1
else
echo "N/A"
fi
}
battery=$(get_battery)
locale_val="${LANG:-N/A}"
raw_logo=$(cat << 'EOF'
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡴⠲⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣷⡄⠈⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⢀⣾⡀⠀⠀⠀⠀⠹⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⡓⠦⢄⣀⠀⠀⡼⠻⠿⢶⡄⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢀⡤⠤⠤⣤⣤⣴⣶⣶⠶⠶⠶⠒⠒⠂⠙⠀⠀⠀⠉⠉⠓⠲⢤⣀⠀⠀⠀⠀⠀⢹⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠈⢿⠈⠳⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠢⣄⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠈⡇⠀⠀⠀⠀⠀⢀⣿⡋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣟⠁⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀⢈⡽⠁⠀⠀⠠⡄⠀⠀⠀⠀⠀⠀⠀⣄⠀⠀⠀⠀⠀⠀⠘⡆⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⠀⣠⠞⠀⠀⠀⠀⣠⢧⠀⠀⠀⠀⠀⠀⠀⠘⣆⠀⠀⠀⠀⠀⠀⢹⠀⣸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢆⡴⠃⠀⠀⠀⢠⠞⠁⠈⢣⡀⣄⠀⠀⠀⠀⠀⠘⣆⠀⠀⠀⠀⠀⢸⣠⡇⠀⠀⠀⠀⠀⠀⠀⢀⡀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⢞⣓⣸⠀⢠⠰⣋⣀⣀⣳⢤⡙⢾⣟⠲⢤⣀⣠⠄⢯⠳⡀⠀⠀⠀⠸⠋⡇⠀⠀⠀⠀⠀⠀⢠⠏⠹⡄⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⣿⠀⢸⢰⠋⠀⣸⣿⣿⡆⠀⠈⠃⢤⣯⣿⡶⠾⣄⠘⡦⠀⠀⢀⣦⣿⠀⠀⠀⠀⠀⢠⠃⠀⠀⢳⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⡄⢸⣼⣀⡀⠣⠽⠿⢃⠀⠀⠀⢾⣻⣿⡿⠀⣸⡸⠁⠀⢀⡼⡼⠉⠳⣄⠀⠀⣰⠃⠀⠀⠀⢸⠇
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠟⣷⡘⣿⣛⡁⠀⠀⠀⠀⠁⠀⠀⠀⠉⠍⠡⣶⡿⠁⠀⣠⠟⣇⡇⠀⠀⠈⢦⣰⠁⠀⠀⠀⠀⢸⠂
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⠖⢻⣣⣈⣿⣤⠤⢒⠒⠦⠖⠢⠴⢄⣀⢬⣷⠞⢁⣠⠞⣡⠞⢹⡟⠀⠀⠀⠀⠃⠀⠀⠀⠀⢀⡞⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢏⣻⡶⠞⠉⠛⢉⣆⣴⡞⠻⠦⣾⢿⣊⣉⡴⣶⠁⠀⠀⠑⢄⡀⢀⢰⠋⢦⠀⠀⣠⠞⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢨⣿⣤⣶⣦⠀⠀⡇⠀⠀⣤⣄⠹⡟⠛⢷⠀⠁⠀⠀⠀⠀⠀⠙⠺⣏⣀⠈⢷⠚⠁⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⢿⣿⣿⣝⢿⡇⠀⣧⢠⣾⣿⣿⣿⡿⡄⢸⡄⠀⠀⠀⠀⠀⠀⠀⢸⠏⠉⠁⠈⡆⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡟⠀⠀⠀⠙⠃⠀⢸⣿⠈⠱⠟⠛⠛⠃⢹⢸⣿⡄⠀⠀⠀⣤⡀⢀⡿⠛⠓⠂⢀⢱⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣸⡀⠀⠀⠀⠀⠀⣠⢟⣸⠦⣄⣀⠀⠀⠀⠘⣸⠹⣟⡆⠀⡼⠁⠙⣾⣷⣦⣄⠀⠀⢸⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⠞⠉⠀⠘⡇⠀⣀⣀⡠⠞⣱⠋⠀⠀⠀⠈⠙⢦⡀⠀⢹⡚⠉⠀⢰⠃⠀⠀⠀⠘⣆⠀⠉⠀⢸⡇⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣜⠁⠀⠀⠀⠀⠙⠉⢹⣿⠁⢰⠃⠀⠀⠀⠀⠀⠀⠀⠑⣦⣬⠇⠀⢀⡏⠀⠀⠀⠀⠉⠀⠀⠀⢀⢸⡇⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⡠⠟⠛⠛⠷⠂⠀⠀⠀⠀⠈⣿⢠⡇⣠⣤⣤⣤⣀⠀⠀⠀⠀⠘⣿⣶⣶⣿⠁⠀⠀⠀⠀⠀⠀⠀⠀⡞⢸⠃⠀⠀⠀
⠀⠀⠀⠀⠀⢀⣾⣷⣤⣤⠀⠀⠀⠀⠀⠀⠀⠀⠛⡼⢉⢉⣉⣉⠛⠻⢷⣄⠀⠀⠀⣸⣯⠙⢿⣧⠀⠀⠀⠀⠀⠀⢀⡼⠁⡼⠀⠀⠀⠀
⠀⠀⠀⠀⡰⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡤⠀⡗⠛⠛⠻⠿⣷⣦⣀⠀⠀⠀⠀⠘⢻⠀⠀⠙⠃⠀⠀⠀⠀⡠⠊⠀⢠⠃⠀⠀⠀⠀
⠀⣠⣖⣟⣳⣒⣆⠀⠀⠀⠀⠀⠀⠀⠀⣠⣎⠀⢸⠁⠀⠀⠀⠀⠀⠉⠉⠀⢸⠀⠀⠀⣏⠀⠀⠀⠀⠀⠠⠒⠋⠀⠀⢠⠏⠀⠀⠀⠀⠀
⠰⣿⣿⣶⠿⠷⣾⠃⠀⠀⠀⠀⢀⡠⠚⠁⠈⠦⡞⠀⠀⠀⠀⠀⠀⠀⠀⢠⠏⠀⠀⡼⠉⠁⠀⠀⠀⠀⠀⠀⠀⣠⠔⠋⠀⠀⠀⠀⠀⠀
⠀⠹⣦⣓⡒⠋⠉⠀⠀⠀⣀⡴⠋⠀⠀⠀⠀⡼⠁⡀⠀⠀⠀⠀⠀⠀⣠⠏⢀⣠⢾⣁⣀⣀⣀⣀⣠⠤⠒⠚⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠹⣦⣿⣯⣿⣦⢀⡴⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
EOF
)
transparent_logo=$(echo "$raw_logo" | sed 's/⠀/ /g' | sed 's/[[:space:]]*$//')
readarray -t logo <<< "$transparent_logo"
labels=(
"OS:" "Host Model:" "Kernel:" "Uptime:" "Packages:" "Shell:"
"DE/WM:" "Terminal:" "Resolution:" "CPU:" "CPU Temp:" "GPU:"
"Memory:" "Swap:" "Disk (/):" "Load Avg:" "Local IP:" "Public IP:"
"Battery:" "Locale:"
)
values=(
"$os" "$host_model" "$kernel" "$uptime_str" "$packages" "$shell_info"
"${de} / ${wm}" "$terminal" "$display_info" "$cpu" "$cpu_temp" "$gpu"
"$memory" "$swap" "$disk" "$load_avg" "$local_ip" "$public_ip"
"$battery" "$locale_val"
)
info=()
info+=("${c1}${user_host}${c0}")
info+=("---------------------------------------")
for i in "${!labels[@]}"; do
lbl=$(printf "%-13s" "${labels[$i]}")
info+=("${c1}${lbl}${c0} ${c2}${values[$i]}${c0}")
done
target_width=72
max_lines=$((${#logo[@]} > ${#info[@]} ? ${#logo[@]} : ${#info[@]}))
for ((i=0; i<max_lines; i++)); do
line="${logo[i]}"
char_count=${#line}
pad_needed=$((target_width - char_count))
(( pad_needed < 0 )) && pad_needed=0
padding=$(printf '%*s' "$pad_needed" '')
printf "%s%s%s %s\n" "${blue}${line}${c0}" "$padding" "" "${info[i]}"
done