You are not logged in.

#3801 2024-12-12 06:49:32

Head_on_a_Stick
Member
From: The Wirral
Registered: 2014-02-20
Posts: 8,842
Website

Re: Post your handy self made command line utilities

@HobbitJack is using a /bin/sh shebang, presumably to avoid the bash bloat, so they should probably read the "Case Conditional Construct" section of https://pubs.opengroup.org/onlinepubs/9 … hap02.html instead.

Bash == /bin/sh in Arch but most other distributions aren't so foolish.


Jin, Jîyan, Azadî

Offline

#3802 2024-12-12 09:04:33

HobbitJack
Member
From: Florida or Michigan
Registered: 2024-09-21
Posts: 17
Website

Re: Post your handy self made command line utilities

In this case it was mostly because this was bodged together several times (this is version 2 lol). Also something of force of Python habit, but this isn't Python. Next time I update it I'll want to merge the argument checking into a case statement, but if I recall I was just adding those ad hoc so whatever came out is what we got here.
I am at least aware of case statements, don't worry! =D
slides massive shell script monstrosities full of bloated case-esac used for data analysis under a rock

Last edited by HobbitJack (2024-12-12 09:06:58)


Astrophysicist and programmer. I like simple things.

Offline

#3803 2024-12-12 20:11:12

snakeroot
Member
Registered: 2012-10-06
Posts: 172

Re: Post your handy self made command line utilities

A seasonally-appropriate script that uses "find" to scan through FLAC files, find any with "Christmas" or "Xmas" in the file name and asks if the GENRE tag should add "Christmas" as a GENRE if it is absent.

#! /bin/bash
if [[ $# -eq 0 ]]; then
        /usr/bin/find . -regextype egrep -regex '.*(Xmas|Christmas).*\.flac' -exec $0 {} \;
else
        GENRE=$(/usr/bin/metaflac --show-tag=GENRE $1)
        if [[ ! $(echo $GENRE | /usr/bin/grep "Christmas") ]]; then
                echo "$1 does not contain Christmas in GENRE"
                read -p "Would you like to add it? [y/n]" answer
                if [[ $answer == y ]]; then
                        NEW_GENRE="$GENRE; Christmas"
                        NEW_GENRE=$(echo $NEW_GENRE | sed 's/GENRE=//')
                        echo "New GENRE is $NEW_GENRE"
                        /usr/bin/metaflac --preserve-modtime --remove-tag=GENRE --set-tag="GENRE=$NEW_GENRE" $1
                        /usr/bin/metaflac --show-tag=TITLE --show-tag=GENRE $1
                fi
        fi
fi

Offline

#3804 2025-01-12 01:12:42

HobbitJack
Member
From: Florida or Michigan
Registered: 2024-09-21
Posts: 17
Website

Re: Post your handy self made command line utilities

Another little script from me, this one to manage my ever-growing collection of 'noteson${thing}.txt'.

#!/usr/bin/sh

case $1 in
        ''|'-h'|'--help'|'help')
                echo 'Usage: note COMMAND|NOTE'
                echo 'manage notes'
                echo
                echo "Notes are saved at '~/.local/share/note/'."
                echo
                echo 'COMMAND:'
                echo '    cat NOTE        print the contents of NOTE to STDOUT'
                echo '    ls              list all notes'
                echo '    reinit          delete all notes and recreate note directory'
                echo '    rm  NOTE        delete NOTE'
                echo '    help            print this help'
                echo '    version         print version and license information'
                echo
                echo 'NOTE:'
                echo "    Open NOTE in '`basename ${EDITOR:-vi}`'"
                exit
                ;;
        '-v'|'--version'|'version')
                echo 'note v2.1.3'
                echo 'Copyright (C) 2024 Jack Renton Uteg.'
                echo 'License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.'
                echo 'This is free software: you are free to change and redistribute it.'
                echo 'There is NO WARRANTY, to the extent permitted by law.'
                echo
                echo 'Written by Jack Renton Uteg.'
                exit
                ;;
        'cat')
                if [ -z "$2" ]
                then
                        echo "note cat: no note specified"
                        exit 1
                fi
                cat ~/.local/share/note/$2
                ;;
        'ls')
                ls -1 ~/.local/share/note/
                ;;
        'reinit')
                rm -rf ~/.local/share/note/
                mkdir -p ~/.local/share/note/
                ;;
        'rm')
                if [ -z "$2" ]
                then
                        echo "note rm: no note specified"
                        exit 1
                fi
                rm ~/.local/share/note/$2
                ;;
        *)
                if [ -z "$1" ]
                then
                        echo "note: no note specified"
                        exit 1
                fi

                ${EDITOR:-vi} ~/.local/share/note/$1
esac

Exceptionally minimal, since all this is a basic wrapper for a few regular commands that you can otherwise do with a tad bit more typing. Likely not of interest to anyone but me -- but I at least did use 'case' this time lol


Astrophysicist and programmer. I like simple things.

Offline

#3805 2025-01-12 08:08:15

seth
Member
Registered: 2012-09-03
Posts: 64,568

Re: Post your handy self made command line utilities

echo "note rm: no note specified"

cd ~/.local/share/note/
select note in *; do echo "$note" && break; done

Or you look into autocompletion wink

You also likely want to quote the parameters everywhere, eg. you're testing "$2" but attempt to delete the unquoted $2

Offline

#3806 2025-01-14 04:13:15

