Skip to content

Instantly share code, notes, and snippets.

@colorwebdesigner
Last active February 24, 2022 22:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colorwebdesigner/b8cd5225741066c502c2f9435f7ae47a to your computer and use it in GitHub Desktop.
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.
#!/bin/bash
# pcinfo.sh
# ---
# Collect general inormation about PC hardware,
# generate nice report and print to cli or file
# --------------------------------------------------------
# author : ip@spiderlab.ru
# 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="ip@spiderlab.ru"
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 "$@"
@colorwebdesigner
Copy link
Author

colorwebdesigner commented Feb 24, 2022

WHAT FOR

user@pc:~/.local/bin/$ sudo ./pcinfo -h

 Script collects data from different
 sources about the PC on which it is
 running and it's hardware, combine
 data in a single table, print this
 table to console or write to file.

 Usage 1 (default): ./pcinfo
 print result to console

 Usage 2: ./pcinfo /path/to/file
 verbose and print result to file.
 permissions will be set to 600

 -h, --help
 print this help message

Script is very useful

  1. With conky app. Just save the result of the script to a file and parse this file from 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 reduce conky access to disk and resources, allows you to get useful data* without using superuser privileges and make the conky.conf file itself less overloaded.
  2. With liveUSB to get quick and easy report about PC hardware. If you are in the repair and sales of computers, it will help you create a beautiful and quick description of the system for placement in sales ads. Just copy/paste.

* 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):

# -------------------------------------
# Platform info
# -------------------------------------
pc-sku         :  ASUS-Ultrabook
pc-vendor      :  ASUSTeK COMPUTER INC.
pc-model       :  UX32LN
pc-version     :  1.0       
pc-serial      :  XXXXXXXXXXXXXX     
pc-uuid        :  f4c2ebde-XXXX-XXXX-XXXX-XXXX

# -------------------------------------
# Bios info
# -------------------------------------
b-vendor       :  American Megatrends Inc.
b-version      :  UX32LN.204
b-revision     :  4.6
b-release      :  04/18/2019
b-rom-size     :  6144 kB
b-uefi         :  UEFI is supported

# -------------------------------------
# Windows info
# -------------------------------------
win-key        :  XXXXX-XXXXX-XXXXX-XXXXX-XXXXX

# -------------------------------------
# Processor info
# -------------------------------------
cpu-type       :  Central Processor
cpu-vendor     :  GenuineIntel
cpu-family     :  Core i7
cpu-arch       :  x86_64
cpu-modes      :  32-bit,64-bit
cpu-sname      :  Intel Core i7-4500U
cpu-name       :  Intel(R) Core(TM) i7-4500U @ 1.80GHz
cpu-cores      :  2
cpu-threads    :  4
cpu-voltage    :  1.2 V
cpu-speed      :  1800 MHz
cpu-max-speed  :  3000 MHz
cpu-min-speed  :  800 MHz
cpu-virt       :  VT-x
cpu-flags      :  fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm cpuid_fault epb invpcid_single pti ssbd ibrs ibpb stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid xsaveopt dtherm ida arat pln pts md_clear flush_l1d
cpu-bugs       :  cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds

# ------------------------------------
# Graphics info
# ------------------------------------
# [ GPU 0 ]
gpu-name       : Intel
gpu-vendor     : Intel Corporation
gpu-prod       : Haswell-ULT Integrated Graphics Controller
gpu-desc       : VGA compatible controller

# [ GPU 1 ]
gpu-name       : NVIDIA
gpu-vendor     : NVIDIA Corporation
gpu-prod       : GM108M [GeForce 840M]
gpu-desc       : 3D controller

# -------------------------------------
# Memory info
# -------------------------------------
# [ BANK 0 (soldered) ]
mem-vendor     :  Hynix/Hyundai
mem-form       :  SODIMM
mem-type       :  DDR3
mem-size       :  4096 MB
mem-speed      :  1600 MT/s
mem-serial     :  00000000

# [ BANK 2 ]
mem-vendor     :  Kingston
mem-form       :  SODIMM
mem-type       :  DDR3
mem-size       :  8192 MB
mem-speed      :  1600 MT/s
mem-serial     :  XXXXXXXX

# -------------------------------------
# Disks info
# -------------------------------------
# [ DISK 0 ]
d-type         :  Solid State Device
d-model        :  CT500MX500SSD1N 
d-form         :  2.5 inch
d-fsize        :  500107 MBytes (500 GB)
d-size         :  476940 MBytes
d-serial       :  XXXXXXXXXXXX 
d-firmware     :  M3CR022 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment