[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 681.   Sistema GNU/Linux fai da te: script

Il kernel di un sistema GNU/Linux offre molte informazioni attraverso il file system virtuale /proc/, inoltre consente di intervenire sul suo funzionamento modificando (virtualmente) questi file. Quando si è alla ricerca di realizzare ciò che è essenziale per il proprio sistema, si può sfruttare questa possibilità.

Questo capitolo ha lo scopo di raccogliere una serie di esempi di script per la realizzazione di funzionalità elementari, senza bisogno dei programmi tradizionali corrispondenti.

Per poter funzionare, questi script richiedono comunque la disponibilità di software elementare, con le caratteristiche descritte nella tabella successiva.

Tabella 681.1. Comandi essenziali richiesti. Si osservi che i comandi possono essere rappresentati da programmi autonomi o essere incorporati nella Shell (o in un altro programma).

Sintassi Descrizione
/bin/sh
Shell Bourne o POSIX.
test espressione
[ espressione ]
Comando per la verifica delle condizioni.
echo [testo]
Comando per emettere un messaggio attraverso lo standard output.
cat file...
Comando per emettere un file attraverso lo standard output.
read [nome_variabile]...
Comando per attendere e leggere dallo standard input una riga da assegnare, parola per parola, alle variabili elencate negli argomenti.
sed
Stream editor.
set
Comando per leggere e impostare variabili di ambiente e parametri della shell.

681.1   «true»

true

Il programma true si limita a restituire il valore Vero, che attraverso uno script di una shell Bourne si rappresenta restituendo zero:

#!/bin/sh
exit 0

681.2   «false»

false

Il programma false fa l'opposto di true e restituire il valore Falso, che attraverso uno script di una shell Bourne si rappresenta restituendo qualunque valore diverso da zero:

#!/bin/sh
exit 1

681.3   «basename»

basename percorso

Ciò che si vuole è separare l'elemento finale di un percorso (in questo caso non si considera il problema dell'estensione). Per risolvere il problema, si sfrutta la variabile di ambiente IFS (Internal field separator) e il comando set con l'opzione --, il cui scopo è quello di trasferire l'argomento successivo nella sequenza dei parametri posizionali (eliminando quanto fosse già esistente prima):

      1 #!/bin/sh
      2 IFS='/'
      3 set -- $1
      4 for a in $@
      5 do
      6     b=$a
      7 done
      8 echo $b

In pratica: si stabilisce che il carattere da usare per separare le parole deve essere la barra obliqua (riga numero 2); si riscrivono i parametri posizionali utilizzando il primo parametro ricevuto dalla chiamata (riga numero 3); si scandiscono i nuovi parametri posizionali (righe da 4 a 7), senza fare nulla di significativo; alla fine viene emesso l'ultimo valore trovato dalla scansione.