HobbitJack
Member
From: Florida or Michigan
Registered: 2024-09-21
Posts: 17
Website

Re: Post your handy self made command line utilities

I *might* look into autocomplete, but a select interface is interactive and exactly not the kind of thing I want to deal with. I'd rather just be told I typed it wrong.

Fixed the quoting oversight, though.


Astrophysicist and programmer. I like simple things.

Offline

#3807 2025-02-10 14:14:27

EISENFELD
Member
From: Germany
Registered: 2024-03-13
Posts: 38
Website

Re: Post your handy self made command line utilities

If you are like me and always check for updates directly after boot with "yay", then you may want this automation too:
This small script creates and enables a systemd service. The service checks for updates and installs them if available with yay.

#!/bin/bash

# Prompt for the username
read -p "Enter the username: " USERNAME

SERVICE_FILE="yay-update.service"
SERVICE_PATH="/etc/systemd/system/${SERVICE_FILE}"

# Create the service file
cat <<EOF > "${SERVICE_PATH}"
[Unit]
Description=Yay System Update by user
After=network-online.target

[Service]
Type=oneshot
User=${USERNAME}
ExecStart=/bin/sh -c "sleep 5 && /usr/bin/yay --noconfirm --sudoloop"

[Install]
WantedBy=multi-user.target
EOF

# Enable and start the service
systemctl enable "${SERVICE_FILE}"
systemctl start "${SERVICE_FILE}"

echo "Service ${SERVICE_FILE} has been installed, enabled, and started for the user: ${USERNAME}."

If you get an error like "can't resolve host...", you have do change "sleep 5" to let's say "8" or "sleep 10".
It depends on how fast your internet connection is available. For my connection over cable It must be "5" or more.
The script must be executed as root of course.
Check the status of the new service with "systemctl status yay-update.service".


Ich weiß, dass ich nichts weiß !

Offline

#3808 2025-02-10 15:02:42

seth
Member
Registered: 2012-09-03
Posts: 64,568

Re: Post your handy self made command line utilities

If you get an error like "can't resolve host...", you have do change "sleep 5" to let's say "8" or "sleep 10".

https://bbs.archlinux.org/viewtopic.php … 1#p2090691

The script must be executed as root of course.

system services/timers run as root.

PSA: unattended updates (leaving alone with yay) are not a very good idea, you want to log that somewhere and present yourself that log after the session started.

Offline

#3809 2025-02-16 07:02:08

EISENFELD
Member
From: Germany
Registered: 2024-03-13
Posts: 38
Website

Re: Post your handy self made command line utilities

This Script will update with "yay" and informs the user about the progress with desktop notifications.
After that it completely cleans the Pacman / Yay cache and use logrotate on pacman.log file.
Every action is logged in /tmp/yay_update.log

You can start this script everytime you boot with a systemd service.
Here is my /usr/local/bin/yay_update.sh file.
Don't forget to make it executable (chmod +x yay_update.sh)
Maybe you have to adjust "sleep 5". My cable connection is ready in 5 seconds.

#!/bin/bash
# File: /usr/local/bin/yay-update.sh

LOG="/tmp/yay_update.log"

sleep 5 && ID=$(notify-send -p -t 1000 -u normal -a Yay --hint=string:sound-name:service-login 'Yay Update' 'System update started ...')

# Trim log file to prevent infinite growth
tail -n 100 $LOG > /tmp/yay_update.tmp && mv /tmp/yay_update.tmp $LOG

echo "$(date): Starting system update..." >> $LOG

# Perform system update
if yay --noconfirm --sudoloop --noprogressbar --needed 2>&1 | tee -a $LOG; then
    if grep -q "Error" /tmp/yay_update.log; then
        beep -f 2000 
        notify-send -r $ID -t 5000 -u critical -a Yay -i /usr/share/icons/Adwaita/symbolic/status/computer-fail-symbolic.svg --hint=string:sound-name:dialog-error 'Yay Update' 'Error during update! See /tmp/yay_update.log'
        exit 1
    fi
    beep -f 100
else
    beep -f 2000 
    notify-send -r $ID -t 5000 -u critical -a Yay -i /usr/share/icons/Adwaita/symbolic/status/computer-fail-symbolic.svg --hint=string:sound-name:dialog-error 'Yay Update' 'Error during update! See /tmp/yay_update.log'
    exit 1
fi

# Check if no update was necessary
if grep -q "there is nothing to do" $LOG; then
    beep -f 500
    notify-send -r $ID -t 1000 -u normal -a Yay -i /usr/share/icons/Adwaita/symbolic/categories/emoji-body-symbolic.svg --hint=string:sound-name:dialog-information 'Yay Update' 'No updates available!'
    echo "No cache cleanup and logrotate needed, as no update was performed!" >> $LOG
    exit 0
fi

# Clean cache
echo "- Cleaning Yay and Pacman cache ..." >> $LOG
yay -Sc --noconfirm 2>&1 | tee -a $LOG
sudo rm -rf /var/cache/pacman/pkg/ 2>&1 | tee -a $LOG
echo "Cache completely cleaned." >> $LOG

# Apply logrotate to pacman.log
echo "- Starting logrotate for /var/log/pacman.log" >> $LOG
if ! sudo logrotate -f -v /etc/logrotate.d/pacman 2>&1 | tee -a $LOG; then
    beep -f 2000
    notify-send -t 5000 -u critical -a Yay --hint=string:sound-name:dialog-error 'Yay Update' 'Error during logrotate!'
fi

echo "Done! Everything completed." >> $LOG
beep -f 500
notify-send -r $ID -t 1000 -u normal -a Yay -i /usr/share/icons/Adwaita/symbolic/categories/emoji-body-symbolic.svg --hint=string:sound-name:complete 'Yay Update' 'New updates installed!'

Now everything is logged in /tmp/yay_update.log
This script works very well under Gnome desktop/Wayland.

Here ist my yay-update.service file /etc/systemd/system/yay-update.service (Gnome/Wayland):
Don't forget to replace "User=lennart" with your username.

[Unit]
Description=Yay System Update by user
After=network-online.target
Wants=network-online.target

[Service]
TimeoutStartSec=infinity
Type=oneshot
User=lennart
Environment=DISPLAY=:0 WAYLAND_DISPLAY=wayland-0 XDG_RUNTIME_DIR=/run/user/1000
ExecStart=/usr/local/bin/yay-update.sh

[Install]
WantedBy=multi-user.target

Here is the configuration file for logrotate: /etc/logrotate.d/pacman:

/var/log/pacman.log {
 weekly
 rotate 4
 compress
 missingok
 notifempty
 create 644 root root
}

Install the service with:

sudo systemctl enable yay-update.service

Test the service with:

sudo systemctl start yay-update.service

As I wrote before, It works very well with Gnome / Wayland. For other systems, you have to adjust it.
btw. "beep", "logrotate" and "notify-send" must be installed

yay -S beep logrotate notify-send

Regards,
EISENFELD

Last edited by EISENFELD (2025-02-16 11:19:33)


Ich weiß, dass ich nichts weiß !

Offline

#3810 2025-02-16 08:38:47

seth
Member
Registered: 2012-09-03
Posts: 64,568

Re: Post your handy self made command line utilities

As discussed in the linked thread, the network-online.target typically does not do what you'd expect.
Wait until you've an icmp response from a relevant peer.

yay -Scc --noconfirm | tee -a /tmp/yay_update.log

This will drop stderr, otoh you're probably not interested in a copy of stdout?

… >> /tmp/yay_update.log 2>&1

Offline

#3811 2025-02-16 11:21:12

EISENFELD
Member
From: Germany
Registered: 2024-03-13
Posts: 38
Website

Re: Post your handy self made command line utilities

I think "yay --noconfirm --sudoloop --noprogressbar --needed 2>&1 | tee -a $LOG" is the best solution.
I updated the code in my post.


Ich weiß, dass ich nichts weiß !

Offline

#3812 2025-02-25 00:37:23

NuSkool
Member
Registered: 2015-03-23
Posts: 268

Re: Post your handy self made command line utilities

yay --noconfirm --sudoloop --noprogressbar --needed 2>&1 | tee -a $LOG

It might be worth pointing out that the following IIRC, would be an alternative equivalent.

 yay --noconfirm --sudoloop --noprogressbar --needed |& tee -a $LOG

  I commonly use '|&' to pipe both 'stdout' and 'stderr'.


Scripts I Use                                                 :  https://github.com/Cody-Learner
grep -m1 'model name' /proc/cpuinfo    : AMD Ryzen 7 8745HS w/ Radeon 780M Graphics
grep -m1 'model name' /proc/cpuinfo    : Intel(R) N95
grep -m1 'model name' /proc/cpuinfo    : AMD Ryzen 5 PRO 2400GE w/ Radeon Vega Graphics

Offline

#3813 2025-02-25 05:36:28

EISENFELD
Member
From: Germany
Registered: 2024-03-13
Posts: 38
Website

Re: Post your handy self made command line utilities

If someone is interested. Here is the final (so far) version with install script:
It works very well on my system, I didn't test it on others. There are also some nice screenshots:
https://github.com/lennart1978/Yay-autoupdate

Last edited by EISENFELD (2025-02-25 05:38:34)


Ich weiß, dass ich nichts weiß !

Offline

#3814 2025-03-06 09:15:43

kokoko3k
Member
Registered: 2008-11-14
Posts: 2,439

Re: Post your handy self made command line utilities

Just got a Rival SteelSeries 3 mouse with leds on it to be used on an always on PC.
To take care of leds, this one turns them off after 15miin inactivity and back on as soon as an input is registered; going to make a simple systemd service out of it:

while true ; do  
	if timeout 900 inotifywait /dev/input/event* ; then 
		c1="08080b" ; c2=$c1 ; 
		rivalcfg  --light-effect steady --logo-color $c1 --strip-top-color $c2 --strip-middle-color $c2 --strip-bottom-color $c2
	else 
		c1="000" ; c2=$c1 ; 
		rivalcfg  --light-effect steady --logo-color $c1 --strip-top-color $c2 --strip-middle-color $c2 --strip-bottom-color $c2
	fi
done

Now if I could find a scriptable way to turn off leds on the K70 core rgb, it would be great.

-edit-
seems i can leverage openlinkhub for that.

Last edited by kokoko3k (2025-03-06 12:35:41)


Help me to improve ssh-rdp !
Retroarch User? Try my koko-aio shader !

Offline

#3815 2025-03-06 16:41:06

seth
Member
Registered: 2012-09-03
Posts: 64,568

Re: Post your handy self made command line utilities

Since there seem to be ongoing hurdles with receiving distro news, this prints the ones for the current and previous month in a console-friendly way.

