It is hard to remember bash shell scripting if you try to memorize it. With my horrible memory, that makes it even harder. It is confusing for me to make notes on topics like these too. So, I wrote a script to remember some basic bash scripting I use in general. This script is a monitoring tool that tells you about your CPU, CPU Temperature, Fan Speed, Memory, Swap, Ethernet, and WiFi speeds.
Monitoring CPU Usage
I did not want to use built-in tools like top, so I grabbed info from the proc directory:
1cpu_info=($(grep '^cpu ' /proc/stat)) # extra () converts it to array
2
3total_cpu_time=$((${cpu_info[1]} + ${cpu_info[2]} + ${cpu_info[3]} + ${cpu_info[4]}))
4idle_cpu_time=${cpu_info[4]}
5
6cpu_usage=$((100 - (100 * ($idle_cpu_time - $prev_idle_cpu_time) / ($total_cpu_time - $prev_total_cpu_time))))
7
8echo "CPU Usage: $cpu_usage %"
CPU info 1 contains CPU time for normal user mode processes, CPU info 2 contains CPU time for niced user mode processes (niced ones are processes with custom priorities), CPU info 3 is CPU time for kernel mode processes, and CPU info 4 is CPU time spent idle waiting.
CPU Temperature
I know I could've used lmsensors, but I had my own goals. I use a ThinkPad, so I grabbed data from the proc directory. Also, it checks whether the thinkpad_acpi kernel module is loaded or not to avoid errors.
1if lsmod | grep -q thinkpad_acpi; then
2 cpu_temp=$(cat /proc/acpi/ibm/thermal | grep 'temperatures' | awk '{print $2 " C"}')
3 echo "CPU Temp: $cpu_temp"
4else
5 echo "CPU Temp: NA, thinkpad_acpi module not loaded"
6fi
Fan Speed
This function monitors the CPU fan speed on a ThinkPad by reading data from the proc directory and checks if the thinkpad_acpi kernel module is loaded to avoid errors.
1if lsmod | grep -q thinkpad_acpi; then
2 fan_speed=$(cat /proc/acpi/ibm/fan | awk '/^speed/ {print $2 " RPM"}')
3 echo "Fan Speed: $fan_speed"
4else
5 echo "Fan Speed: NA, thinkpad_acpi module not loaded"
6fi
Monitoring RAM Usage
This function grabs memory usage information from the proc directory, calculates the total and available memory, and then converts it into MB for readability.
1mem_total=$(grep '^MemTotal:' /proc/meminfo | awk '{print $2}')
2mem_free=$(grep '^MemAvailable:' /proc/meminfo | awk '{print $2}')
3
4mem_total_mb=$((mem_total / 1024))
5mem_used_mb=$(( (mem_total - mem_free) / 1024 ))
6
7echo "Memory Used: $mem_used_mb MB / $mem_total_mb MB"
Monitoring Swap Usage
This function checks the swap usage by reading data from the proc directory, calculating the total and free swap memory, and then converting it into MB for readability.
1swap_total=$(grep '^SwapTotal:' /proc/meminfo | awk '{print $2}')
2swap_free=$(grep '^SwapFree:' /proc/meminfo | awk '{print $2}')
3
4swap_total_mb=$((swap_total / 1024))
5swap_used_mb=$(( (swap_total - swap_free) / 1024 ))
6
7echo "Swap Used: $swap_used_mb MB / $swap_total_mb MB"
Ethernet Speed
This function monitors Ethernet usage by checking the bit count from the sys directory. It calculates the difference between the current and previous bit counts to determine the speed, then converts it to Mbps or Kbps for readability.
1prev_rx_bytes_eth=$(cat /sys/class/net/enp2s0/statistics/rx_bytes)
2prev_tx_bytes_eth=$(cat /sys/class/net/enp2s0/statistics/tx_bytes)
3
4get_ethernet_stats() {
5 rx_bytes_eth=$(cat /sys/class/net/enp2s0/statistics/rx_bytes)
6 tx_bytes_eth=$(cat /sys/class/net/enp2s0/statistics/tx_bytes)
7
8 diff_rx=$((rx_bytes_eth - prev_rx_bytes_eth))
9 diff_tx=$((tx_bytes_eth - prev_tx_bytes_eth))
10
11 prev_rx_bytes_eth=$rx_bytes_eth
12 prev_tx_bytes_eth=$tx_bytes_eth
13
14 if [[ $diff_rx -gt 1000000 ]]; then
15 rx_speed=$((diff_rx / 1000000))
16 rx_unit="Mbps"
17 else
18 rx_speed=$((diff_rx / 1000))
19 rx_unit="Kbps"
20 fi
21
22 if [[ $diff_tx -gt 1000000 ]]; then
23 tx_speed=$((diff_tx / 1000000))
24 tx_unit="Mbps"
25 else
26 tx_speed=$((diff_tx / 1000))
27 tx_unit="Kbps"
28 fi
29
30 echo "Up: $tx_speed $tx_unit | Down: $rx_speed $rx_unit"
31}
WiFi Speed
This function monitors WiFi usage similarly to Ethernet, by checking the bit count from the sys directory. It calculates the difference between the current and previous bit counts to determine the speed, then converts it to Mbps or Kbps for readability.
1prev_rx_bytes_wifi=$(cat /sys/class/net/wlp3s0/statistics/rx_bytes)
2prev_tx_bytes_wifi=$(cat /sys/class/net/wlp3s0/statistics/tx_bytes)
3
4get_wifi_stats() {
5 rx_bytes_wifi=$(cat /sys/class/net/wlp3s0/statistics/rx_bytes)
6 tx_bytes_wifi=$(cat /sys/class/net/wlp3s0/statistics/tx_bytes)
7
8 diff_rx=$((rx_bytes_wifi - prev_rx_bytes_wifi))
9 diff_tx=$((tx_bytes_wifi - prev_tx_bytes_wifi))
10
11 prev_rx_bytes_wifi=$rx_bytes_wifi
12 prev_tx_bytes_wifi=$tx_bytes_wifi
13
14 if [[ $diff_rx -gt 1000000 ]]; then
15 rx_speed=$((diff_rx / 1000000))
16 rx_unit="Mbps"
17 else
18 rx_speed=$((diff_rx / 1000))
19 rx_unit="Kbps"
20 fi
21
22 if [[ $diff_tx -gt 1000000 ]]; then
23 tx_speed=$((diff_tx / 1000000))
24 tx_unit="Mbps"
25 else
26 tx_speed=$((diff_tx / 1000))
27 tx_unit="Kbps"
28 fi
29
30 echo "Up: $tx_speed $tx_unit | Down: $rx_speed $rx_unit"
31}
Using the script
You can check out the complete script here.
1snwzd@kepler ~ > system-status
2Usage: system-status [option]
3Options:
4 cpu Display CPU usage
5 temp Display CPU temperature
6 fan Display CPU fan speed
7 mem Display Memory usage
8 swap Display Swap usage
9 eth Display Ethernet usage
10 wifi Display Wifi usage
11 all Display all information
12snwzd@kepler ~ > system-status temp
13CPU Temp: 47 C
14CPU Temp: 46 C
15CPU Temp: 46 C
16^C
17snwzd@kepler ~ > system-status mem
18Memory Used: 3711 MB / 11768 MB
19Memory Used: 3712 MB / 11768 MB
20^C
21snwzd@kepler ~ > system-status wifi
22Up: 0 Kbps | Down: 0 Kbps
23Up: 0 Kbps | Down: 0 Kbps
24^C
25snwzd@kepler ~ > system-status all
26CPU Temp: 46 C
27Fan Speed: 1800 RPM
28Memory Used: 3683 MB / 11768 MB
29Swap Used: 631 MB / 8191 MB
30Up: 0 Kbps | Down: 0 Kbps
31Up: 0 Kbps | Down: 0 Kbps
32---
33CPU Usage: 2%
34CPU Temp: 46 C
35Fan Speed: 1800 RPM
36Memory Used: 3684 MB / 11768 MB
37Swap Used: 631 MB / 8191 MB
38Up: 0 Kbps | Down: 0 Kbps
39Up: 0 Kbps | Down: 0 Kbps
40---
Extras
- You should write a bash script with
set -eux.-edisallows the script to execute further if one of the statements in the script fails.-utreats unset variables as errors and exits.-xprints each command before executing it to stdout. - There is a difference in POSIX-compliant syntax and bash extensions of it, e.g. while writing an
ifcondition, if you use[ condition ], you are using POSIX-compliant syntax. But with bash extensions[[ condition ]], you can do pattern matching==, regex=~, logical operations&& ||, etc. - If you don't want your script to be executed as root, check
$EUID, i.e., effective user ID. If it is 0, prevent the script from executing further. Running a script assudofrom a user who is part of the wheel group sets the user ID to 0, which gives administrator privileges to the user. You can check this by executingid -uand thensudo id -u. - You can check the exit status of the previously executed program with
$?.
Thankyou for reading my blog.