Tra gli script del sistema GNU/Linux LRP (Linux router project, originariamente presso http://www.linuxrouter.org), appare un metodo diverso che funziona, ma viene mostrato senza spiegazione:

#!/bin/sh
# Da: "Linux router project" 2.9.8 anno 2000
IFS='/'
set -- $1
eval rc="\$$#"
[ "$rc" = "" ] && eval rc="\$$(($# - 1))"
echo "$rc"

681.4   «dirname»

dirname percorso

Ciò che si vuole è separare il percorso che precede l'elemento finale, ovvero determinare la directory che contiene l'ultimo elemento. Per risolvere il problema si utilizza sed, verificando inizialmente alcuni casi:

      1 #!/bin/sh
      2 if [ "$1" = "/" ]
      3 then
      4     echo "/"
      5     exit 0
      6 fi
      7 A=`echo "$1" | sed "s/\///g"`
      8 if [ "$1" = "$A" ]
      9 then
     10     echo "."
     11     exit 0
     12 fi
     13 echo "$1" | sed "s/\/$//" \
     14           | sed "s/^\(..*\)\/[^/]*$/\1/"

In pratica, nelle righe dalla numero 2 alla numero 6 si verifica se il percorso è esattamente la radice, perché in tal caso il risultato deve rimanere tale e quale; nella riga 7 viene assegnato alla variabile A il risultato della trasformazione del percorso, togliendo tutte le barre oblique, per poi confrontarlo (righe da 8 a 12) con il percorso iniziale: se le due stringhe sono uguali, vuol dire che si tratta di un nome unico riferito alla directory corrente, pertanto si deve restituire un solo punto. In tutti gli altri casi si rimuove la barra obliqua finale, se c'è (riga 13), quindi si rimuove la parte di percorso che inizia dall'ultima barra obliqua rimasta (riga 14).

Tra gli script del sistema GNU/Linux LRP (Linux router project, originariamente presso http://www.linuxrouter.org), appare un metodo diverso che funziona, ma viene mostrato senza spiegazione:

#!/bin/sh
# Da: "Linux router project" 2.9.8 anno 2000
echo "$1" | sed '/^\/$/c\
/
s/\/*$//
s/[^/]*$//
/./!c\
.
s/\/$//'

681.5   «grep»

grep [-n] modello file...

Dagli script del sistema GNU/Linux LRP (Linux router project, originariamente presso http://www.linuxrouter.org):

#!/bin/sh
# Da: "Linux router project" 2.9.8 anno 2000
pat=""
file=""
if [ "$1" = "-n" ]; then
        pat="$2"
        shift 2
        for file in $@ ; do
                sed -n -e "\'$pat'=" -e "\'$pat'P" $file |
                sed -e N -e 's/\(.*\)\n/'$file':\1:/'
        done
else
        pat="$1"
        shift
        sed "\'$pat'P" -n "$@"
fi

681.6   «head»

head [-n n] file...

Con il comando head si vogliono ottenere le prime 10 righe, oppure le prime n righe specificate con l'opzione -n, dai file indicati:

#!/bin/sh
n=10
if [ "$1" = "-n" ]
then
    n=$2
    shift 2
fi
sed "$n"q "$@"

Come si vede, si sfrutta il comando q di sed, in modo da terminare l'emissione del testo dopo $n righe.

681.7   «tail»

tail [-n n] file...

Con il comando tail si vogliono ottenere le ultime 10 righe, oppure le ultime n righe specificate con l'opzione -n, dai file indicati. La soluzione mostrata è tratta dagli script del sistema GNU/Linux LRP (Linux router project, originariamente presso http://www.linuxrouter.org):

#!/bin/sh
# Linux Router Project 2.9.8 year 2000
num=10
if [ "$1" = "-n" ]; then
        num=$2
        shift 2
fi
num=$(($num + 1))
sed -e :a -e '$q;N;'$num',$D;ba' "$@"

681.8   «id»

id

Con il comando id si vogliono ottenere i numeri UID e GID dell'utente, assieme ai nomi relativi, oltre all'abbinamento ad altri gruppi rispetto a quello principale. La soluzione seguente dà soltanto l'indicazione del gruppo di origine:

      1 #!/bin/sh
      2 IFS=':'
      3 set -- `grep "^$USER:" /etc/passwd`
      4 uid=$3
      5 gid=$4
      6 set -- `grep ":[^:]*:$gid:" /etc/group`
      7 group=$1
      8 echo "uid=$uid($USER) gid=$gid($group)"

In pratica: si stabilisce che il carattere da usare per separare le parole devono essere i due punti (riga numero 2); si riscrivono i parametri posizionali utilizzando quanto restituito da grep che cerca la riga corrispondente nel file /etc/passwd (riga numero 3); si salvano i numeri UID e GID in due variabili di ambiente (righe 4 e 5); si esegue un'altra ricerca nel file /etc/group in base al numero GID ottenuto (riga numero 6); si salva il nome del gruppo (riga numero 7); si compone il risultato finale (riga numero 8).

681.9   «whoami»

whoami

Basta mostrare il contenuto della variabile di ambiente USER:

#!/bin/sh
echo "$USER"

681.10   «nl»

nl file...

Con il comando nl si vogliono emettere attraverso lo standard output i file indicati come argomento, numerando le righe. La soluzione mostrata è tratta dagli script del sistema GNU/Linux LRP (Linux router project, originariamente presso http://www.linuxrouter.org):

      1 #!/bin/sh
      2 # Linux Router Project 2.9.8 year 2000
      3 if [ "$1" = "-b" ]; then
      4         if [ "$2" = "a" ]; then
      5                 sed = $3 | sed 'N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'
      6         fi
      7 shift 2
      8 else
      9         sed -e '/^$/d' $1 -e = |
     10         sed '/./N; s/^/     /; s/ *\(.\{6,\}\)\n/\1  /'
     11 fi

Come si può interpretare, è ammesso l'uso dell'opzione -b a, ma non è prevista la scansione di più file.

681.11   «free»

free

Il programma free di un sistema GNU/Linux si occupa di interpretare il file /proc/meminfo, mostrando un risultato sintetico sulla disponibilità della memoria centrale reale e di quella virtuale. In generale, è sufficiente visualizzare il contenuto di questo file, anche se l'aspetto non è conforme alla convenzione:

#!/bin/sh 
cat /proc/meminfo

Il comando free comune potrebbe dare il risultato seguente:

             total       used       free     shared    buffers     cached
Mem:         27892      27108        784          0        620       3024
-/+ buffers/cache:      23464       4428
Swap:       102780      30144      72636

Al contrario, visualizzando il contenuto di /proc/meminfo si può avere questo rapporto:

        total:    used:    free:  shared: buffers:  cached:
Mem:  28561408 27729920   831488        0   659456 23130112
Swap: 105246720 30867456 74379264
MemTotal:        27892 kB
MemFree:           812 kB
MemShared:           0 kB
Buffers:           644 kB
Cached:           3052 kB
SwapCached:      19536 kB
Active:          10884 kB
Inactive:        12520 kB
HighTotal:           0 kB
HighFree:            0 kB
LowTotal:        27892 kB
LowFree:           812 kB
SwapTotal:      102780 kB
SwapFree:        72636 kB

681.12   «ps»

ps

Dal file /proc/n/stat è possibile ottenere facilmente alcune informazioni sullo stato del processo elaborativo abbinato al numero PID n; inoltre, dal file /proc/n/cmdline è possibile leggere la riga di comando (anche se non in modo perfetto):

echo "PID       STAT    COMMAND"
for proc in /proc/[0-9]*/
do
    read a b c z < $proc/stat
    echo "$a    $c      $b `cat $proc/cmdline`"
done

Si osservi che gli spazi orizzontali dei due comandi echo sono ottenuti esattamente con un carattere di tabulazione orizzontale, altrimenti non si otterrebbe l'allineamento delle colonne.

A prima vista, lo script potrebbe sembrare un po' oscuro. Il punto chiave sta nel comando read, che riceve le prime tre parole contenute nella riga che compone il file /proc/n/stat, mentre il resto viene abbandonato nell'ultima variabile. Successivamente, vengono riordinati i dati con il comando echo, aggiungendo il contenuto del file /proc/n/cmdline.

681.13   «uptime»

uptime

Dal file /proc/uptime è possibile sapere da quanti secondi è in funzione l'elaboratore, mentre dal file /proc/loadavg si ottiene il carico medio. Sono necessarie delle rielaborazioni per calcolare il tempo in ore e in giorni (togliendo i decimali), inoltre serve l'ora attuale:

#!/bin/sh
IFS='.'
set -- `cat /proc/uptime` 
H=$(($1 / 3600))
D=$(($H / 24))
IFS=' '
set -- `date` 
T="$4"
set -- `cat /proc/loadavg`
echo " $T up $D Days ($H"h"), load average: $1, $2, $3"

Dal rapporto che si ottiene manca l'indicazione della quantità degli utenti collegati:

 19:22:08 up 0 Days (6h), load average: 0.05, 0.01, 0.00

681.14   «hostname»

hostname [-y] [nome]

Il programma hostname viene usato normalmente per impostare il nome dell'elaboratore, per definire il dominio NIS (con l'opzione -y), o per conoscere il nomi già definiti. In un sistema GNU/Linux basta intervenire nel file /proc/sys/kernel/hostname per conoscere o modificare il nome dell'elaboratore, mentre si può usare il file /proc/sys/kernel/domainname per il dominio NIS:

if [ "$1" = "-y" ]
then
    PROC=/proc/sys/kernel/domainname
    shift
else
    PROC=/proc/sys/kernel/hostname
fi
if [ "$1" = "" ]
then
    cat $PROC
else
    echo "$1" > $PROC
fi

681.15   «uname»

uname [-a|-s|-n|-r|-m|-o|-v]

Le informazioni sul sistema si possono ottenere da vari file nella directory /proc/sys/kernel/:

#!/bin/sh
case $1 in
-a)
    echo "`cat /proc/sys/kernel/ostype` `cat /proc/sys/kernel/hostname` \
`cat /proc/sys/kernel/osrelease` `cat /proc/sys/kernel/version` i386 unknown"
    ;;