#!/bin/sh
export LC_ALL=C
THIS_MONTH="$(date +'%b %Y')"
LAST_MONTH="$(date +'%b %Y' -d -1month)"
curl -sL 'https://archlinux.org/feeds/news/' | tr '\n' ' ' | \
xmlstarlet sel -T -t -m "//rss/channel/item[contains(string(pubDate), '$LAST_MONTH') or contains(string(pubDate), '$THIS_MONTH')]" \
                        -o $'\n\e[33m' -v pubDate -o $'\n\e[0;1m' -v title -o $'\e[0m' -v description -o $'\n\n────────────────\n' |\
sed $'s/<p>/\\n\\n/g; s/<h.>/\\n\\n\e[1;34m/g; s%</\(h.\|b\|em\|strong\|pre\|code\)>%\e[0m%g;
    s/<li>/\\n· /g;
    s/<\(code\|pre\)>/\e[1;32m/g;
    s/<\(strong\|em\)>/\e[1m/g;
    s/&gt;/>/g; s/&lt;/</g; s/<[^>]*>//g' | fold -sw 100

Offline

#3816 2025-05-24 23:27:13

4ndr0666
Member
Registered: 2024-04-11
Posts: 4

Re: Post your handy self made command line utilities

How have you handled the concatenation of multiple video clips in your workflow?

Simple task right?! My goal was to develop a simple shell script ensuring lossless quality by leveraging FFmpeg's stream copy feature (`-c copy`). This approach would avoid re-encoding and preserves the original quality of the videos. However, this seemingly simple task has plagued me with a consistent problem that I cannot seem to remedy. The problem, of course, is the handling of video clips that vary in container, codec, size, resolution, fps, etc. I have been at this for more than a year with countless revisions and no satisfactory result. All suggestions and improvements are welcome! I will share the first iteration I began with to start:

Ffx_v1:

#!/bin/sh -e
# Author: 4ndr0666
# ================= // FFX version 1 //
## Description: Losslessly concatenate video files.
# --------------------------------------------

usage() {
    >&2 printf 'Usage: %s [files]\n' "${0##*/}"
    exit 1
}

[ "$1" ] || usage
for i in "$@" ; do
    [ -f "$i" ] || usage
done

tmp=input.txt
trap 'rm "$tmp" 2>/dev/null ||:' EXIT INT TERM
:>"$tmp"

for i in "$@" ; do
    printf 'file %s\n' "$i" >>"$tmp"
done

exec ffmpeg \
    -safe 0 \
    -f concat \
    -i "$tmp" \
    -c copy \
    output."${1##*.}"

Issues Impeding Goal:

  • All input videos must have the same codec, resolution, and frame rate to ensure seamless concatenation without re-encoding.

  • Transcoding is typically required to "normalize" videos with different codecs or formats before concatenation.



- 4ndr0666

Offline

#3817 2025-05-25 07:09:33

seth
Member
Registered: 2012-09-03
Posts: 64,568

Re: Post your handy self made command line utilities

he problem, of course, is the handling of video clips that vary in container, codec, size, resolution, fps, etc.

https://mkvtoolnix.download/doc/mkvmerg … le_linking
Actual concatenation isn't a thing here - you'll have to re-encode to align the sources no matter what.

Also with shells for sane adults you don't need any temporary file for the source list, https://trac.ffmpeg.org/wiki/Concatenat … einputfile

Offline

#3818 2025-05-26 00:29:03

4ndr0666
Member
Registered: 2024-04-11
Posts: 4

Re: Post your handy self made command line utilities

seth wrote:

he problem, of course, is the handling of video clips that vary in container, codec, size, resolution, fps, etc.

https://mkvtoolnix.download/doc/mkvmerg … le_linking
Actual concatenation isn't a thing here - you'll have to re-encode to align the sources no matter what.

Also with shells for sane adults you don't need any temporary file for the source list, https://trac.ffmpeg.org/wiki/Concatenat … einputfile

Badass! Thanks for sharing your knowledge! To follow up with an update, this is what I wound up with:

#!/bin/sh
# Author: 4ndr0666
set -euo

# ===================== // MERGE //
## Description: Merges video files using ffmpeg.
#               Handles temporary files securely with mktemp and trap.
#               Attempts stream copy before re-encoding.
#               Uses `qp 0` for lossless quality.
# --------------------------------------------------------

# === Dependencies ===
command -v ffmpeg >/dev/null 2>&1 || {
	echo "Error: ffmpeg not found in PATH." >&2
	exit 1
}
command -v realpath >/dev/null 2>&1 || {
	echo "Error: realpath command not found." >&2
	exit 1
}

# === Help ===
show_help() {
	echo "Usage: $0 [OPTIONS] file1.mp4 file2.mp4 [...]" >&2
	echo "Options:"
	echo "  -o FILE       Set output filename."
	echo "  -q            Use fast preset (crf=15) instead of lossless fallback."
	echo "  -h, --help    Show this help message and exit."
	echo "Default output filename: merged_$(date +%Y%m%d_%H%M%S).mp4" >&2
}

XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}"
MERGE_DIR="$XDG_CACHE_HOME/ffx"
mkdir -p "$MERGE_DIR"

TS=$(date +%Y%m%d_%H%M%S)
DEFAULT_OUT_NAME="merged_${TS}.mp4"
OUT_NAME="$DEFAULT_OUT_NAME"
FAST_MODE=0

# === Args ===
while [ $# -gt 0 ]; do
	case "$1" in
		-o)
			shift
			[ -z "${1:-}" ] && {
				echo "Error: -o requires a filename." >&2
				show_help
				exit 1
			}
			OUT_NAME="$1"
			shift
			;;
		-q)
			FAST_MODE=1
			shift
			;;
		-h | --help)
			show_help
			exit 0
			;;
		--)
			shift
			break
			;;
		-*)
			echo "Error: Unknown option '$1'" >&2
			show_help
			exit 1
			;;
		*) break ;;
	esac
done

[ "$#" -lt 2 ] && {
	echo "Error: At least two input files required." >&2
	show_help
	exit 1
}

[ -f "$OUT_NAME" ] && {
	echo "Error: '$OUT_NAME' exists. Refusing to overwrite." >&2
	exit 1
}

# === TMP & TRAP ===
LIST_FILE=$(mktemp "$MERGE_DIR/merge_list.XXXXXX.txt") || {
	echo "Error: Could not create list file." >&2
	exit 1
}
cleanup() { rm -f "$LIST_FILE"; }
trap cleanup EXIT INT TERM

for f in "$@"; do
	if [ -f "$f" ] && [ -r "$f" ]; then
		abs_path=$(realpath "$f") || {
			echo "Warning: Could not resolve '$f'." >&2
			continue
		}
		esc=$(printf "%s" "$abs_path" | sed "s/'/''/g")
		printf "file '%s'\n" "$esc" >>"$LIST_FILE" || {
			echo "Error writing to list." >&2
			exit 1
		}
	else
		echo "Warning: Skipping '$f'." >&2
	fi
done

[ ! -s "$LIST_FILE" ] && {
	echo "Error: No valid input files." >&2
	exit 1
}

# === FFMPEG stream copy ===
echo "Attempting stream copy to '$OUT_NAME'..." >&2
if ffmpeg -hide_banner -loglevel warning -f concat -safe 0 -i "$LIST_FILE" -c copy "$OUT_NAME"; then
	echo "✅ Stream copy successful: $OUT_NAME" >&2
	exit 0
else
	echo "⚠️  Stream copy failed. Re-encoding..." >&2
fi

# === FFMPEG Re-encode ===
if [ "$FAST_MODE" -eq 1 ]; then
	echo "Using fast preset with crf=15." >&2
	VOPTS="-c:v libx264 -crf 15 -preset fast"
else
	echo "Using lossless encode with qp=0." >&2
	VOPTS="-c:v libx264 -qp 0 -preset veryslow -pix_fmt yuv420p"
fi

if ffmpeg -hide_banner -loglevel warning -f concat -safe 0 -i "$LIST_FILE" $VOPTS -c:a flac "$OUT_NAME"; then
	echo "✅ Re-encoded merge complete: $OUT_NAME" >&2
	exit 0
else
	echo "❌ Re-encode failed." >&2
	exit 1
fi

Last edited by 4ndr0666 (2025-05-30 04:55:01)

Offline

#3819 2025-05-30 13:11:04

dbm
Member
From: localhost
Registered: 2025-05-21
Posts: 4
Website

Re: Post your handy self made command line utilities

Hardware check, that i use to preview whats working and what not, as post install. (Python Script)

### FAST HARDWARE CHECK-SCRIPT ###
### AUTOR: DBM ###
#!/usr/bin/env python3
import subprocess
import sys
import time
import os
import re
import platform
import json
import argparse
from datetime import datetime
import socket

# ANSI colors and styles
class Colors:
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    BLUE = '\033[94m'
    CYAN = '\033[96m'
    MAGENTA = '\033[95m'
    RESET = '\033[0m'
    BOLD = '\033[1m'
    UNDERLINE = '\033[4m'

# Symbols for status indicators
SYMBOLS = {
    "OK": "✓",
    "WARN": "⚠",
    "FAIL": "✗",
    "INFO": "ℹ"
}

def print_status(name, status, message="", indent=0):
    color = {
        "OK": Colors.GREEN,
        "WARN": Colors.YELLOW,
        "FAIL": Colors.RED,
        "INFO": Colors.CYAN
    }.get(status, Colors.RESET)
    
    indent_str = " " * indent
    print(f"{indent_str}{Colors.BOLD}{name:20}{Colors.RESET} [{color}{SYMBOLS.get(status, '?')}{Colors.RESET}] {color}{message}{Colors.RESET}")

def run_cmd(cmd, ignore_errors=False, timeout=10):
    try:
        output = subprocess.check_output(
            cmd, 
            shell=True, 
            stderr=subprocess.STDOUT, 
            text=True,
            timeout=timeout
        )
        return output.strip()
    except subprocess.CalledProcessError as e:
        if not ignore_errors:
            return f"Command failed: {e.output.strip()}"
        return ""
    except subprocess.TimeoutExpired:
        return "Command timed out"
    except Exception as e:
        return f"Error: {str(e)}"

def get_system_info():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}System Information{Colors.RESET}")
    
    # Basic system info
    distro = run_cmd("cat /etc/os-release | grep PRETTY_NAME | cut -d'=' -f2 | tr -d '\"'", ignore_errors=True)
    hostname = socket.gethostname()
    kernel = run_cmd("uname -r")
    arch = platform.machine()
    uptime = run_cmd("uptime -p")
    
    print_status("OS", "INFO", distro)
    print_status("Hostname", "INFO", hostname)
    print_status("Kernel", "INFO", f"{kernel} ({arch})")
    print_status("Uptime", "INFO", uptime)

