Last active
February 24, 2022 22:52
-
-
Save colorwebdesigner/b8cd5225741066c502c2f9435f7ae47a to your computer and use it in GitHub Desktop.
Tested on GNU Linux Debian system. Collect general inormation about PC hardware, generate nice report and print to cli or file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# pcinfo.sh | |
# --- | |
# Collect general inormation about PC hardware, | |
# generate nice report and print to cli or file | |
# -------------------------------------------------------- | |
# author : [email protected] | |
# license : GNU | |
# date : 17.02.2022 | |
# src : https://gist.github.com/colorwebdesigner/b8cd5225741066c502c2f9435f7ae47a | |
# -------------------------------------------------------- | |
# Arguments: | |
# [ /path/to/file ] - | |
# [ -h, --help ] - | |
# -------------------------------------------------------- | |
# Dependencies: | |
# dmidecode, hexdump, lscpu, lshw, lsblk, hdparm | |
# ======================================================== | |
# DEBUGGER On/Off | |
# set -x | |
# SUDO checker | |
# =========================== | |
[[ $EUID -ne 0 ]] && echo "This script must be run as root." && exit 1 | |
# DEFAULT VARIABLES | |
# =========================== | |
DEVNAME="[email protected]" | |
COLWIDTH=14 | |
DECORWIDTH=36 | |
# MAIN FUNCTION | |
# =========================== | |
pcinfo () { | |
# Local variables | |
# ------------------------- | |
local status name path path_status msg result | |
local user="$(logname)" | |
# USAGE | |
# ========================= | |
usage () { | |
local man="\n Script collects data from different\n sources about the PC on which it is\n running and it's hardware, combine\n data in a single table, print this\n table to console or write to file.\n\n ${c[c]}Usage 1 (default)${c[n]}: $0\n ${c[y]}print result to console${c[n]}\n\n ${c[c]}Usage 2${c[n]}: $0 /path/to/file\n ${c[y]}verbose and print result to file.${c[n]}\n ${c[y]}permissions will be set to 600.${c[n]}\n\n ${c[c]}-h, --help${c[n]}\n ${c[y]}print this help message${c[n]}\n\n" | |
if [[ -n "$1" ]]; then | |
local err="${c[r]}[ error ]${c[n]} ${0} --> ${c[y]}%s${c[n]} ${c[r]}%s${c[n]}\n" | |
printf "${err}" "${1}" "${2}" >&2 | |
else | |
printf "$man" | |
fi | |
return 0 | |
} | |
# Catch ARGS | |
# ========================= | |
function checkArgs() { | |
function checkPath() { | |
path="${1%/*}"; | |
ls "${path}" &>/dev/null; path_status=$? | |
[[ ${path_status} -eq 0 ]] && name="$(basename ${1})" | |
return ${path_status} | |
} | |
while [ $# -gt 0 ]; do | |
local arg="${1}" | |
case "${arg}" in | |
-* ) [[ -n "${arg}" && "${arg}" != "-"* ]] && arg="${arg}" | |
case "${arg}" in | |
--help|-h ) usage && exit 0;; | |
* ) usage "unknown argument" "${arg}" && exit 1;; | |
esac;; | |
* ) checkPath "${arg}" && break || \ | |
usage "wrong path" "${path}"; exit 1;; | |
esac | |
shift | |
done | |
} | |
# TUI | |
# ========================= | |
function hr() { | |
[[ -n $1 ]] && printf "%s " "${1}" | |
printf "%${DECORWIDTH}s" | tr " " "-" | |
return 0 | |
} | |
function banner () { | |
local banner=" $(hr)\n ${1} by ${DEVNAME}\n $(hr)\n" | |
printf "${banner}" | |
return 0 | |
} | |
function stepTitle() { | |
printf " --> ${c[y]}$(fr "${1}" 24)${c[n]}" | |
tput sc && printf "\n" | |
return 0 | |
} | |
function stepResult () { | |
local report=${2:-done} | |
if [ $1 -eq 0 ]; then | |
tput rc && tput ed && printf "${c[g]}${report}${c[n]}\n" | |
elif [ $1 -eq 1 ]; then | |
tput rc && printf "${c[r]}error ${1}${c[n]}\n" && exit 1 | |
else | |
tput rc && tput ed && tput cnorm | |
printf "${c[y]}${report}${c[n]}\n" && exit 1 | |
fi | |
unset status | |
return 0 | |
} | |
# Dependencies | |
# ========================= | |
# Default table first row formatting | |
# All device items names must be less | |
# then COLWIDTH. | |
# ------------------------------ | |
function fr() { | |
[[ -n ${2} ]] && w=${2} || w=${COLWIDTH} | |
local spaces=$((${w}-${#1})) | |
printf "%s" "${1}" && printf "%${spaces}s : " | |
return 0 | |
} | |
# Collect PC info function | |
# ------------------------------ | |
function checkPC() { | |
unset msg && tput civis | |
local title='# Platform info' | |
local vendor model version serial uuid sku | |
function getData() { | |
dmidecode | grep -A8 '^System Information' | \ | |
grep "${1}" | cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
vendor="$(getData 'Manufacturer')" | |
model="$(getData 'Product Name')" | |
version="$(getData 'Version')" | |
serial="$(getData 'Serial Number')" | |
uuid="$(getData 'UUID')" | |
sku="$(getData 'SKU Number')" | |
result+="$(hr '#')\n${title}\n$(hr '#')\n$(fr "pc-sku")${sku}\n$(fr "pc-vendor")${vendor}\n$(fr "pc-model")${model}\n$(fr "pc-version")${version}\n$(fr "pc-serial")${serial}\n$(fr "pc-uuid")${uuid}\n\n" | |
tput cnorm && unset title vendor model version serial uuid sku | |
return 0 | |
} | |
# Collect bios info function | |
# ------------------------------ | |
function checkBios() { | |
unset msg && tput civis | |
local title="# Bios info" | |
local vendor version revision release rom uefi | |
function getData() { | |
dmidecode --type 0 | grep "${1}" | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
vendor="$(getData 'Vendor')" | |
version="$(getData 'Version')" | |
revision="$(getData 'BIOS Revision')" | |
release="$(getData 'Release Date')" | |
rom="$(getData 'ROM Size')" | |
[[ $(getData 'UEFI' &>/dev/null) -eq 0 ]] && \ | |
uefi="$(getData 'UEFI' | tr '\t' ' ' | sed 's/^ *//')" || \ | |
uefi='Not supported' | |
result+="$(hr '#')\n${title}\n$(hr '#')\n$(fr "b-vendor")${vendor}\n$(fr "b-version")${version}\n$(fr "b-revision")${revision}\n$(fr "b-release")${release}\n$(fr "b-rom-size")${rom}\n$(fr "b-uefi")${uefi}\n\n" | |
tput cnorm && unset vendor version revision release rom uefi | |
return 0 | |
} | |
# Collect winkey info function | |
# ------------------------------ | |
function checkWin() { | |
unset msg && tput civis | |
local title="# Windows info" | |
function extractWinKey() { | |
local src='/sys/firmware/acpi/tables/MSDM' | |
if [[ -f ${src} ]]; then | |
printf "%s" "$(hexdump -s 56 -e '/29 "%s\n"' ${src})" | |
# Other methods: | |
# strings /sys/firmware/acpi/tables/MSDM | tail -n 1 | |
# cat /sys/firmware/acpi/tables/MSDM | strings | tail -n 1 | |
# sudo acpidump | |
# hd /sys/firmware/acpi/tables/MSDM | |
else | |
printf "%s" "${src} not found" | |
fi | |
} | |
result+="$(hr '#')\n${title}\n$(hr '#')\n$(fr "win-key")$(extractWinKey)\n\n" | |
tput cnorm && unset title src | |
return 0 | |
} | |
# Collect processor info function | |
# ------------------------------ | |
function checkCPU() { | |
unset msg && tput civis | |
local title="# Processor info" | |
local src='/proc/cpuinfo' | |
local type vendor family arch modes name sname cores threads volt speed maxs mins virt flags bugs | |
cat ${src} &>/dev/null | |
[[ $? -ne 0 ]] && usage "can not read" "${src}" && exit 1 | |
# /proc/cpuinfo source | |
function getData1() { | |
cat "${src}" | grep "${1}" -m1 | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
# dmidecode source | |
function getData2() { | |
dmidecode --type 4 | grep "${1}" | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
# lscpu source | |
function getData3() { | |
lscpu | grep "${1}" | cut -d':' -f2 | \ | |
tr '\t' ' ' | sed 's/^ *//' | |
return $? | |
} | |
type="$(getData2 'Type:')" | |
vendor="$(getData3 'Vendor ID')" | |
family="$(getData2 'Family:')" | |
arch="$(getData3 'Architecture')" | |
modes="$(getData3 'CPU op-mode')" | |
name="$(getData1 'model name')" | |
sname="$(getData1 'model name' | cut -d' ' -f1,2,3 | sed -e 's/(R)//; s/(TM)//')" | |
cores="$(getData1 'cpu cores')" | |
threads="$(getData1 'siblings')" | |
volt="$(getData2 'Voltage:')" | |
speed="$(getData2 'Current Speed:')" | |
maxs="$(getData3 'CPU max MHz' | cut -d',' -f1)" | |
mins="$(getData3 'CPU min MHz' | cut -d',' -f1)" | |
virt="$(getData3 'Virtualization')" | |
flags="$(getData1 'flags')" | |
bugs="$(getData1 'bugs')" | |
result+="$(hr '#')\n${title}\n$(hr '#')\n$(fr "cpu-type")${type}\n$(fr "cpu-vendor")${vendor}\n$(fr "cpu-family")${family}\n$(fr "cpu-arch")${arch}\n$(fr "cpu-modes")${modes}\n$(fr "cpu-sname")${sname}\n$(fr "cpu-name")${name}\n$(fr "cpu-cores")${cores}\n$(fr "cpu-threads")${threads}\n$(fr "cpu-voltage")${volt}\n$(fr "cpu-speed")${speed}\n$(fr "cpu-max-speed")${maxs} MHz\n$(fr "cpu-min-speed")${mins} MHz\n$(fr "cpu-virt")${virt}\n$(fr "cpu-flags")${flags}\n$(fr "cpu-bugs")${bugs}\n\n" | |
tput cnorm && unset title src type vendor family arch modes name sname cores threads volt speed maxs mins virt flags bugs | |
return 0 | |
} | |
# Collect graphics info function | |
# ------------------------------ | |
function checkGPU() { | |
unset msg && tput civis | |
local title="# Graphics info" | |
local vendor product descr | |
IFS_BAK=$IFS && IFS='|' | |
local str=( $(lshw -C display 2>/dev/null | grep -A4 'display' | sed 's/--/|/') ) | |
IFS=$IFS_BAK | |
function formatRes() { | |
local name vendor product desc | |
local str="${1}" | |
function getData() { | |
printf "%s" "${str}" | grep "${1}" | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
name="$(getData 'vendor:' | cut -d' ' -f1)" | |
vendor="$(getData 'vendor:')" | |
product="$(getData 'product:')" | |
desc="$(getData 'description:')" | |
result+="# [ GPU ${2} ]\n$(fr "gpu-name")${name}\n$(fr "gpu-vendor")${vendor}\n$(fr "gpu-prod")${product}\n$(fr "gpu-desc")${desc}\n\n" | |
unset name vendor product desc str | |
return 0 | |
} | |
result+="$(hr '#')\n${title}\n$(hr '#')\n" | |
for i in "${!str[@]}"; do | |
formatRes "${str[$i]}" "${i}" | |
done | |
tput cnorm && unset title pci vendor model version | |
return 0 | |
} | |
# Collect memory info function | |
# ------------------------------ | |
function checkMEM() { | |
unset msg && tput civis | |
local title="# Memory info" | |
local handleArr=($(dmidecode --type 17 | grep '^Handle' | cut -d' ' -f2 | sed 's/,//')) | |
function filterRes() { | |
local filter='No Module Installed' | |
dmidecode -H "${1}" | grep "${filter}" &>/dev/null | |
echo $? | |
} | |
function formatRes() { | |
local bank vendor size form type speed serial | |
local str="${1}" | |
function getData() { | |
dmidecode -H "${str}" | grep "${1}" | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
bank="$(getData 'Bank Locator')" | |
vendor="$(getData 'Manufacturer')" | |
size="$(getData 'Size:')" | |
form="$(getData 'Form Factor')" | |
type="$(getData 'Type:')" | |
speed="$(getData 'Memory Speed:')" | |
serial="$(getData 'Serial Number')" | |
[[ "${serial// /}" =~ ^[0]*$ ]] && bank="${bank} (soldered)" | |
result+="# [${bank} ]\n$(fr "mem-vendor")${vendor}\n$(fr "mem-form")${form}\n$(fr "mem-type")${type}\n$(fr "mem-size")${size}\n$(fr "mem-speed")${speed}\n$(fr "mem-serial")${serial}\n\n" | |
unset bank vendor size form type speed serial str | |
return 0 | |
} | |
result+="$(hr '#')\n${title}\n$(hr '#')\n" | |
for handle in ${handleArr[*]}; do | |
case $(filterRes ${handle}) in | |
1 ) formatRes ${handle};; | |
* ) continue;; | |
esac | |
done | |
tput cnorm && unset title handleArr | |
return 0 | |
} | |
# Collect disks info function | |
# ------------------------------ | |
function checkHDD() { | |
unset msg && tput civis | |
local title="# Disks info" | |
local diskArr=( $(lsblk -dnp -e 7 -o NAME) ) | |
function filterSATA() { | |
local stat=0 | |
[[ "$(lsblk -dnp -o TRAN ${1})" = 'sata' ]] && stat=1 | |
[[ $(lsblk -dnp -o HOTPLUG ${1}) -eq 0 ]] && stat=2 | |
[[ $(lsblk -dnp -o RM ${1}) -eq 0 ]] && stat=3 | |
echo ${stat} && return ${stat} | |
} | |
function formatRes() { | |
local model serial fw dsize size form type | |
local str="${1}" | |
function getData() { | |
hdparm -I "${str}" | grep "${1}" | \ | |
cut -d':' -f2 | sed 's/^ *//' | |
return $? | |
} | |
type="$(getData 'Nominal Media')" | |
model="$(getData 'Model Number' )" | |
serial="$(getData 'Serial Number')" | |
fw="$(getData 'Firmware Revision')" | |
dsize="$(getData 'M = 1000')" | |
size="$(getData 'M = 1024')" | |
form="$(getData 'Form Factor')" | |
result+="# [ DISK ${2} ]\n$(fr "d-type")${type}\n$(fr "d-model")${model}\n$(fr "d-form")${form}\n$(fr "d-fsize")${dsize}\n$(fr "d-size")${size}\n$(fr "d-serial")${serial}\n$(fr "d-firmware")${fw}\n\n" | |
unset model serial fw dsize size form type str | |
return 0 | |
} | |
result+="$(hr '#')\n${title}\n$(hr '#')\n" | |
[[ -z "${diskArr}" ]] && result+="No disks found\n\n" | |
for i in ${!diskArr[@]}; do | |
case $(filterSATA ${diskArr[$i]}) in | |
3 ) formatRes ${diskArr[$i]} ${i};; | |
* ) continue;; | |
esac | |
done | |
tput cnorm && unset title diskArr | |
return 0 | |
} | |
# DO THE JOB | |
# ========================= | |
checkArgs "$@" | |
banner $FUNCNAME | |
# ------------------------- | |
# [1] Step - platform info | |
# ------------------------- | |
stepTitle "Collect platform info" | |
checkPC; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [2] Step - bios info | |
# ------------------------- | |
stepTitle "Collect bios info" | |
checkBios; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [3] Step - win info | |
# ------------------------- | |
stepTitle "Collect windows info" | |
checkWin; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [4] Step - CPU info | |
# ------------------------- | |
stepTitle "Collect processor info" | |
checkCPU; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [5] Step - GPU info | |
# ------------------------- | |
stepTitle "Collect graphics info" | |
checkGPU; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [6] Step - MEM info | |
# ------------------------- | |
stepTitle "Collect memory info" | |
checkMEM; status=$? | |
stepResult $status "$msg" | |
# ------------------------- | |
# [7] Step - HDD/SSD info | |
# ------------------------- | |
stepTitle "Collect disks info" | |
checkHDD; status=$? | |
stepResult $status "$msg" | |
# FINISH JOB | |
# ------------------------- | |
printf " $(hr)\n --> ${c[c]}result${c[n]} --> %s\n\n" "${path}/${name}" | |
case $path_status in | |
0 ) printf -- "${result}" > "${path}/${name}" | |
chown ${user}:${user} "${path}/${name}" | |
chmod 600 "${path}/${name}" | |
;; | |
* ) printf -- "${result}" | |
;; | |
esac | |
# Exit function | |
unset status name path path_status msg result | |
exit 0 | |
} | |
# Declare array with color values to colorise output | |
declare -A c=( ['c']='\e[36m' ['y']='\e[33m' ['r']='\e[31m' ['g']='\e[32m' ['n']='\e[0m' ) | |
# Sample usage | |
pcinfo "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
WHAT FOR
Script is very useful
conky.conf
. All hardware data is conveniently collected in one file. Which can be stored, for example, in RAM (tmpfs). You can also configure the script to automatically run at system startup so that the file is always up to date. This strategy allows you to reduceconky
access to disk and resources, allows you to get useful data* without using superuser privileges and make theconky.conf
file itself less overloaded.* non critical data, like RAM vendor and etc.
dmidecode
need sudo for example.HOW TO
Start script by root or sudo user. By default it will print information about your system to console.
Here is example output from my laptop ASUS UX32ln with dual boot system (Win 10 / Debian 10):