-r)
    echo "`cat /proc/sys/kernel/osrelease`"
    ;;
-n)
    echo "`cat /proc/sys/kernel/hostname`"
    ;;
-v)
    echo "`cat /proc/sys/kernel/version`"
    ;;
-m)
    echo "i386"
    ;;
-o)
    echo "GNU/Linux"
    ;;
-s|*)
    echo "`cat /proc/sys/kernel/ostype`"
    ;;
esac

Naturalmente, si può anche ridurre, se non servono alcune informazioni che altrimenti sono implicite:

#!/bin/sh
case $1 in
-a)
    echo "`cat /proc/sys/kernel/ostype` `cat /proc/sys/kernel/hostname` \
`cat /proc/sys/kernel/osrelease` `cat /proc/sys/kernel/version` i386 unknown"
    ;;
-r)
    echo "`cat /proc/sys/kernel/osrelease`"
    ;;
-n)
    echo "`cat /proc/sys/kernel/hostname`"
    ;;
-v)
    echo "`cat /proc/sys/kernel/version`"
    ;;
-s|*)
    echo "`cat /proc/sys/kernel/ostype`"
    ;;
esac

681.16   «cpuinfo»

cpuinfo

Questo non è un comando standard dei sistemi Unix, ma può essere utile accedere facilmente al contenuto del file /proc/cpuinfo:

#!/bin/sh 
cat /proc/cpuinfo

Il risultato può essere molto interessante:

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 8
model name      : Celeron (Coppermine)
stepping        : 6
cpu MHz         : 768.434
cache size      : 128 KB
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 2
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 sep mtrr \
  \pge mca cmov pat pse36 mmx fxsr sse bogomips : 1533.54

681.17   «lsmod»

lsmod

Si tratta semplicemente di leggere il file /proc/modules:

#!/bin/sh 
cat /proc/modules

Il risultato che si ottiene potrebbe essere simile a quello seguente:

emu10k1                49836   0 (unused)
ac97_codec             11956   0 [emu10k1]
via-rhine              13328   1
mii                     2240   0 [via-rhine]
crc32                   2880   0 [via-rhine]
emu10k1-gp              1288   0 (unused)
gameport                1548   0 [emu10k1-gp]
agpgart                38232   0 (unused)
ide-tape               42064   0 (autoclean)

681.18   Riferimenti

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome sistema_gnu_linux_fai_da_te_script.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!