Elenco in Conky di applicazioni con l'uso della swap
Published: 2023-04-20 15:30:00

In continuazione del precedente post, adesso vediamo come stampare in Conky un elenco di programmi con la relativa quantità di swap usata.

Prepariamo uno script bash che restituisce il testo che vogliamo visualizzare in Conky basato sul comando spiegato in precedenza ma con una differenza in particolare: ci possono essere più processi in esecuzione dello spesso comando (ad esempio 5 processi per Chrome) ma quello che voglio visualizzare in Conky, è la somma della swap usata da tutti i processi dello stesso comando.

```bash
unioneTest=""
for file in /proc/*/status; do
  if [[ -f "$file" ]]; then
    test=$(awk '/^(Name:)/ {awk_name=$2} /^(VmSwap:)/ {awk_vmswap=$2" "$3} /^(Pid:)/ {awk_pid=$2} END { if (awk_vmswap && substr(awk_vmswap,1,1) != '0' ) print awk_name" "awk_pid" "awk_vmswap}' $file)
    if [ -n "$test" ]; then
      unioneTest+=$test"\n"
    fi
  fi
done
```

A differenza del comando precedente, alla fine dell'analisi awk di ogni file, viene verificato se la variabile _awk_vmswap_ non è vuota e se il primo carattere non è "0". Se queste condizioni sono soddisfatte, viene creato un nuovo record di informazioni sul processo, che viene memorizzato nella variabile _test_.
Se la variabile _test_ non è vuota, viene aggiunto alla variabile _unioneTest_ e viene inserito un carattere di nuova linea ("\n") per separare i record.
Alla fine, _$unioneTest_ conterrà l'elenco dei processi che usano la swap.

Il passo successivo, è sommare tra loro i valori di swap degli stessi comandi.
Per farlo, creo un array associativo denominato _somma_ mediante il comando `declare -A`.
Questo array verrà utilizzato per accumulare i valori della terza colonna (ovvero la quantità di swap usata) dei processi che hanno lo stesso nome.

```bash
dati=$unioneTest
declare -A somma

while read -r linea; do
  nome=$(echo "$linea" | cut -d ' ' -f1)
  valore=$(echo "$linea" | cut -d ' ' -f3)
  ((somma[$nome] += valore))
done <<<"$(echo -e "$unioneTest")"
```