def check_cpu():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}CPU Information{Colors.RESET}")
    
    # Basic CPU info
    cpuinfo = run_cmd("grep 'model name' /proc/cpuinfo | head -1")
    if cpuinfo:
        cpu_name = cpuinfo.split(':',1)[1].strip()
        print_status("Model", "OK", cpu_name)
    else:
        print_status("Model", "FAIL", "No CPU info detected")
    
    # CPU cores/threads
    cores = run_cmd("grep -c '^processor' /proc/cpuinfo")
    physical_cores = run_cmd("lscpu | grep 'Core(s) per socket' | awk '{print $4}'")
    sockets = run_cmd("lscpu | grep 'Socket(s)' | awk '{print $2}'")
    
    print_status("Cores/Threads", "OK", f"{cores} threads, {physical_cores} cores/socket, {sockets} sockets")
    
    # CPU flags
    flags = run_cmd("grep 'flags' /proc/cpuinfo | head -1 | cut -d':' -f2")
    if flags:
        important_flags = ["avx", "avx2", "aes", "sse4", "vmx", "svm"]
        present_flags = [f for f in important_flags if f in flags.lower()]
        print_status("Important Flags", "OK", ", ".join(present_flags) if present_flags else "None")
    
    # CPU vulnerabilities
    vulnerabilities = run_cmd("grep . /sys/devices/system/cpu/vulnerabilities/*")
    if vulnerabilities:
        vuln_lines = vulnerabilities.splitlines()
        for line in vuln_lines:
            vuln = line.split(":")[-1].strip()
            status = "WARN" if vuln not in ["Not affected", "Mitigation: PTI"] else "OK"
            print_status(os.path.basename(line.split(":")[0]), status, vuln)

def check_gpu():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}GPU Information{Colors.RESET}")
    
    # PCI devices
    lspci_out = run_cmd("lspci -nn | grep -i 'vga\\|3d\\|2d'")
    if not lspci_out:
        print_status("GPU", "FAIL", "No GPU detected")
        return
    
    gpus = lspci_out.splitlines()
    for i, gpu in enumerate(gpus, 1):
        print_status(f"GPU {i}", "OK", gpu.strip())
    
    # OpenGL info
    glx = run_cmd("glxinfo -B | grep -E 'OpenGL vendor|OpenGL renderer|OpenGL version'", ignore_errors=True)
    if glx:
        for line in glx.splitlines():
            key, val = line.split(":", 1)
            print_status(key.strip(), "OK", val.strip(), indent=4)
    else:
        print_status("OpenGL", "WARN", "No OpenGL info available", indent=4)
    
    # Vulkan info
    vulkan = run_cmd("vulkaninfo --summary 2>/dev/null | grep -A3 '^GPU id:'", ignore_errors=True)
    if vulkan:
        print_status("Vulkan", "OK", "Available", indent=4)
        for line in vulkan.splitlines():
            if ":" in line:
                key, val = line.split(":", 1)
                print_status(key.strip(), "OK", val.strip(), indent=8)
    else:
        print_status("Vulkan", "WARN", "Not available or no Vulkan devices", indent=4)

def check_memory():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Memory Information{Colors.RESET}")
    
    # RAM info
    meminfo = run_cmd("free -h")
    if meminfo:
        lines = meminfo.splitlines()
        if len(lines) >= 2:
            mem_line = lines[1].split()
            total, used, free, shared, buff_cache, available = mem_line[1:7]
            print_status("RAM", "OK", f"Total: {total}, Used: {used}, Free: {free}, Available: {available}")
    
    # Swap info
    swap = run_cmd("swapon --show=NAME,SIZE,USED,PRIO --noheadings")
    if swap:
        swap_lines = swap.splitlines()
        for i, line in enumerate(swap_lines, 1):
            parts = line.split()
            if len(parts) >= 3:
                print_status(f"Swap {i}", "OK", f"Size: {parts[1]}, Used: {parts[2]}")
    else:
        print_status("Swap", "WARN", "No swap configured")
    
    # DIMM info
    dimm = run_cmd("sudo dmidecode --type memory", ignore_errors=True)
    if dimm and "No such file or directory" not in dimm:
        dimm_count = dimm.count("Memory Device")
        speed = re.search(r"Speed: (\d+ MHz)", dimm)
        max_speed = re.search(r"Maximum Speed: (\d+ MHz)", dimm)
        
        print_status("DIMMs", "OK", f"{dimm_count} memory devices detected")
        if speed:
            print_status("Speed", "OK", speed.group(1), indent=4)
        if max_speed:
            print_status("Max Speed", "OK", max_speed.group(1), indent=4)
    else:
        print_status("DIMM Info", "WARN", "No DIMM info (try as root)")