Il ciclo _while_ viene alimentato con un [here-string](https://tldp.org/LDP/abs/html/x17837.html) (<<<) che contiene l'output di un comando _echo -e_ che converte una stringa contenente i dati da elaborare.

Infine, stampiamo l'elenco dall'array _somma_ inserendo in ogni riga il numero di spazi necessario ad avere una lunghezza prestabilita. Gli spazi vengono inseriti tra il nome del comando e la quantità di swap usata.
La lunghezza delle righe, verrà specificata da un parametro dello script.

```bash
elenco=""
  for nome in "${!somma[@]}"; do
    tmp="$nome${somma[$nome]} kB\n"
    nrSpacesToAdd=$(expr $spaces - ${#tmp})
    elenco+="$nome$(printf '%*s' $nrSpacesToAdd)${somma[$nome]} kB\n"
  done
  echo -e "$elenco" | sort -k 2 -n -r
```

_nrSpacesToAdd_ calcola il numero di spazi da aggiungere ad una stringa, in modo da ottenere una stringa di una lunghezza specifica.
La variabile _$spaces_ rappresenta il numero totale di spazi che si desidera avere nella stringa finale.
La variabile _${#tmp}_ rappresenta la lunghezza della stringa _tmp_.

`(printf '%*s' $nrSpacesToAdd)` viene utilizzato per generare una stringa di spazi della lunghezza _$nrSpacesToAdd_.

Questo è lo script completo:

```bash
#!/bin/bash

if [ $# -eq 0 ]; then
  echo "Specificare la lunghezza delle stringhe"
  exit 1
fi

# Lunghezza voluta delle stringhe
spaces=$1

unioneTest=""
for file in /proc/*/status; do
  if [[ -f "$file" ]]; then
    test=$(awk '/^(Name:)/ {awk_name=$2} /^(VmSwap:)/ {awk_vmswap=$2" "$3} /^(Pid:)/ {awk_pid=$2} END { if (awk_vmswap && substr(awk_vmswap,1,1) != '0' ) print awk_name" "awk_pid" "awk_vmswap}' $file)
    if [ -n "$test" ]; then
      unioneTest+=$test"\n"
    fi
  fi
done

if [ -n "$unioneTest" ]; then
  dati=$unioneTest
  declare -A somma

  while read -r linea; do
    nome=$(echo "$linea" | cut -d ' ' -f1)
    valore=$(echo "$linea" | cut -d ' ' -f3)
    ((somma[$nome] += valore))
  done <<<"$(echo -e "$unioneTest")"

  elenco=""
  for nome in "${!somma[@]}"; do
    tmp=" $nome${somma[$nome]} kB\n"
    nrSpacesToAdd=$(expr $spaces - ${#tmp})
    elenco+="$nome$(printf '%*s' $nrSpacesToAdd)${somma[$nome]} kB\n"
  done
  echo -e "$elenco" | sort -k 2 -n -r
fi
```
Esempio di output:

```bash
$ ./swapUsed.sh 41
java                           94708 kB
apache2                        16188 kB
gnome-software                  8792 kB
chrome                           632 kB
pipewire-pulse                   564 kB
dbus-launch                      452 kB
exim4                            388 kB
dbus-daemon                      388 kB
postgres                          72 kB
(sd-pam)                          64 kB
containerd                        60 kB
spotify                           52 kB
```

Per usare il comando in Conky, ho aggiunto al file di configurazione .conkyrc le seguenti linee nella sezione conky.text:

```
...
$hr
${color grey}Name                          Swap used
${color lightgrey}${execi 30 ~/swapUsed.sh 41 | head -n 10}
```

Questo fa si che il comando venga eseguito ogni 30 secondi con il parametro _41_ che specifica la lunghezza delle stringhe corretta per la mia configurazione Conky.
Con `head -n 10`, indico di stampare solo le prime 10 righe.

Qui il file .conkyrc completo:

```
-- Conky, a system monitor https://github.com/brndnmtthws/conky
--
-- This configuration file is Lua code. You can write code in here, and it will
-- execute when Conky loads. You can use it to generate your own advanced
-- configurations.
--
-- Try this (remove the `--`):
--
--   print("Loading Conky config")
--
-- For more on Lua, see:
-- https://www.lua.org/pil/contents.html

conky.config = {
    alignment = 'top_right',
    background = false,
    border_width = 1,
    cpu_avg_samples = 2,
    default_color = 'white',
    default_outline_color = 'white',
    default_shade_color = 'white',
    double_buffer = true,
    draw_borders = false,
    draw_graph_borders = true,
    draw_outline = false,
    draw_shades = false,
    extra_newline = false,
    font = 'DejaVu Sans Mono:size=8',
    gap_x = 60,
    gap_y = 60,
    minimum_height = 5,
    minimum_width = 5,
    net_avg_samples = 2,
    no_buffers = true,
    out_to_console = false,
    out_to_ncurses = false,
    out_to_stderr = false,
    out_to_x = true,
    own_window = true,
    own_window_colour = '000000',
    own_window_class = 'Conky',
    own_window_type = 'desktop',
    own_window_transparent = true,
    own_window_argb_visual = true,
    own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager',
    show_graph_range = false,
    show_graph_scale = false,
    stippled_borders = 0,
    update_interval = 5.0,
    uppercase = false,
    use_spacer = 'none',
    use_xft = true,
}

conky.text = [[
${color grey}Uptime:$color $uptime
${color grey}RAM Usage:$color $mem/$memmax - $memperc% ${membar 8}
${color grey}Swap Usage:$color $swap/$swapmax - $swapperc% ${swapbar 8}
${color grey}CPU Usage:$color $cpu% ${cpubar 8}
${color grey}Processes:$color $processes  ${color grey}Running:$color $running_processes
$hr
${color grey}File systems:
 root $color${fs_used /}/${fs_size /} ${fs_bar 8 /}
 ec2-free $color${fs_free /mnt/ec2_export}/${fs_size /mnt/ec2_export} ${fs_bar 8 /mnt/ec2_export}
 qnap $color${fs_used /mnt/u_rsync}/${fs_size /mnt/u_rsync} ${fs_bar 8 /mnt/u_rsync}
${color grey}Networking:
Up:$color ${upspeed} ${color grey} - Down:$color ${downspeed}
$hr
${color grey}Name              PID     CPU%   MEM%
${color lightgrey} ${top name 1} ${top pid 1} ${top cpu 1} ${top mem 1}
${color lightgrey} ${top name 2} ${top pid 2} ${top cpu 2} ${top mem 2}
${color lightgrey} ${top name 3} ${top pid 3} ${top cpu 3} ${top mem 3}
${color lightgrey} ${top name 4} ${top pid 4} ${top cpu 4} ${top mem 4}
${color lightgrey} ${top name 5} ${top pid 5} ${top cpu 5} ${top mem 5}
$hr
${color grey}Name                          Swap used
${color lightgrey}${execi 30 ~/swapUsed.sh 41 | head -n 10}
]]
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8gmaiepta7kxydr6cqq.png)