def check_disks():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Storage Information{Colors.RESET}")
    
    # Disk devices
    disks = run_cmd("lsblk -d -o NAME,MODEL,SIZE,TYPE,ROTA,RO | grep disk")
    if not disks:
        print_status("Disks", "FAIL", "No disks detected")
        return
    
    disk_list = disks.splitlines()
    for disk in disk_list:
        parts = disk.split()
        name = parts[0]
        model = " ".join(parts[1:-3]) if len(parts) > 3 else "Unknown"
        size = parts[-3]
        is_ssd = parts[-2] == "0"
        ro = parts[-1] == "1"
        
        disk_type = "SSD" if is_ssd else "HDD"
        status = "WARN" if ro else "OK"
        print_status(f"Disk {name}", status, f"{model} ({size}, {disk_type})")
        
        # SMART status
        smart = run_cmd(f"sudo smartctl -H /dev/{name}", ignore_errors=True)
        if "PASSED" in smart:
            print_status("SMART", "OK", "Healthy", indent=4)
        elif "FAILED" in smart:
            print_status("SMART", "FAIL", "Failed", indent=4)
        else:
            print_status("SMART", "WARN", "Unknown", indent=4)
    
    # Filesystems
    mounts = run_cmd("df -hT | grep -v tmpfs | grep -v udev")
    if mounts:
        print(f"\n{Colors.BOLD}Filesystems:{Colors.RESET}")
        print(mounts)
    
    # Test disk speed (only on / or /home)
    test_disk_speed()

def test_disk_speed():
    test_locations = ["/", "/home", "/tmp"]
    valid_locations = [loc for loc in test_locations if os.path.exists(loc)]
    
    if not valid_locations:
        print_status("Disk Speed", "WARN", "No suitable location for testing")
        return
    
    test_dir = valid_locations[0]
    test_file = os.path.join(test_dir, "hwtest_disk_testfile.bin")
    size_mb = 100
    
    try:
        # Write speed test
        start_write = time.time()
        with open(test_file, "wb") as f:
            for _ in range(size_mb):
                f.write(os.urandom(1024 * 1024))
                f.flush()
                os.fsync(f.fileno())
        end_write = time.time()
        write_time = end_write - start_write
        write_speed = size_mb / write_time if write_time > 0 else 0
        
        # Read speed test
        start_read = time.time()
        with open(test_file, "rb") as f:
            while f.read(1024 * 1024):
                pass
        end_read = time.time()
        read_time = end_read - start_read
        read_speed = size_mb / read_time if read_time > 0 else 0
        
        os.remove(test_file)
        
        status = "OK" if read_speed > 200 and write_speed > 100 else ("WARN" if read_speed > 50 else "FAIL")
        print_status("Disk Speed", status, 
                    f"Read: {read_speed:.1f} MB/s, Write: {write_speed:.1f} MB/s (tested on {test_dir})")
    except Exception as e:
        print_status("Disk Speed", "FAIL", f"Error: {str(e)}")
        if os.path.exists(test_file):
            try:
                os.remove(test_file)
            except:
                pass

def check_network():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Network Information{Colors.RESET}")
    
    # Interfaces
    interfaces = run_cmd("ip -brief a")
    if interfaces:
        print(f"{Colors.BOLD}Network Interfaces:{Colors.RESET}")
        for line in interfaces.splitlines():
            parts = line.split()
            if len(parts) >= 3:
                print(f"  {parts[0]:10} {parts[1]:8} {parts[2]}")
    else:
        print_status("Interfaces", "FAIL", "No network interfaces detected")
    
    # Connectivity
    ping = run_cmd("ping -c 3 8.8.8.8", ignore_errors=True)
    if "3 packets transmitted, 3 received" in ping:
        print_status("Connectivity", "OK", "Internet reachable")
    else:
        print_status("Connectivity", "WARN", "Internet not reachable")
    
    # DNS
    try:
        socket.gethostbyname("google.com")
        print_status("DNS", "OK", "DNS resolution working")
    except:
        print_status("DNS", "FAIL", "DNS resolution failed")
    
    # Speed test (optional)
    if "--full" in sys.argv:
        print_status("Speed Test", "INFO", "Running... (this may take a while)")
        speed = run_cmd("curl -s https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py | python3 -", ignore_errors=True)
        if speed:
            print(f"\n{Colors.BOLD}Speed Test Results:{Colors.RESET}")
            print(speed)

def check_usb():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}USB Devices{Colors.RESET}")
    
    out = run_cmd("lsusb -v", ignore_errors=True) or run_cmd("lsusb")
    if out:
        devices = out.split("\n\n")
        print_status("USB Devices", "OK", f"{len(devices)} devices detected")
        
        for i, dev in enumerate(devices[:5], 1):  # Limit to first 5 devices for brevity
            lines = dev.splitlines()
            if not lines:
                continue
                
            # First line is usually the device info
            bus_device = lines[0].strip()
            print_status(f"Device {i}", "INFO", bus_device, indent=4)
            
            # Try to extract more info
            for line in lines[1:]:
                if "idVendor" in line or "idProduct" in line or "iProduct" in line:
                    print(f"{' ' * 8}{line.strip()}")
    else:
        print_status("USB Devices", "FAIL", "No USB devices detected")

def check_temperatures():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Temperatures{Colors.RESET}")
    
    temps = run_cmd("sensors -j", ignore_errors=True) or run_cmd("sensors", ignore_errors=True)
    if not temps:
        print_status("Sensors", "WARN", "No temperature sensors found")
        return
    
    try:
        # Try to parse as JSON first
        data = json.loads(temps)
        for chip, values in data.items():
            if not isinstance(values, dict):
                continue
                
            print_status(chip, "INFO", "", indent=0)
            for key, val in values.items():
                if isinstance(val, dict):
                    for subkey, subval in val.items():
                        if "input" in subkey and isinstance(subval, (int, float)):
                            print_status(f"{key} {subkey}", "OK", f"{subval:.1f}°C", indent=4)
                elif isinstance(val, (int, float)) and "temp" in key.lower():
                    print_status(key, "OK", f"{val:.1f}°C", indent=4)
    except json.JSONDecodeError:
        # Fall back to text parsing
        lines = temps.splitlines()
        for line in lines:
            if "°C" in line or "°F" in line:
                print(line.strip())

def check_power():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Power Information{Colors.RESET}")
    
    # Battery info
    battery = run_cmd("upower -e | grep battery", ignore_errors=True)
    if battery:
        battery = battery.strip()
        info = run_cmd(f"upower -i {battery}")
        if info:
            # Extract relevant info
            state = re.search(r'state:\s*(\w+)', info, re.I)
            percentage = re.search(r'percentage:\s*(\d+%)', info, re.I)
            time_to = re.search(r'time to (empty|full):\s*([\d\.]+ \w+)', info, re.I)
            
            if state and percentage:
                status = "OK" if state.group(1).lower() != "discharging" else "WARN"
                print_status("Battery", status, 
                           f"{percentage.group(1)}, {state.group(1).capitalize()}" + 
                           (f", {time_to.group(2)} to {time_to.group(1)}" if time_to else ""))
    else:
        print_status("Battery", "INFO", "No battery detected (desktop system?)")
    
    # Power profile
    profile = run_cmd("cat /sys/firmware/acpi/platform_profile", ignore_errors=True)
    if profile:
        print_status("Power Profile", "OK", profile.strip().capitalize())
    
    # CPU frequencies
    freqs = run_cmd("cpupower frequency-info | grep 'current CPU frequency'", ignore_errors=True)
    if freqs:
        freq = freqs.split(":")[1].strip()
        print_status("CPU Freq", "OK", freq)

def check_hardware_acceleration():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Hardware Acceleration{Colors.RESET}")
    
    # VAAPI
    vaapi = run_cmd("vainfo | grep -i 'vainfo: VA-API version'", ignore_errors=True)
    if vaapi:
        print_status("VA-API", "OK", "Supported")
    else:
        print_status("VA-API", "WARN", "Not available")
    
    # VDPAU
    vdpau = run_cmd("vdpauinfo | grep -i 'Information string'", ignore_errors=True)
    if vdpau:
        print_status("VDPAU", "OK", "Supported")
    else:
        print_status("VDPAU", "WARN", "Not available")
    
    # NVDEC/NVENC
    nvdec = run_cmd("nvidia-smi -q | grep 'NVDEC'", ignore_errors=True)
    if nvdec:
        print_status("NVDEC", "OK", "Supported")
    
    nvenc = run_cmd("nvidia-smi -q | grep 'NVENC'", ignore_errors=True)
    if nvenc:
        print_status("NVENC", "OK", "Supported")

def check_security():
    print(f"\n{Colors.BOLD}{Colors.UNDERLINE}Security Status{Colors.RESET}")
    
    # Secure Boot
    secure_boot = run_cmd("bootctl status | grep 'Secure Boot'", ignore_errors=True)
    if secure_boot:
        status = "OK" if "enabled" in secure_boot.lower() else "WARN"
        print_status("Secure Boot", status, secure_boot.split(":")[1].strip())
    else:
        print_status("Secure Boot", "WARN", "Status unknown")
    
    # Firewall
    firewall = run_cmd("sudo firewall-cmd --state 2>/dev/null", ignore_errors=True) or \
               run_cmd("sudo ufw status | grep 'Status'", ignore_errors=True)
    if firewall:
        status = "OK" if "running" in firewall.lower() or "active" in firewall.lower() else "WARN"
        print_status("Firewall", status, firewall.strip())
    else:
        print_status("Firewall", "WARN", "No active firewall detected")
    
    # SELinux/AppArmor
    selinux = run_cmd("getenforce 2>/dev/null", ignore_errors=True)
    if selinux:
        status = "OK" if "Enforcing" in selinux else "WARN"
        print_status("SELinux", status, selinux.strip())
    
    apparmor = run_cmd("aa-status 2>/dev/null | grep 'apparmor module is loaded'", ignore_errors=True)
    if apparmor:
        print_status("AppArmor", "OK", "Loaded")

def generate_report():
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    filename = f"hardware_report_{timestamp}.txt"
    
    print(f"\n{Colors.BOLD}Generating report to {filename}...{Colors.RESET}")
    
    # Redirect stdout to file
    original_stdout = sys.stdout
    with open(filename, "w") as f:
        sys.stdout = f
        main(quiet=True)
        sys.stdout = original_stdout
    
    print_status("Report", "OK", f"Saved to {filename}")

def main(quiet=False):
    if not quiet:
        print(f"\n{Colors.BOLD}{Colors.BLUE}=== Comprehensive Hardware Diagnostic Tool ==={Colors.RESET}")
        print(f"{Colors.BOLD}Running on {platform.system()} {platform.release()}{Colors.RESET}\n")
    
    get_system_info()
    check_cpu()
    check_gpu()
    check_memory()
    check_disks()
    check_network()
    check_usb()
    check_temperatures()
    check_power()
    check_hardware_acceleration()
    check_security()
    
    if not quiet:
        print(f"\n{Colors.BOLD}{Colors.GREEN}Diagnostic complete!{Colors.RESET}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Comprehensive hardware diagnostic tool")
    parser.add_argument("--full", action="store_true", help="Run extended tests (including network speed test)")
    parser.add_argument("--report", action="store_true", help="Generate a detailed report file")
    args = parser.parse_args()
    
    if args.report:
        generate_report()
    else:
        main()

Offline

Board footer

Powered by FluxBB

OSZAR »