zkk / Linux Exam 101 - Moreno Razzoli



Sezione 1 : Introduzione

0.0 - Distribuzioni Linux : le differenze, e quale scegliere

Durante il corso verranno principalmente evidenziate le differenze tra RedHat e Debian.

0.1 - Scaricare, installare e configurare VirtualBox

Spiega come installare Virtualbox.

0.2 - Download ed installazione di Linux

Spiega come installare Ubuntu.

0.3 - Installazione VirtualBox Guest Additions e risoluzione problemi comuni

Avviamo la macchina virtuale e digitiamo nel terminale:

sudo apt update && sudo apt install build-essentials -y

Inseriamo quindi il cd delle guest-additions nella macchina virtuale e, se non parte automaticamente, eseguiamo il file autorun.sh (ci verrà chiesta la password di amministratore) poi si deve riavviare la macchina.

Qualche volta abbiamo la necessità di aggiungere il nostro utente al gruppo vboxsf con il seguente comando:

sudo usermod -aG vboxsf <nome-utente>

IMPORTANTE : Problemi con VirtualBox

Elenco di alcuni problemi comuni utilizzando Virtualbox (e relativa soluzione).

Se dopo aver seguito le istruzioni per l’installazione delle VirtualBox Guest Additions alcune funzionalità come:

Non dovessero funzionare, non preoccupatevi.

Queste funzionalità sono state mostrate per completezza e per massimizzare il vostro comfort nel caso in cui voleste scambiare dati da e verso la macchina virtuale, ma non sono necessari ai fini dello svolgimento del corso, in quanto non andremo mai a fare uso di tali funzionalità.

Se volete comunque provare a risolvere, l’approccio generalmente consigliato alla risoluzione dei problemi è il seguente:

  1. Ricerca VirtualBox nella sezione D&R: ci sono già numerosi casi trattati (e future richieste non riceveranno considerazione in quanto duplicate).
  2. Ricerca del codice dell’errore di VirtualBox (se presente) o descrizione della problematica (a più riprese e con diversi sinonimi) nella barra di ricerca di Google: come informatici o aspiranti tali è indispensabile imparare ad usare i motori di ricerca in maniera efficace. Serve perseveranza: non si trova sempre tutto al primo tentativo, è normale.

VirtualBox funziona solo a 32 bit

Questo problema è tipicamente causato dalle impostazioni del BIOS: alcuni produttori disabilitano la funzionalità virtualizzazione o VT-x ed è necessario andare ad abilitarla manualmente. Questa particolare procedura varia da computer a computer in base a marca e modello.

Questo problema può anche essere causato da una delle seguenti cause:

Questo genere di problema a seconda dei casi può essere più tedioso da risolvere, perciò sarà necessario un quantitativo non indifferente di ingegno da parte vostra: anche in questo caso valgono i consigli 1 e 2 del punto precedente.

Sezione 2 : 101 - Architettura del sistema

101.1 - Gli interrupts in Linux

Le periferiche comunicano con il processore tramite gli interrupts che ‘interrompono’ il processore per comunicare cosa stanno facendo (ad esempio l’interrupt della tastiera interrompe il processore ogni volta che si digita un tasto).

Il file /proc/interrupts (rigenerato ad ogni avvio del sistema) contiene (da sinistra a destra):

101.1 - ioports e DMA in Linux

File /proc/ioports

Una volta che il dispositivo lancia l’interrupt al processore, questo gli assegna un indirizzo di memoria in modo che possa essere utilizzata dalla periferica (sempre con la mediazione del processore). Gli indirizzi di memoria assegnati alle varie periferiche sono indicati nel file /proc/ioports.

File /proc/dma

Nelle macchine più recenti viene riservato un indirizzo di memoria che la periferica può utilizzare senza la mediazione del processore, questi indirizzi sono registrati nel file /proc/dma.

101.1 - dispositivi e drivers in Linux

lspci  # mostra i dispositivi collegati
lspci -t  # mostra i dispositivi collegati in una struttura ad albero

Dal kernel 2.6 i devices vengono creati dinamicamente e sono inseriti nella cartella /dev/.

UDEV crea automaticamente i device sotto /dev/ quando vengono collegati e comunica con lo userspace tramite il netlink socket quando sono caricati i devices.

Indipendentemente che siano hot-plug o cold-plug i vari dispositivi hanno bisogno di un driver per funzionare, quelli caricati sono visualizzati digitando il comando =lsmod= che elenca:

I moduli che non sono utilizzati da nessun dispositivo possono essere rimossi dalla memoria tramite:

rmmod <nome-modulo>

Per visualizzare la lista dei moduli non utilizzati:

lsmod | awk -F ' ' '{if ($3==0) print $0}' | less

Per inserire un mdulo invece si esegue il seguente comando:

insmod <full-path-to-module>

Tutti i moduli si trovano all’interno della directory /lib/modules/<kernel-version>/ e posso cercarli tramite il comando:

find /lib/modules/$(uname -r)/ -iname "**<nome-modulo>*.ko*"

Per caricare o rimuovere un modulo posso alternativamente utilizzare i seguenti comandi (più semplice perchè non serve il full-path):

modeprobe <nome-modulo>     # per caricare il modulo
modeprobe -r <nome-modulo>  # per scaricarlo

La configurazione di un modulo (configurare gli interrupts oppure l’indirizzo dma da utilizzare con quel modulo) può essere fatta modificando/creando il file /etc/modeprobe.conf presente nella directory /etc/modprobe.d/.

sysfs è un file system montato dentro a /sys/ che contiene informazioni accessibili in modalità testuale relative ai devices. Risulta accessibile dallo =userspace= sia per le applicazioni che per gli utenti anche senza privilegi di root.

Digitare:

mount | grep sys

Per verificare che sysfs è montato su /sys/.

Quindi ricapitolando: le applicazioni accedono ai device ed alle loro proprietà tramite la cartella /sys/ che, tramite dei simbolic-links ci fa accedere alle informazioni relative ai devices.

Dentro a /sys/ abbiamo quindi i devices divisi per categoria, tipo nei block-devices abbiamo le chiavette o i dischi e nella directory /devices/ abbiamo, appunto, i devices con molte informazioni a cui attingere.

All’interno della directory /sys/dev/ abbiamo sempre i devices ma con cui interagiscono i processi che, tramite link simbolici puntano al bus e alla directory del dispositivo.

101.1 - Spiegazione di D-bus, HAL, udev

Un’altra rappresentazione è tramite un ’tubo’ che permette ai vari comparti di comunicare.

Infine possiamo vedere dove si trovano D-bus e Udev (fagocitato da Systemd) tramite una rappresentazione ancora più complessa ma più completa.

101.2 - BIOS, EFI, Bootloader e runlevel

BIOS/UEFI

Sequenza di boot (BIOS)

Alcune definizioni:

Il comando lsinitrd oppure lsinitramfs elencano il contenuto del relativo file.

Quando deve lanciare windows, GRUB in realtà non fa altro che caricare il bootloader di windows non essendo in grado di avviare propriamente windows.

EFI/UEFI boot

bootloaders

Elenco dei bootloader di Linux per il BIOS:

Elenco dei bootloader di Linux per UEFI:

processo init

Il primo processo ad essere lanciato è init che ha il PID 1 e fa partire tutti gli altri processi. Rimane attivo fino allo spegnimento della macchina ed è chiaramente l’ultimo processo a morire.

Nei nuovi sistemi che utilizzano systemd il file /sbin/init è un collegamento simbolico a /lib/systemd/systemd. La stessa cosa può essere verificata analizzando il comando del primo processo avviato dal sistema tramite:

cat /proc/1/cmdline  # contiene il file eseguito che è un collegamento al programma 'systemd'
cat /proc/1/comm     # contiene il programma lanciato 'systemd' a cui è collegato il comando indicato in 'cmdline'

Con systemd si utilizza il concetto di units, che possono essere definite come delle risorse di sistema che systemd è in grado di gestire. Queste risorse sono divise in varie categorie riconoscibili dal suffisso applicato al nome della unit, quali, a titolo esemplificativo e non esaustivo:

runlevels

Ci sono 6 livelli di runlevel:

101.2 - Runlevel e diagnostica al boot

Per poter partire con un determinato runlevel posso modificare i parametri del kernel (digitare e in corrispondenza della relativa riga del menu di GRUB) aggiungendo alla fine della riga del comando il numero del runlevel in cui voglio partire (1|2|3|4|5).

Il file /etc/inittab contiene il runlevel di default. Questo file era però utilizzato prima dell’avvento di systemd (anche se in realtà io ce l’ho ancora). Quindi per sapere il runlevel digitiamo:

systemctl get-default

Che ci può restituire:

Per cambiare il runlevel di default digitiamo:

systemctl set-default <nuovo-runlevel>

Il seguente comando:

ls -l /lib/systemd/system/runlevel*.target

Serve per vedere i runlevel che ci sono e che sostanzialmente puntano a dei target:

Se, alla fine della linea del kernel di GRUB, aggiungo init=/bin/sh, avrò l’avvio del sistema con una shell con privilegi di root (senza inserire la password di root!!!!).

All’interno del file /var/log/boot.log ci sono tutte le scritte che appaiono al boot, mentre il file /var/log/messages è un log globale da cui partire per cercare eventuali errori (sono presenti eventi dbus, eventi kernel ed altro).

Il comando dmesg digitato come utente root o preceduto da sudo fa vedere i messaggi del kernel (anche dopo il boot).

101.2 - Leggere i log con journalctl

journalctl è l’aggregatore dei log di sistema fornito da systemd.

Non è pratico usare journalctl senza opzioni in quanto il suo output risulta essere un’infinito elenco di log provenienti da qualsiasi elemento del sistema pertanto analizziamo le opzioni principali, utili a selezionarne gli elementi da visualizzare.

journalctl -b  # mostra i log dal boot;
journalctl -br  # mostra i log dal boot in ordine inverso (opzione =-r=);
journalctl --list-boots  # mostra tutti i boot della macchina indicando un numero identificativo all'inizio della riga;
journalctl -b -<numero-boot>  # per visualizzare un determinato boot;
journalctl --since="1 hour ago"  # mostra i log dell'ultima ora;
journalctl --since="20 minutes ago"  # mostra i log degli ultimi 20 minuti;
journalctl --since="2022-10-31"  # mostra i log della data indicata;
journalctl --since="1 hour ago" --until="20 minutes ago"  # mostra i log dell'ultima ora fino a 20 minuti fa;
journalctl _UID=<user-id>  # mostra i log di un determinato utente.

Tutte varie linee dell’output di journalctl non vanno a capo ma possono essere visualizzate se troppo lunghe utilizzando i tasti cursore <RIGHT> e <LEFT>.

L’opzione -k equivale all’opzione --dmesg e mostra (appunto) i messaggi del kernel (equivale al comando dmesg).

L’opzione -u <nome-unita> (oppure --unit <nome-unita>) visualizza i log di quella particolare unità.

Il comando systemctl lanciato da solo visualizza le unità in esecuzione.

All’interno dell’output di journalctl posso cercare ed evidenziare una stringa di testo premettendo il carattere / (N per cercare la ricorrenza successiva, n per cercare a ritroso).

Il comando journalctl -f serve per vedere in tempo reale i log (come per tail -f 'nome-log-file').

101.3 - Cambiare runlevel | System V init

Come detto in precedenza per cambiare runlevel al boot si mette il numerino alla fine della riga del kernel in GRUB.

Per i sistemi che utilizzano =systemd= possiamo cambiare il runlevel predefinito tramite:

systemctl set-default <nuovo-runlevel-predefinito>

Mentre per i sistemi che utilizzano System V init devo modificare il numero di runlevel di default all’interno del file /etc/inittab e precisamente nella seguente riga:

id:5:initdefault:

All’interno del file /etc/inittab possono essere configurati, per ogni runlevel, i comandi che vengono lanciati. Per i vari parametri utilizzati (es. respawn, wait, once) possiamo leggere la relativa pagina del manuale di inittab.

Per sapere in quale runlevel siamo (da root tramite su -) digitiamo il comando runlevel.

Per poter utilizzare il comando anche con sudo posso creare un collegamento simbolico tramite il seguente comando digitato da root:

ln -s /sbin/runlevel /usr/bin/runlevel

Allo stesso modo posso sapere in che runlevel sono con il comando:

who -r

Per cambiare il runlevel “al volo” invece utilizzo il seguente comando:

telinit <numero-nuovo-runlevel>

La cartella /etc/init.d/ contiene tutti gli script che possono essere lanciati quando passo ad un determinato runlevel.

Le cartelle /etc/rc<numero-runlevel>.d/ invece contengono i collegamenti simbolici agli script che si trovano in /etc/init.d/ e che, a secondo di come è costruito il nome del collegamento, possono essere lanciati (lo script inizia con S) oppure “uccisi” (lo script inizia con K).

sysv-rc-conf è lo strumento che permette di configurare quali servizi vengono lanciati per quali runlevel, è la stessa cosa che creare/eliminare manualmente i collegamenti.

Analogamente esiste poi il comando chkconfig -l che però non trovo in Debian.

101.3 - Comandi shutdown e wall

Per spegnere la macchina è preferibile usare il comando shutdown che:

Vediamo un paio di esempi:

shutdown -c                 #annulla uno shutdown programmato
shutdown 17:01 "messaggio"  # programma uno shutdown che verrà anticipato (15 min. prima) da un messaggio personalizzato

Con il comando wall, si può mandare in broadcast un messaggio personalizzato, per esempio:

wall "messaggio"

All’interno dei doppi apici posso mettere anche delle variabili ($nome-variabile).

101.3 - Come funziona upstart

upstart rimpiazza System V init in Ubuntu e Fedora ma viene a sua volta rimpiazzato da systemd.

Alcuni servizi però non sono ancora stati migrati in systemd e continuano a funzionare con il sistema precedente. All’interno della cartella /etc/init/ troviamo questi servizi (io non ho la cartella).

Alcuni comandi per gestire i servizi con upstart:

stop <servizio>
start <servizio>
status <servizio>

startup permette ai servizi di partire o fermarsi in determinate condizioni e con determinate regole ed al verificarsi di determinati eventi e non solo al cambiamento di runlevel.

Con il seguente comando:

initctl show-config

Possiamo visualizzare queste regole per tutti i servizi configurati.

101.3 - Come funziona systemd

systemd parte dall’assunto di avviare un determinato servizio quando questo viene richiesto.

Molti demoni comunicano attraverso i socket, pertanto systemd associa ad ogni servizio un socket e lo avvia solo quando il suo socket riceve una richiesta.

Ad esempio mountfs monta un disco solamente quando qualche servizio ne fa richiesta e non prima.

Tutto quanto precede rende, almeno teoricamente, l’avvio del sistema più rapido.

Anche systemd è retro compatibile con inittab e, se presente, lo legge.

Ogni unit ha un’estensione che in qualche modo la identifica:

Il comando principale per controllare le unit è systemctl, alcuni esempi:

systemctl status <nome-unity>
systemctl stop <nome-unity>
systemctl start <nome-unity>

Alcuni cenni sulla struttura di un file di configurazione di una unit tipo <file>.service:

Il seguente comando:

systemd-analyze blame

Ci dice quanto ogni servizio ha impiegato in fase di boot.

101.3 - SystemV “runlevels” vs “boot targets” di systemd

systemd sostituisce il concetto di runlevel con il concetto di boot target.

La seguente tabella sintetizza il boot target equivalente per ciascun runlevel:

RunlevelTarget
0poweroff.target
1rescue.target
2,3,4multi-user.target
5graphical.target
6reboot.target

Su tutte le distro più recenti (dove systemd è il sistema di init di default) per cambiare boot target scriviamo alternativamente, ad esempio:

systemctl isolate multi-user.target
systemctl isolate graphical.target

E così via per gli altri target.

Per impostare un boot target predefinito (ad esempio, multi-user.target) dobbiamo prima di tutto abilitarlo e poi impostarlo come default:

systemctl enable multi-user.target
systemctl set-default multi-user.target

Come vedete è più semplice e intuitivo rispetto al vecchio concetto di runlevel.

101.3 - acpid

E’ un demone per gestire gli eventi ACPI (Advance Configuration Power Interface).

Gestisce gli eventi come aprire/chiudere il coperchio del portatile, premere tasti speciali, gestisce la connessione o disconnessione dalla rete elettrica e altro.

Da tener presente che certi Desktop Environment (come Gnome o KDE), oppure il login manager di systemd (il servizio systemd-logind) hanno i loro demoni e regole speciali per gestire questi eventi e che, questo demone potrebbe quindi andare in conflitto.

E’ quindi probabile che questo demone sia disabilitato, per verificare:

systemctl status acpid.service

Per configurare queste regole devo creare i relativi file di configurazione all’interno della cartella /etc/acpi/.

Test sul capitolo 101

Lezioni da n. 6 a n. 18.

Risposte giuste: 7 su 10.

Sezione 3 : 102 - Installazione di Linux e Package Management

102.1 - GNU Parted - alterare la tabella delle partizion

fdisk è pensato per lavorare con MBR:

fdisk -l

Con la diffusione di GPT viene sempre più spesso utilizzato Parted.

parted -l    # elenca i dischi presenti
parted /dev/sdb mklabel msdos  # crea una tabella delle partizioni tipo msdos (MBR)
parted /dev/sdb -a cylinder mkpart primary linux-swap 2 102  # crea una partizione tipo swap di 100MB
parted /dev/sda print  # visualizza le partizioni del disco
parted /dev/sda print free  # visualizza anche gli spazi vuoti
mkswap /dev/sdb1  # per scrivere la partizione di swap
parted /dev/sdb mklabel gpt  # cambia la tabella delle partizioni e cancella tutto il contenuto del disco

Utilizzo di Parted in modalità interattiva:

parted
(parted) select /dev/sda
(parted) print
(parted) print free
(parted) mkpart
Tipo di partizione?
Tipo di file system?
Inzio? 2MB
Fine? 102MB
(parted) print
(parted) q

Adesso la partizione non contiene nessun file system nonostante lo abbiamo selezionato, dobbiamo crearlo con il comando:

mkfs.ext4 /dev/sdb1

Adesso posso montare la partizione ed utilizzarla.

102.1 - PARTED : Recupero partition table

IMPORTANTE

Dopo aver espanso una partizione con fdisk o parted non dimentichiamo di espandere anche il filesystem:

resize2fs /dev/sda

All’opposto, prima di ridurre una partizione riduciamo il suo filesystem:

resize2fs /dev/sda 8GB

Quindi, ricapitolando:

Quindi, ricapitolando ancora:

esempio

parted /dev/sdb
(parted) print
(parted) rm
Partizione numero? 1

Cancello erroneamente una partizione. Praticamente però è stata cancellata la riga della tabella delle partizione ma i dati sono presenti sul disco. Sarà quindi sufficiente ricreare la partizione esattamente come era per rendere nuovamente disponibili i dati.

dd if=/dev/zero of=/dev/sdb bs=512 count=1

Il comando scrive tutti zeri sul primo blocco del disco (la tabella delle partizioni) che viene in tal modo cancellata.

Ricreiamo quindi la partizione allo stesso modo e la recuperiamo.

parted
(parted) print
(parted) mklabel
Tipo dell'etichetta del disco? msdos
(parted) mkpart

102.1 - swap, swapfile e swappiness

getconf PAGESIZE

E’ il comando che ci restituisce l’unità minima che il computer trascrive ‘a blocchi’. Nel mio caso risulta essere un valore di 4096 ovvero 4kb.

swapon -s

Con questo comando vedo le partizioni swap ed il loro utilizzo. Il comando non fa altro che restituire il valore contenuto nel file /proc/swaps.

Come regola generale per decidere quanta swap utilizzare possiamo fare 1,5 o 2 volte la RAM, anche se, mano mano che cresce la RAM installata possiamo anche diminuire la dimensione della partizione swap.

Per creare una partizione swap su un disco /dev/sdb con il comando fdisk digitiamo:

Il comando fdisk crea automaticamente la disk-label tipo msdos in quanto, come detto in precedenza, è un comando pensato per le tabelle MBR.

Ora devo creare il filesystem e dire a linux di utilizzare la partizione:

mkswap /dev/sdb1
swapon /dev/sdb1  # per attivare la partizione swap
swapoff /dev/sdb1  # per disattivarla

Per cambiare la priorità ad una partizione di swap digito:

swapon /dev/sdb1 -p <priorita>

Cambiamento che vale sono fino al riavvio della macchina.

Per rendere la modifica permanente devo aggiugere sul file /etc/fstab mettendo l’opzione -p (priorita).

Nel file /etc/fstab si preferisce mettere l’UUID invece che /dev/sdXY perchè se, in fase di montaggio, cambia la priorità di montaggio dei vari dischi un disco /dev/sdb potrebbe diventare /dev/sdc non venendo quindi eseguita la relativa riga di /etc/fstab, questo non accade usando il riferimento UUID.

Per conoscere un UUID di una partizione esegui il seguente comando:

blkid /dev/sdb1

Un paio di altri comandi:

swapoff -a  # disattiva tutte le partizioni di swap
swapon -a  # rimonta le partizioni di swap da 'etc/fstab'

Anziché utilizzare una partizione come file di swap posso anche utilizzare un file:

dd if=/dev/zero of=/tmp/swapfile bs=10M count=10

Con il precedente comando ho creato un file riempiendolo di ZERI (/dev/zero), quindi:

mkswap /tmp/swapfile  # crea una UUID per questo file che viene impostato di tipo swap
swapon /tmp/swapfile  # la procedura restituisce un warning per problemi di sicurezza
chmod 600 /tmp/swapfile  # imposto permessi per cui solo il proprietario del file può leggere/modificare il file e rieseguo il comando precedente

E’ possibile anche controllare a quale soglia la partizione di swap deve entrare in funzione iniziando a ‘swappare’:

cat /proc/sys/vm/swappiness

Restituisce un valore da 0 a 100, più il valore è alto e più la swap verrà utilizzata. Con un valore pari a 0 la swap verrà utilizzata solamente quando la RAM viene esaurita mentre un valore pari a 100 rende la swap molto più aggressiva ed entrerà in azione prima (non si sa bene quanto prima).

Per cambiare questo valore, come detto, dobbiamo modificare il valore del file di ‘swappiness’:

echo 30 > /proc/sys/vm/swappiness/

Per cambiare permanentemente il valore di swappiness dobbiamo invece agire sul file /etc/sysctl.conf inserendo una riga simile alla seguente:

vm.swappiness=80

102.1 - progettare il partizionamento dei dischi

Il comando df elenca le partizioni montate:

df -hT

In Linux esiste uno standard di come deve essere strutturato il filesystem partendo dalla radice (/) e lo possiamo trovare al seguente Link . Stabilisce delle directory che troveremo sempre su sistemi Linux.

E’ abbastanza frequente trovare la cartella /boot/ su una partizione diversa in quanto, ad esempio, LVM (si veda capitolo successivo) non è supportato al momento del boot. LVM è infatti una architettura software ad alto livello che non è ancora disponibile in fase di boot.

Ci sono cartelle che dovrebbero avere la loro partizione e montate separatamente come, ad esempio, /home/ e /var/.

La cartella /var/log/ (ad esempio) può diventare molto grande quando qualche applicazione inizia a inviare messaggi log con una frequenza intensa per qualche errore. Questo può fare ingrandire la cartella fino a saturare il disco e, nel caso si tratti del disco dove è presente il sistema, può creare problemi di funzionamento del computer.

Per pesare il contenuto delle varie cartelle possiamo lanciare il comando du (seguono alcuni esempi da provare):

du -sh <cartella>/*
du -h <cartella>>/*
du -sh <cartella>/

Posso quindi creare una nuova partizione sul disco e creare la relativa filesystem (tipo ext4).

Può capitare che la tabella delle partizioni caricata in memoria differisca da quella scritta sul disco (questo capita spesso quando quella su disco è stata appena riscritta), in questo caso dobbiamo riallineare le tabelle con il seguente comando:

partprobe /dev/sdb

I passaggi da compiere per spostare i dati di una cartella su una partizione sono quindi i seguenti (ad esempio voglio spostare la cartella /home/ su una partizione diversa da quella dove risiede il sistema operativo):

Comandi per copiare i dati:

rsync -arvh /home/ /mnt/
cp -Ra /home/ /mnt/

Il comando rsync serve solitamente per fare i backup o per mantenere sincronizzate 2 cartelle pertanto posso anche rilanciarlo successivamente e, se ci sono state delle variazioni nell’origine, copierà solamente i dati incrementali.

Comando per cancellare tutti i dati:

rm -rf /home/*

L’ultimo numero delle righe di montaggio delle partizioni di /etc/fstab indica l’ordine con cui fare il check delle partizioni che vengono montate pertanto il n. 1 andrà riservato doverosamente alla partizione /root/ e gli altri numeri (2,3,4,…) alle partizioni meno importanti.

102.1 - Cos’è e come funziona LVM

E’ buona cosa che i Phisical Volumes siano indicati come partizioni (es. /dev/sdb1) e non come disco nella sua interezza (/dev/sdb), anche quando il disco dovesse contenere una sola partizione.

Per poter essere utilizzata da LVM la partizione deve essere creata come tipo LV, ossia tipo 8e e non il solito 83 per le partizioni Linux classiche.

Con LVM si possono spostare i dati a caldo, ossia posso aggiungere un disco e allargare una partizione LVM senza spegnere la macchina.

LVM supporta anche lo striping ossia la possibilità di scrivere e leggere contemporanemanete su più dischi aumentando in questo modo la velocità di esecuzione dell’operazione.

LVM supporta anche la possibilità di fare gli snapshot a caldo, ossia dei backup a caldo delle varie partizioni.

102.1 - LVM: aggiungere e rimuovere dischi

LVM può essere usato in maniera interattiva semplicemente digitandone il comando:

lvm
lvm> help

L’utilizzo può anche essere da riga di comando ed esistono 3 gruppi di comandi che si riferiscono ed agiscono rispettivamente su:

Ad esempio, per analizzare la situazione digitiamo:

pvscan  # per sapere quanti dischi fisici sono presenti
vgscan  # per sapere quanti volume group esistono
lvscan  # per sapere quante partizioni sono state create

Posso quindi creare una partizione su un disco (es. /dev/sdb1) con tipo partizione Linux LVM (codice 8e) e creare il physical volume tramite il comando:

pvcreate /dev/sdb1

Devo poi procedere con l’estensione del volume group per poter avere poi a disposizione questo ulteriore spazio da assegnare ai logical volume:

vgextend <nome-volume-group> /dev/sdb1

Ora posso procedere con l’estensione del logical volume, esempio:

lvextend /dev/<volume-group>/<logical-volume> -L +8G

Per estendere la dimensione di un volume group di 8GB.

Ora analizziamo cosa bisogna fare per poter, ad esempio, staccare un disco fisico che è caricato come physical volume.

Prima di tutto dobbiamo fare in modo che la somma totale dei =logical volume= lasci spazio almeno per togliere il physical volume. Ad esempio, se ho 2 dischi da 10GB ciascuno e voglio staccarne uno, tutte le varie partizioni create (i logical volume) sommate devono essere al massimo 10GB in modo che posso staccare tranquillamente un disco senza creare problemi.

Il comando per ridimensionare una partizione (i.e. logical volume) è il seguente:

lvresize /dev/<volume-group>/<logical-volume> -L -8GB

I comando invece per rimuovere un disco o partizione:

pvmove /dev/sdb1  # sposta i dati eventualmente presenti sul disco
vgreduce <volume-group> /dev/sdb1  # riduce la dimensione del volume group
pvremove /dev/sdb1  # rimuove il disco fisico

102.1 - La partizione EFI (ESP)

Sui computer moderni ormai abbiamo UEFI e non più BIOS.

La prima cosa che fa il computer quando si accende è cercare una partizione di tipo UEFI, ossia formattata fat32 e con i flag boot e esp.

All’interno di questa partizione va a cercare un eseguibile di tipo UEFI che è il bootloader (nel nostro caso GRUB) il quale fa partire il kernel ed il sistema operativo.

La partizione UEFI è solitamente montata sotto /boot/ ed entrandoci possiamo trovare altre cartelle fino ad arrivare ad un percorso simile al seguente: /boot/efi/EFI/.

Dove ci sono altre cartelle, una per ogni sistema operativo installato, oltre alla cartella /BOOT/ dove risiede l’eseguibile predefinito di UEFI che, dopo aver installato Linux è il bootloader di grub (anche se ha il nome di default di UEFI, tipo BOOTx64.efi).

102.2 - GRUB Legacy

GRUB fino alla versione 0.97 è chiamato GRUB Legacy.

Con i seguenti comandi:

parted -l
fdisk -l

Troviamo quale partizione ha il flag boot, ossia quale partizione contiene il bootloader GRUB che fa partire il sistema operativo.

Posso installare GRUB alternativamente con:

grub-install /dev/sda  # all'interno del MBR del disco (primi 512kb)
grub-install /dev/sda1  # su una partizione

Nel secondo caso si esegue il c.d. chainload ossia un bootloader scritto nell’MBR che lancia un secondo bootloader scritto su una partizione.

Per fare il backup del MBR prima di sovrascriverlo posso eseguire il seguente comando:

dd if=/dev/sda of=/path/to/backup-file count=1 bs=512

Quando appare il menu di GRUB posso premere e per entrare e modificare la relativa voce, premo nuovamente e per modificare la relativa linea di istruzioni.

Le varie voci del menu di GRUB sono all’interno del file /boot/grub/menu.lst.

All’inizio delle varie righe non commentate ci sono diverse istruzioni (poche) che hanno il seguente significato:

Modificando il file menu.lst naturalmente non è necessario eseguire il comando update-grub (di fatto questo comando sovrascrive le nostre personalizzazioni).

uuid può essere sostituito con root (hd0,0) prima partizione del primo disco ma, come già detto, l’individuazione di una partizione tramite UUID è più sicura e quindi preferibile.

Il comando:

cat /proc/cmdline

Mostra i parametri che sono stati passati al kernel.

Dal menu di GRUB posso digitare c per entrare in una shell minimale da cui posso, ad esempio:

grub> root (hd0,0)  # reinstallare GRUB specificando la partizione dove di trova il bootloader del SO, ossia GRUB
grub> setup (hd0)  # installare grub sul primo disco
grub> setup (hd0,0)  # installare grub sulla prima partizione del primo disco
grub> kernel /boot/vmlinux root=/dev/sda1  # specificare la root del kernel
grub> initrd /boot/initrd  # specificare l'immagine del ramdisk
grub> boot  # fare partire il SO

102.2 - Come funziona GRUB 2

Arrivato alla versione 0.97 GRUB ha smesso di essere sviluppato ed ha cambiato nome in GRUB Legacy.

Viceversa la versione attualmente in uso di GRUB deriva da GRUB2 che, a differenza di Legacy, permette la gestione di molte più situazioni quali UEFI/GPT.

Pertanto i comandi e le cartelle di GRUB2:

grub2install
grub2-mkconfig
ls /boot/grub2

Sono oggi i quelli di GRUB (che è quindi lo stesso programma):

grub-install
grub-mkconfig
ls /boot/grub/

GRUB2 conta i dischi partendo da 0 (come GRUB Legacy) ma conta le partizioni partendo da 1 (a differenza di GRUB Legacy che parte da 0).

In GRUB2 si può anche identificare il tipo di partizione, ad esempio (hd0,gpt1) oppure (hd0,msdos1).

GRUB2 supporta nativamente UEFI e le partizioni GPT.

Come per GRUB Legacy l’installazione viene eseguita con il comando:

grub2-install /dev/sda

Il file /boot/grub/grub.cfg (come viene scritto all’inizio del file):

#
#DO NOT EDIT THIS FILE
#
#It is automatically generated by grub-mkconfig using templates
#from /etc/grub.d and settings from /etc/default/grub
#

All’interno del file viene anche indicato da quale file di configurazione è stata generata la parte di codice racchiusa, ad esempio:

###BEGIN /etc/grub.d/40_custom ###
  ...codice....
###END /etc/grub.d/40_custom ###

Per approfondimenti:

102.2 - Menu di GRUB nascosto

A partire dalla Ubuntu 18 il menu di GRUB non viene visualizzato, in quanto disabilitato nelle impostazioni che si trovano all’interno del file /etc/default/grub.

Precisamente dobbiamo andare a cambiare la seguente impostazione:

GRUB_TIMEOUT_STYLE=hidden

Che non fa visualizzare il menu di GRUB (premere ripetutamente ESC al boot per visualizzarlo), con la seguente che invece rende visibile il menu di GRUB senza premere ESC:

GRUB_TIMEOUT_STYLE=menu

102.3 - librerie condivise (shared libraries)

Nel sistema operativo Windows si chiamano dll, Dybamic Link Library.

Sono dei pezzi di software condivisi che sono messi a disposizione di tutti i programmi eseguibili che vengono installati. Se un programma richiede librerie esterne per poter funzionare (dipende quindi dalla presenza di tali libreria) viene definito Dinamically Linked Executebles, se viceversa può essere eseguito senza nessuna dipendenza da librerie esterne (ossia contiene tutte le librerie necessarie al suo funzionamento compilate al suo interno) viene chiamato Statically Linked Executeble.

E’ naturale che un programma compilato insieme alle librerie necessarie per eseguirlo avrà una dimensione maggiore di un programma che invece si appoggia a librerie esterne che vengono richiamate per l’esecuzione.

Il comando =ldd= ci mostra, per ogni comando indicato come parametro, le librerie necessarie ad un eseguibile, ad esempio:

ldd /bin/ln /bin/ls

Mostra le librerie necessarie per eseguire rispettivamente il comando ln ed ls.

Le librerie sono file che hanno generalmente estensione .so (shared object) e che si trovano in alcune cartelle tra le quali troviamo sempre le seguenti:

Quando viene installato un programma questo cerca nelle cartelle dove si trovano le librerie per vedere se sono già presenti quelle necessarie per il suo proprio funzionamento e, se non le trova, avvisa che sono necessarie e vanno installate (oppure, più spesso, il gestore dei pacchetti della nostra distribuzione si preoccupa di installarle).

Il file /etc/ld.so.conf contiene altri percorsi dove cercare se sono presenti altre librerie (viene inserito un percorso per ogni riga) pertanto può capitare che alcune applicazioni aggiungano la riga relativa al percorso dove installano le librerie necessarie al proprio funzionamento per future applicazioni che verranno installate.

Il comando ldconfig aggiorna tali percorsi nella cache leggendo, appunto, il file di configurazione /etc/ld.so.conf.

Il comando ldconf -p stampa invece a video l’elenco di tutte le librerie con il loro percorso.

Un modo meno invasivo e più rapido per aggiungere uno o più percorsi in cui cercare le librerie (senza dover cambiare il contenuto del file /etc/ld.so.conf e senza dover aggiornare la cache dei percorsi con il comando ldconfig) è configurare la seguente variabile d’ambiente:

export set $LD_LIBRARY_PATH="/lib2:/lib3:/lib4"

I percorsi inseriti nella variabile vengono probabilmente letti prioritariamente permettendo, ad esempio, di copiare una libreria nel percorso specificato e modificarla per fare lavoro di sviluppo e tornare alla variabile originaria semplicemente azzerando la variabile d’ambiente.

102.4 - dpkg : installare e gestire pacchetti .deb

I pacchetti con della distribuzione Debian e derivate hanno estensione .deb.

Generalmente per scaricare un pacchetto dobbiamo conoscere l’architettura del nostro sistema, per conoscerla possiamo digitare il seguente comando:

uname -a

Da cui possiamo capire se l’architettura è a 32 bit (x86) oppure a 64 bit (x86_64).

Alcuni comandi utili:

dpkg -i <nome-pacchetto>  # per installare un pacchetto
dpkg --info <nome-pacchetto>  # per avere ulteriori informazioni sul pacchetto, tra cui le varie dipendenze
dpkg --listfiles <nome-pacchetto>  # elenca i file contenuti nel pacchetto
dpkg -S <percorso-file>  # fornisce il nome del pacchetto da cui è stato scaricato il file indicato
dpkg -l  # fornisce la lista di tutti i pacchetti installati sul sistema
dpkg -l tz*  # filtra i pacchetti che iniziano con tz
dpkg -r <nome-pacchetto>  # rimuove il pacchetto mantenendo i file di configurazione
dpkg --purge <nome-pacchetto>  # elimina anche i file di configurazione

Il seguente comando, quando previsto dal pacchetto, procede alla sua riconfigurazione:

dpkg-reconfigure <nome-pacchetto>

Proviamo a capire da quale pacchetto (e quale versione) è collegato il comando java:

which java  # per vedere il percorso completo del comando

E’ probabile che il nome del comando sia in realtà un link simbolico ad un file, in questo caso un link a qualcosa del tipo /etc/alternatives/java per cui, per vedere il percorso di default collegato al comando java, eseguo:

update-alternatives --display java

E quindi posso digitare:

dpkg -S <percorso-java>

E capire a quale pacchetto appartiene il comando.

Come sempre risulta utile leggere il file man del comando =dpkg= per scoprire le varie opzioni e le varie funzionalità.

102.4 - apt-get e aptitude

Il gestore di pacchetti di Debian è apt, questo comando, a differenza di dpkg, gestisce anche le dipendenze.

apt-cache

Iniziamo con vedere qualche esempio del comando apt-cache il quale esegue delle query sui pacchetti:

apt-cache search <stringa>  # cerca pacchetti che hanno nel titolo o nella descrizione 'stringa'
apt-cache showpkg <pacchetto>  # mostra informazioni sul pacchetto
apt-cache stats  # mostra statistiche
apt-cache unmet  # visualizza le dipendenze non soddisfatte (probabilmente tutti i pacchetti che vengono elencati hanno una dipendenza più recente a sostituirli e quindi va tutto bene)
apt-cache depends <pacchetto>  # mostra le dipendenze del pacchetto (i pacchetti di cui necessita per essere installato
apt-cache rdepends <pacchetto>  # mostra le dipendenze inverse (i pacchetti che necessitano del pacchetto in query per essere installati)
apt-cache pkgnames <stringa>  # mostra i pacchetti il cui nome inizia per 'stringa'

apt utilizza dei repository per fornirci tutte le informazioni di cui sopra, questi repository si trovano all’interno del file /etc/apt/sources.list e vengono aggiornati tramite il seguente comando:

apt-get update  # aggiorna le cache dei repository
apt-get upgrade  # aggiorna il sistema

apt-get

Ora vediamo esempi relativi al comando apt-get:

apt-get install <pacchetto>  # installa anche le dipendenze e vengono segnalati anche i pacchetti suggeriti e raccomandati
apt-get -s install <pacchetto> # fa la simulazione dell'installazione
apt-get install -d <pacchetto>  # scarica solamente i pacchetti nella directory `/var/cache/apt/archives`
apt-get clean  # per eliminare tutti i file dalla cache
apt-get remove <pacchetto>  # disinstalla il pacchetto
apt-get autoremove <pacchetto>  # rimuove anche le dipendenze che non sono più necessarie
apt-get autoremove --purge <pacchetto>  # vengono rimossi anche i file di configurazione (nota l'asterisco accanto al nome del pacchetto)

aptitude

aptitude è un pacchetto più completo e funziona anche tramite interfaccia =ncurses=:

aptitude searche <stringa>
aptitude install <pacchetto>
aptitutde remove <pacchetto>
aptitude  # senza parametri lancia la console

102.4 - apt

In sostituzione di apt-cache, apt-get e aptitude ora abbiamo il solo comando apt che risulta essere più semplice da gestire:

apt  # lanciato da solo da le informazioni sui principali utilizzi
apt search <stringa>  # cerca tra i nomi dei pacchetti e le descrizioni
apt show <pacchetto>  # mostra informazioni su un pacchetto
apt install <pacchetto>  # installa un pacchetto e le sue dipendenze
apt list <stringa>  # mostra i nomi dei pacchetti che rispettano i parametri di ricerca di 'stringa'
apt update  # aggiorna la cache dai repository
apt upgrade  # aggiorna il sistema
apt full-upgrade  # differenza di upgrade rimuove anche le versioni precedenti e le dipendenze non più richieste

102.5 - Guida a yum e rpm

Sulle distribuzioni RedHat, CentOS, Fedora, SuSe e derivate si usano pacchetti rpm.

I pacchetti rpm sono archivi cpio:

rpm2cpio <pacchetto> > <file>  # crea un file che contiene tutti i file del pacchetto
cpio -i --make-directories > <file>  # crea tutti file contenuti nel file precedente (anche le directory e le sottodirectory) rispetto alla directory root del sistema

rpm

rpm, come dpkg per Debian non risolve le dipendenze e non scarica i pacchetti ma installa solo quelli già scaricati, vediamo qualche esempio:

rpm -qpi <pacchetto>  # (q)uery (p)ackage (i)nformations: informazioni sul pacchetto
rpm -qpl <pacchetto>  # (q)uery (p)ackage (l)ist: lista dei file contenuti nel pacchetto
rpm -ih <pacchetto>  # installa il pacchetto
rpm -qa  # (q)uery (a)ll: fa l'elenco di tutti i pacchetti installati
rpm -qi <pacchetto>  # (q)uery (i)nformations: informazioni sul pacchetto installato
rpm -ql <pacchetto>  # (q)uery (l)ist: lista dei file di un pacchetto installato
rpm -qf <percorso-file>  # (q)uery (f)ile: restituisce il pacchetto da cui è stato installato un file
rpm -e <pacchetto>  # (e)rase: disintalla un pacchetto

yum

yum è il gestore dei pacchetti tipo apt per Debian ossia può gestire repository online e gestire le dipendenze. E’ stato definitivamente sostituito da dnf che, per la maggior parte dei casi, mantiene la stessa sintassi. Vediamo qualche esempio:

yum -help  # stampa del testo di aiuto
yum search <stringa>  # cerca una stringa all'interno dei nomi e delle descrizioni dei pacchetti
yum provides <file>  # ci dice quali pacchetti contengono il file
yum info <pacchetto>  # restituisce informazioni sul pacchetto
yum check-update  # come 'apt-get update'
yum update  # come 'apt-get upgrade'
yum list  # elenca tutti i pacchetti
yum list <stringa>  # cerca i pacchetti con la stringa nel nome
yum install <pacchetto>  # installa un pacchetto
yum provides <percorso-file>  # restituisce da quale pacchetto è stato installato un file
yum remove <pacchetto>  # disinstalla un pacchetto

Vi sono poi delle installazioni c.d. “di gruppo”, ossia che raggruppano più pacchetti per installare un ambiente, ad esempio un Desktop Manager oppure tutto il necessario per un Server Web:

yum grouplist  # visualizza tutti i gruppi di applicazioni (es. "Server Web")
yum groupinstall "Server Web"  # installa tutto il necessario per ottenere il risultato, in questo caso un Server Web

102.5 - DNF e zypper

Aggiornamento del corso: dnf ha sostituto definitivamente yum e, come detto, ha praticamente la stessa sintassi:

dnf search <stringa>  # cerca una stringa all'interno del nome e della descrizione dei pacchetti
dnf install <pacchetto>  # installa un pacchetto
....

Al posto di yumdownloader è stato invece introdotto zypper che può fare tantissime cose, ad esempio:

zypper search <stringa>  # cerca una stringa nel nome e nella descrizione dei pacchetti e le espone con un output più leggibile
zypper download <pacchetto>  # fa il download del pacchetto nella cache ('~/.cache/zypp/packages/...')

102.6 - Virtualizzazione vs Containerizzazione

La virtualizzazione avviene tramite un Hypervisor che espone alle macchine virtuali l’hardware virtualizzato, su ogni macchina virtuale troviamo quindi binari/librerie e applicazioni.

Nella containerizzazione il Container Daemon non virtualizza un intero sistema operativo ma solamente tutto quanto necessario all’esecuzione della singola applicazione.

Vediamo ora alcune differenze.

Alcune ulteriori informazioni sulle differenze:

102.6 - IaaS, PaaS, SaaS e concetti

Per meglio chiarire il significato delle sigle menzionate vediamo l’immagine seguente dove si evidenzia cosa viene gestito dal provider e cosa viene “autogestito” passando progressivamente da TUTTO a NULLA.

Alcuni termini:

Proviamo a chiarire con una immagine dove si vedono notebook degli utenti che visitano un sito web che accede ad una struttura tipo IaaS, la nuvola che rappresenta internet, il load balancer (che gestisce il carico di lavoro sui vari server) ed i server stessi:

102.6 - Clonazione di macchine virtuali e accorgimenti

Una volta creata una macchina virtuale posso installare varie applicazioni e potrei volerla clonare tale e quale in modo da non dover ripetere le stesse configurazioni oppure potrei volerla esportare per poterla utilizzare in altro contesto magari con alcune configurazioni specifiche e diverse da quelle attuali.

clonazione

In caso di clonazione della macchina devo porre attenzione ad alcuni aspetti che, post-clonazione, devo personalizzare sulla macchina clonata quali:

esportazione

In caso di esportazione della macchina virtuale viene creato un file *.ova che, in fase di importazione da parte di Virtualbox chiede alcune informazioni per personalizzare l’importazione (ad esempio chiede il n. di GB di RAM da mettere a disposizione o il n. di core del processore) come se si stesse creando la macchina virtuale ma, una volta creata, questa avrà già installati tutti i programmi e le personalizzazioni che erano state fatte prima della sua esportazione.

cloud-init

Questo è uno strumento per personalizzare per istanze di cloud.

Ad esempio Canonical (Ubuntu) permette di creare uno script di personalizzazione che viene eseguito ad ogni clonazione di una macchina virtuale. A esempio si può impostare che il sistema venga aggiornato all’ultima versione dei pacchetti (tramite apt), che vengano installati alcuni di essi e personalizzati alcuni file (come ad esempio l’hostname) in modo da facilitare le operazioni di post-clonazione citate sopra.

Test pratico 1 : Test sul capitolo 102

Lezioni da n. 20 a n. 38.

Risposte giuste: 12 su 15.

Sezione 4 : 102 - Comandi GNU e Unix3.

103.1 - cambiare shell

Il terminale (ambiente grafico) o la console (tty) sono aperte su una shell.

Posso sapere su quale shell sto operando consultando la variabile $SHELL che mi restituisce il percorso della shell in uso, ad esempio /bin/bash.

Nel file /etc/passwd è indicato all’ultimo campo la shell di default dell’utente.

Si può entrare in una shell ‘al volo’ digitandola, ad esempio digitando sh, bash, ksh, e le altre shell disponibili.

Per conoscere le shell disponibili sul sistema digito il seguente comando chsh -l che sfortunatamente nel mio caso non funziona ma suggerisce di consultare il file /etc/shells che naturalmente è modificabile da root.

Per cambiare la propria shell di default un utente può digitare il seguente comando:

chsh -s <percorso-nuova-shell>

Il comando richiede la password dell’utente per poter procedere a modificare (con privilegi amministrativi) il file /etc/passwd variando (appunto) la shell di default (ultimo campo).

All’utente amministratore root è sufficiente invece digitare:

chsh -s <percorso-shell> <nome-utente>

L’esecuzione del comando in questo caso non richiede nessuna password di conferma.

103.1 - entrare e uscire dalla shell

In linux abbiamo più modi per entrare ed uscire da una shell:

Digitando il comando ps le shell aperte sono individuate come rispettivamente:

Per uscire da una shell abbiamo le seguenti opzioni:

103.1 - Linux Bash Shell : Le variabili

In bash abbiamo alcune variabili d’ambiente come ad esempio $USER oppure $PATH.

Per visualizzarle tutte digitare doppio TAB dopo avere digitato il carattere $ in quanto funziona il completamento automatico.

E’ buona abitudine impostare le nostre variabili in minuscolo per non andare a confliggere con le variabili d’ambiente che sono sempre in maiuscolo.

Vediamo qualche comando ed il suo significato:

env  # mostra le principali variabili d'ambiente ed il loro contenuto
nomevariabile="contenuto-assegnato"  # imposta il valore di una variabile

La variabile impostata in questo modo è visibile solo nella sessione della shell in cui viene assegnata (stesso PID). Per poterla rendere visibile anche nelle sessioni figlie dobbiamo esportarla, operazione che può essere fatta anche in fase di assegnazione:

export <nome-variabile>  # rende accessibile la variabile anche nelle shell figlie
export nomevariabile="contenuto-assegnato"  # esportazione di una variabile in fase di assegnazione del suo valore

Il seguente comando:

ps --forest

Mostra l’albero delle shell figlie nel terminale dove lancio il comando. Per rimuovere il valore assegnato ad una variabile e renderla così ‘vuota’ utilizzo il seguente comando:

unset nome-variabile

Per vedere tutte le opzioni di shell digitiamo il seguente comando:

set -o

Quando c’è il segno + significa che l’opzione è disattivata, mentre quando ho il segno - significa che l’opzione è attiva.

Alcune opzioni:

La variabile $SHELLOPTS contiene le opzioni di shell attive.

103.1 - Bash Shell : gli alias

type ls  # ci dice che tipo di eseguibile è il comando 'ls'
alias ls  # ci dice quale alias corrisponde ad 'ls'
alias  # mostra tutti gli alias attivi
\ls  # esegue il comando 'ls' e non il suo alias
unalias ls  # elimina l'alias 'ls'
alias ls='ls --color'  # definisce nuovamente l'alias 'ls'

Per rendere le modifiche permanenti queste devono essere inserite nel file di configurazione <homedir>/.bashrc che viene caricato ogni volta che faccio il login in una shell.

Il comando shopt invece mostra altre impostazioni della shell tra cui expand_aliases che è generalmente in stato on. Questo significa che gli alias che sono configurati verranno utilizzati.

Per non utilizzare gli alias configurati posso disabilitare questa opzione con il seguente comando:

shopt -u expand_aliases

Per riattivare l’opzione digito invece il comando seguente:

shopt -s expand_aliases

103.1 - La bash history

L’elenco dei comandi della =history= si trova nel file ~/.bash_history.

history  # elenca i comandi della history dal meno recente al più recente
!20  # esegue nuovamente il comando contrassegnato con il n. 20
!$  # corrisponde all'ultimo parametro passato al comando precedente
!!  # esegue nuovamente l'ultimo comando presente nella history
!apt  # esegue nuovamente l'ultimo comando che iniziava con apt

Per eseguire ricerche nella history premo la combinazione di tasti Ctrl-r che esegue una ricerca progressiva a ritroso nella history man mano che digito i caratteri cercando quanto digitato all’interno dei vari comandi presenti nella history.

Durante la ricerca posso premere:

Per azzerare la history eseguo il seguente comando:

history -c

In una shell di tipo bash è attivata di default l’opzione histexpand che permette l’interpretazione (espansione) del carattere !:

set +o histexpand  # per disattivare l'opzione
set -o histexpand  # per riattivarla

Per visualizzare le varie opzioni presenti nella propria sessione di shell:

shopt  # per visualizzare tutte le variabili ed il loro stato (on/off)
echo $BASHOPTS  # per visualizzare solo le opzioni attive

Il comando che inizia con uno SPAZIO viene eseguito ma non viene registrato nella history.

103.1 - man e uname

Il comando uname fornisce informazioni sul sistema:

uname --help  # chiarisce quali informazioni fornisce a seconda delle opzioni utilizzate
uname -a  # fornisce praticamente tutte le informazioni
uname -r  # fornisce la versione del kernel

L’ultimo comando risulta utile da espandere all’interno di qualche istruzione dove (appunto) è necessaria la versione del kernel in uso come nell’esempio seguente:

apt install linux-headers-$(uname -r)

Il comando man visualizzare il manuale di qualsiasi comando linux che ne abbia uno abbinato.

Non tutti i comandi hanno un manuale e quando esiste è generalmente più completo ed esaustivo dell’utilizzo dell’opzione –help.

All’interno di man possiamo cercare qualcosa premendo il carattere / e digitando una stringa di ricerca seguita dalla pressione del tasto INVIO poi possiamo premere n per passare al risultato successivo oppure N per passare a quello precedente.

Per uscire dalle pagine del manuela premiamo q.

which <comando>  # fornisce il percorso all'eseguibile di un comando
whereis <comando>  # oltre all'eseguibile ci dice anche dove si trova il file di man

103.1 - Quoting in bash

I metodi di quoting sono 3 e sono interscambiabili a seconda dei casi e delle necessità.

Il simbolo di escape (**) fa in modo che il carattere immediatamente successivo ad esso non venga interpretato dalla shell.

Ad esempio posso creare una directory con un nome che contiene degli spazi come segue:

mkdir nome\ directory

Gli apici singoli (') fanno in modo che ciò che è contenuto tra di essi non venga interpretato in alcun modo da bash e venga riportato tale e quale.

I doppi apici (") invece interpretano solamente i seguenti caratteri:

Non interpreta invece tutti gli altri caratteri quali, ad esempio, i seguenti: ><|&;:[]{}.

Alcuni esempi su quanto visto sopra:

echo $USER
echo '$USER'
echo $(date)
$ echo `date`
$ echo '`date`'
echo "$USER mi deve $5"
echo "$USER mi deve \$5"

Per approfondimenti: https://www.gnu.org/software/bash/manual/html_node/Quoting.html

103.2 - expand e unexpand

I comandi expand e unexpand servono a uniformare all’interno di un file l’utilizzo degli spazi o del tabulatore.

Se, inserendo del testo o del codice in un file, utilizzo la barra spaziatrice più volte oppure utilizzo il tabulatore non ho apparentemente nessuna differenza da un punto di vista visivo anche se i caratteri utilizzati non sono gli stessi.

Per poter visualizzare l’utilizzo di un carattere nascosto (es. il tabulatore) posso utilizzare l’opzione -A del comando cat, ad esempio:

cat -A <nome-file>

Ora vediamo l’utilizzo dei comandi citati in concreto:

expand <file> > <file-expanded>  # sostituisce agli spazi le tabulazioni
unexpand <file> > <unexpanded-file>  # sostituisce alle tabulazioni gli spazi

103.2 - dividere e unire file con split e cat

A volte può essere utile dividere un file di testo in più righe oppure spezzare un file binario eccessivamente grosso per poi riunirlo in un secondo momento (magari per inviarlo via email a pezzetti).

Per fare gli esempi utilizzeremo un file di testo creato con il seguente comando:

for l in $(seq 4500) ; do  echo "riga numero $l" >> textfile.txt ; done
wc -l textfile.txt  # verifica che il file abbia 4500 righe

split ha la funzione di dividere un file in piccoli file, vediamo come:

split textfile.txt  # suddivide il file in più file di 1000 righe ciascuno (l'ultimo ne avrà 500)
wc -l x*  # verifica numero di righe in ogni file creato ed il loro nome (che inizia per 'x')
split textfile.txt -l 100  # specifica di quante righe deve essere ogni file
cat x*  # stampiamo a video l'elenco di tutti i file come fosse una ricostituzione del file originale
split textfile.txt -l 100 <nome.file_>  # specifica come devono iniziare i nomi dei file spezzati (a cui split aggiunge la numerazione 'aa', 'ab', ...)

Per dividere un file binario qualsiasi (archivio, video, audio o altro) eseguiamo:

split -b 5m nomebinario.mp4

Dove l’opzione -b specifica che si tratta di un file binario e 5m specifica la dimensione di ogni file spezzettato.

Se provo a digitare il comando:

file *

Posso osservare che solo il primo file della sequenza spezzata contiene l’header che descrive il contenuto del file (multimedia) in quanto gli altri pezzi non sono identificabili. I file così divisi non sono accessibili fino a che non vengono riuniti con il seguente comando:

cat <files-da-concatenare> <file-riunito>

103.2 - Le meraviglie di cut

Il comando cut ritaglia in base alle colonne un file di testo:

cut -c2 <nome-file>  # visualizza la colonna 2 di ogni riga di un file
cut -c12-15 <nome-file>  # visualizza un range di colonne
cut -c12- <nome-file>  # visualizza da una colonne fino alla fine di ogni riga
cut -c-11 <nome-file>  # visualizza dall'inizio della riga fino ad un carattere

Quando abbiamo un file in cui ogni riga vi sono dei campi con un determinato separatore di campo, tipo nel file /etc/passwd posso specificare il delimitatore con il seguente comando:

cut -d':' -f1 <nome-file>

Che visualizza il primo campo (o field) con l’opzione -f1 e specifica come delimitatore il relativo carattere :.

Posso ottenere la stessa cosa utilizzando la redirezione del flusso come segue:

cat <file> | cut -d':' -f1

La redirezione dell’output può essere fatta con qualsiasi comando che accetti un flusso dallo standart input. Siccome cut produce un flusso dallo standard output posso anche ordinarlo aggiungendo il relativo comando:

cat <file> | cut -d':' -f1 | sort

Per visualizzare più campi:

cat <file> | cut -d':' -f1,7  # visualizza le colonne 1 e 7
cat <file> | cut -d':' -f1-3  # visualizza le colonne 1,2,3

Se alcune righe del file hanno il carattere separatore ed altre no, quelle che ne sono sprovviste vengono visualizzate per intero. Aggiungendo il parametro -s specifico che, qualora non esista il separatore dei campi indicato, non venga visualizzato la relativa linea:

cat <file> | cut -d':' -s -f1,7

L’opzione --complement permette di stampare gli altri campi che non sono stati filtrati, esempio:

cat <file> | cut -d':' --complement -s -f1,7

Visualizza tutti i campi ad eccezione del campo 1 e 7.

Infine posso anche specificare un mio delimitatore personalizzato da utilizzare in fase di output:

cut -d':' -s -f1,7 --output-delimiter=' # ' <file>

103.2 - Formattare file di testo con fmt

Il comando fmt serve a riformattare un file di testo:

cat <file> | fmt  # formatta il testo a 75 colonne andando a capo con la parola intera ma allunga le righe corte, sempre entro le 75 colonne
cat <file> | fmt -w 30  # specifica il numero delle colonne (se una parola singola ha più caratteri la lascia da sola e sfora il parametro)
fmt -w 1 <file>  # di conseguenza visualizza parola per parola

Ad esempio possiamo provare a rendere leggibile dmesg tramite:

sudo dmesg | fmt -w 100  # va a capo con la parola intera ma dove il comando è corto riempie la riga con il comando successivo
sudo dmesg | fmt -w 100 -s  # dove il file originale va a capo lo forza anche nell'output
sudo dmesg | fmt -w 100 -s | less  # il file è anche scorribile e ricercabile utilizzando le funzioni di less

103.2 - head e tail

Comandi essenziali per vedere i log su Linux: head e tail.

Per vedere i primi messaggi (quelli più vecchi), la testa (‘head’) del file:

sudo head /var/log/messages  # vede le prime 10 righe di default
sudo head /var/log/messages -n 5  # vede le prime 5 righe

Alla stessa maniera tail mostra la fine (coda o ’tail’) di un file:

sudo tail /var/log/messages  # vede le ultime 10 righe di default
sudo tail /var/log/messages -n 5  # vede le ultime 5 righe

L’opzione follow aggiunta al comando tail permette di seguire in tempo reale l’aggiunta di nuove righe di log al file indicato:

sudo tail -f /var/log/messages  # se il file non esiste da errore
sudo tail -F /var/log/messages  # segue il file anche se non esiste avvisando

Se passo al comando tail 2 file il comando li segue entrambi:

tail -f <file-1> <file-2>  # avvisandoci ogni volta quale file è stato modificato
tail -f -q <file-1> <file-2>  # l'opzione '-q' non specifica ogni volta quale file viene modificato ma visualizza solo la modifica

103.2 - Comando tr in GNU/Linux

Il comando tr lavora con i caratteri, l’utilizzo principale è nella seguente forma:

cat <text-file> | tr <SET1> <SET2>

Il comando sostituisce ai caratteri di ‘SET1’ con i caratteri di ‘SET2’.

La prima e seconda stringa devono contenere lo stesso numero di caratteri e questi sono confrontati uno alla volta: il primo carattere di ‘SET1’ viene sostituito dal primo carattere di ‘SET2’ e così via.

Nel caso ‘SET2’ contenga un maggior numero di caratteri verranno ignorati quelli che eccedono, non è chiaro invece il comportamento in caso di maggiore lunghezza della stringa ‘SET1’.

Esempio di altri utilizzi:

cat <text-file> | tr [:lower:] [:upper:]  # visualizza il file con tutti caratteri maiuscoli
cat <text-file> | tr [:lower:] [:upper:] > <new-file>  # crea 'new-file' con tutti caratteri maiuscoli
cat <text-file> | tr -d '.'  # elimina il carattere indicato '.'
cat <text-file> | tr -d '.d'  # elimina i due caratteri indicati '.' e 'd'
cat <text-file> | tr -dc '.'  # elimina tutto tranne il carattere indicato '.' (funzione complementare)
cat <text-file> | tr -dc [:print:]  # elimina tutto i caratteri non stampabili (tutti quelli nascosti, compreso a capo riga)
cat <text-file> | tr -s ' '  # riduce ad un solo spazio/carattere quando ve ne sono più di uno consecutivi
cat <text-file> | tr -sd <SET1> <SET2>  # elimina i caratteri di 'SET1' e riduce a 1 solo i caratteri di 'SET2'

Per ottenere informazioni sul funzionamento di tr possiamo, come spesso accade, attingere a diverse fonti:

tr --help
info tr
man tr

103.2 - Comando nl e wc

nl

nl <file>  # mette un contatore di riga a sinistra di ogni riga
cat <file> | nl > <nuovo-file>  # come sopra e salva tutto in un 'nuovo-file'
nl -i2 <file>  # incrementa di 2 il contatore ogni riga
nl -s. <file>  # mette un punto ('.') dopo il contatore
nl -s'> ' <file>  # mette la stringa tra gli apici dopo il contatore
nl -w2 <file>  # mette il contatore alla seconda colonna (default 6)
nl -nrz <file>  # mette gli zero davanti alla numerazione

wc

wc <file>  # restituisce numero righe, parole e caratteri (byte)
cat <file> | wc  # come sopra
wc -w <file>  # restituisce il numero di parole
wc -L <file>  # restituisce il numero di caratteri di cui è composta la riga più lunga
wc -c <file>  # restituisce il numero di caratteri (o byte)

103.2 - less, sort, uniq

sort <file>  # ordinamento alfabetico delle righe
cat <file> | sort  # ordina il file esattamente come sopra
sort -r <file>  # ordinamento alfabetico inverso, da non confondere con
sort -R <file>  # ordinamento casuale
du -s /home/max/* | sort -n  # ordina per numero crescente (quello all'inizio della riga)
sort -k 2,2 <file>  # ordina in base al secondo campo (campi separati da spazi)
sort -k 2 <file>  # ordina la riga a partire dal secondo campo (non considera il primo campo nell'ordinamento)
cat <file> | sort | uniq  # mostra solo valori di riga unici (non mostra i duplicati consecutivi, ecco perchè è preceduto da 'sort')
cat <file> | sort | uniq -d  # mostra solo le righe che presentano duplicati consecutivi
cat <file> | sort | uniq | wc -l  # conta i valori unici del file

103.2 - paste, od, pr

paste

paste <file>  # come 'cat' visualizza il contenuto del file
paste -s <file>  # sopprime il carattere '\newline' visualizzando tutto su una riga e separando con una tabulazione le righe
paste -s <file> -d,  # separa le righe del file originale con il carattere ','
cat <file> | paste - -  # accorpa 2 righe per volta su 2 colonne (utile per righe originarie corte ovviamente)
cat <file> | paste - - -  # accorpa 3 righe per volta su 3 colonne
cat <file> | paste - - - -d,:-  # accorpa 3 righe per volta su 3 colonne ed imposta il separatore che viene utilizzato in sequenza (prima ',' poi ':' poi '-' e poi di nuovo da capo)

od

od -b <file>  # rappresenta il file in ottale (octal dump)
od -x <file>  # rappresentazine in esadecimale

pr

Il comando pr è un impaginatore per la stampa o visualizzazione di un file di testo:

pr <file>  # impagina un file mettendo anche un'intestazione con DATA NOME-FILE N-PAGINA

A titolo di esempio, una volta configurato numero di righe e colonne ottimali, è possibile visualizzare un file di testo decentemente formattato tramite less (utilizzare Pag-Up e Pag-Down), digitando quanto segue:

cat <file> | fmt -w 78 -s | ts '  ' | pr -W 82 -l 45 | less

103.2 - Introduzione a sed

Tra le risorse della lezione vi è un link alla playlist Corso base SED - Manipolare testo, input e output shell in cui vi sono n. 8 lezioni dedicate al comando =sed= che vanno seguite prima di fare la lezione stessa.

lezione 1: Sintassi Espressioni Regolari

Le espressioni regolari sono uno standard POSIX/UNIX che ci permettono di individuare un pattern di testo, ad esempio:

https://regexr.com : è un sito dove ci si può esercitare a scrivere le espressioni regolari.

Ad esempio per selezionare in un testo sia la parola color che colour:

Significato dei caratteri:

L’utilizzo delle parentesi quadre [] significa individuare uno dei caratteri indicati:

Vi sono 2 simboli speciali (tra tanti):

All’interno delle parentesi tonde si possono individuare singoli =pattern= e valutarli insieme ad altri:

lezione 2: SED | sostituire una parola

sed è uno stream editor, ossia individua dei pattern di testo in un file per poi esegue su di essi delle istruzioni e dei comandi che gli impartiamo.

Vediamo come sostituire una parola:

echo "giorno" | sed 's/giorno/notte/'
echo "giorno 2" | sed 's/giorno/notte/'
echo "buon giorno" | sed 's/giorno/notte/'
echo "sunday" | sed 's/day/night/'

Le espressioni regolari servono proprio per identificare esattamente la parola in modo da evitare, per esempio, che nel caso sia contenuta in altre parole si eviti di sostituirla. Proseguiamo con gli esempi:

echo "buon giorno giorno" | sed 's/giorno/notte/'

Il comando che precede sostituisce solamente la prima occorrenza di ‘giorno’. sed infatti lavora i file riga dopo riga ed alla prima occorrenza fa la sostituzione e passa alla riga successiva.

Quindi quando ci sono più occorrenze sulla stessa riga non effettua la sostituzione dalla seconda occorrenza a meno che non venga istruito per farlo “globalmente” aggiungendo il carattere g= alla fine dell’espressione:

echo "buon giorno giorno" | sed 's/giorno/notte/g'

Il comando =sed= ha quindi una sintassi strutturata come segue:

Naturalmente posso lavorare con i file da redirigere nello standard input di sed:

sed 's/one/ONE/' < <file>  # sostituisce solamente la prima ricorrenza di ogni riga e visualizza il file con le sostituzioni effettuate

lezione 3: SED | i separatori, scrivere file

Per utilizzare nelle stringhe di ricerca o sostituzione il carattere separatore delle direcotry in linux (/) devo farlo precedere dal simbolo di ’escape’ (\) altrimenti sed lo interpreterebbe come parte del comando che gli abbiamo impartito.

Ipotizzando di voler cambiare il percorso /usr/bin/bash con /usr/bin/sh all’interno di un file denominato =percorsi=:

sed 's/\/usr\/bin\/bash/\/usr\/bin\/sh/' < percorsi

Quando ci sono queste situazioni (la riga diventa difficilmente leggibile ed anche di difficile scrittura), all’interno del comando impartito a sed, in sostituzione del separatore / posso utilizzare altri caratteri come : oppure | come segue:

sed 's:/usr/bin/bash:/usr/bin/sh:' < percorsi
sed 's|/usr/bin/bash|/usr/bin/sh|' < percorsi

Se voglio salvare il risultato in un ’nuovo-file’ posso redigere anche l’output di sed:

sed 's:/usr/bin/bash:/usr/bin/sh:' < percorsi > nuovo-file

lezione 4: SED | aggiungere apici o parentesi

echo "ciao mamma" | sed s/'[a-z]*/(&)/g'

L’espressione che precede mette tra parentesi tonde tutte le occorrenze incontrate:

Per abilitare le espressioni regolari estese in sed devo usare il parametro -r:

echo "Ciao mamma" | sed 's/[a-z]*/(&)/g'
echo " Ciao mamma" | sed 's/[a-z]*/(&)/g'
echo "Ciao mamma" | sed -r 's/[a-z]+/(&)/g'
echo "Ciao mamma" | sed -r 's/([a-z]|[A-Z])+/(&)/g'

Posso quindi utilizzare il simbolo + (una o più ricorrenze in quanto il simbolo =*= indica zero o più ricorrenze).

Le parentesi tonde possono essere utilizzate lo stesso anche senza l’abilitazione delle espressioni regolari estese ma devo farne l’escape: \( e \).

lezione 5: SED | duplicare una parola

Allo stesso modo l’utilizzo delle espressioni regolari estese mi è utile per la duplicazione di una parola:

echo "123 abc" | sed 's/[0-9]*/& &/'
echo " 123 abc" | sed 's/[0-9]*/& &/'
echo " 123 abc" | sed -r 's/[0-9]+/& &/'

lezione 6: SED | invertire parole; le espressioni regoleri

L’utilizzo delle parentesi, come detto in precedenza, crea un pattern identificato da sed con un numero progressivo (da 1 per il primo pattern definito fino a 9 massimo) che viene richiamato nella parte della sostituzione con il numero preceduto dal simbolo di escape \:

echo "123abc" | sed -r 's/([0-9]+)([a-z]+)/\2\1/'
echo "123abc" | sed -r 's/([0-9]+)([a-z]+)/\2 \1/'
echo "123 abc" | sed -r 's/([0-9]+)([a-z]+)/\2\1/'
echo "123 abc" | sed -r 's/([0-9]+)|([a-z]+)/\2@ \1/'
echo "123abc" | sed -r 's/([0-9]+)([a-z]+)/\2@\1/'
echo "123 abc" | sed -r 's/([0-9]+)([a-z]+)/\2@\1/g'
echo "123abc" | sed -r 's/([0-9]+)([a-z]+)/\2@\1/g'

lezione 7: SED | eliminare numeri o testo o cambiarne l’ordine

Tramite l’utilizzo dei pattern e delle espressioni regolari estese posso eliminare numeri, testo o cambiarne l’ordine (come visto nella lezione precedente):

echo "abc123" | sed -r 's/([a-z]+).*/\1/'
echo "abc123" | sed -r 's/([a-z]+)(.*)/\2 \1/'
echo "abc123" | sed -r 's/([a-z]+)(.*)/\2/'

lezione 8: SED | eliminare parole duplicate

Il riferimento al =pattern= può anche essere inserito nella stringa di ricerca, ad esempio per trovare le parole doppie (inteso come due parole identiche separate da uno spazio) ed eliminarne una:

echo "ciao ciao" | sed -r 's/([a-z]+) \1/\1/'

LEZIONE DEL CORSO

Strumento per il debug delle espressioni regolari al seguente LINK: https://regex101.com .

sed -n '1,5 p' <file>  # stampa le righe da 1 a 5 sopprimendo l'output del file intero (opzione '-n')
sed -n '5,$ p' <file>  # come sopra ma dalla riga 5 alla fine del file
sed -i .$(date +%F) '/^#/d;' <file>  # l'opzione '-i' modifica direttamente 'file' e se, come nel caso esposto, viene aggiunta un'estensione, viene creata una copia del file appena lavorato

103.2 - zcat, bzcat, xzcat

Per visualizzare file di testo compressi non è necessario decomprimerli ma, a seconda del tipo di compressione, possiamo utilizzare il relativo comando che si comporterà esattamente come cat ma visualizzerà il contenuto del file compresso (utile ad esempio per visualizzare il contenuto dei file nella directory /var/log):

103.2 - md5sum, sha256sum, sha512sum

Per calcolare l’integrità di un file possiamo vedere se corrisponde il suo hash che è una rappresentazione alfanumerica di un file calcolata tramite un algoritmo. Possiamo avere diversi modi per calcolare hash di un file:

md5sum <nome-file>
sha256sum <nome-file>
sha512sum <nome-file>

103.3 - Copiare, spostare, creare, rinominare file da shell

Il comando touch aggiorna la data dei dati di accesso di un file ed è generalmente utilizzato per creare dei file vuoti:

touch file{1,2,3}  # crea 'file1', 'file2' e 'file3'
touch file{1...3}  # come il comando precedente

Il comando mv serve a spostare e rinominare i files.

Il comando mkdir serve a creare una o più directory:

mkdir dir{1,2,3}  # crea 3 directory come visto sopra per 'touch'
mkdir dir{1...3}  # come sopra
mkdir -m 777 <directory>  # imposta i permessi della cartella che stiamo creando
mkdir -p dir1/dir2  # crea le cartelle che mancano nel percorso
rmdir <dir>  # elimina una directory solo se vuota

umask definisce i permessi predefiniti che vengono utilizzati quando si crea un file o una directory.

Il comando ls elenca i file presenti in una cartella:

ls -lah  # (l)ong listing, (a)all e (h)uman readable
ls -lhad <dir>  # il parametro '-d' permetti di elencare solo la directory altrimenti ci elencherebbe il contenuto della stessa

In Linux non è possibile creare un file con lo stesso nome di una directory perché le directory sono particolari tipi di file ma sono comunque file.

Il comando rm permette di cancellare file e, con il parametro -r anche directory:

rm -r <dir>  # elimina la directory e tutto il suo contenuto
rm -rf <qualcosa>  # non chiede forzature ed elimina ricorsivamente (usare solo in caso di necessità)

Il comando cp serve a copiare un file da un posto ad un altro e, contemporaneamente se necessario, anche a rinominarlo:

cp <file1> <file2>  # in caso 'file2' esistesse già lo sovrascrive senza avvisare
cp -i <file1> <file2>  # (i)nteractive, avvisa prima di sovrascrivere
cp -ra <espressione> <destinazione>  # copia ricorsivamente (cercando anche di mantenere i permessi) tutto, non segue i link simbolici e non fornisce grandi informazioni in caso di fallimento
cp -ral <espressione> <destinazione>  # come il precedente ma segue anche i link simbolici

103.3 - archiviazione e compressione di file e cartelle

Compressione

Riduce le dimensioni di un file eliminando l’origine.

Compressione con gzip:

gzip <file>  # trasforma nel formato compresso 'file.gz' ed elimina 'file'
gzip -l <file.gz>  # mostra informazioni sulla compressione
gunzip <file.gz>  # decomprime 'file' ed elimina 'file.gz'
zcat <file.gz>  # permette di leggere file di testo compressi con 'gzip'
gzip --help  # per vedere i vari parametri

Compressione con xz (miglior algoritmo):

xz -z <file>  # comprime il file in 'file.xz' eliminando 'file'
xz -l <file.xz>  # mostra informazioni sulla compressione
xz -d <file.xz>  # per decomprimere (elimina il file compresso)

I comandi gzip e xz fa solo compressione, non archiviazione.

Archiviazione

Crea un file che contiene altri file, cartelle e sottocartelle non eliminando gli elementi originali oggetto di archiviazione.

Per l’archiviazione usiamo il comando tar che invece crea un archivio partendo da una moltitudine di file, cartelle e sottocartelle. Lo stesso comando permette anche di comprimere contestualmente il file che stiamo creando utilizzando uno dei vari strumenti di compressione visti:

tar -cvzf <archivio.gz> <cartella/espressione>  # (c)reate (v)erbose (z)tipo-compressione (f)ile-name seguito dall'identificazione dei file da archiviare (se indicata una cartella la archivia/comprime con il suo contenuto)
tar -cvf  <archivio.tar> <cartella/espressione>  # crea solamente l'archivio senza eseguire nessuna compressione che posso eseguire separatamente
tar -xvf <file.compresso> -C <cartella-destinzaione>  # se ometto la destinazione decomprime nella cartella corrente

Alcune opzioni di =tar=:

103.3 - rpm, cpio, rpm2cpio, yumdownloader

cpio è un formato di archiviazione standard in linux ancora largamente utilizzato nei pacchetti rmp (RedHat/Fedora/CentOS e derivate).

yum install <pacchetto>  # installa il pacchetto .rpm e tutte le sue dipendenze
yumdownloader <pacchetto>  # fa solamente il download del pacchetto .rpm
yumdownloader --resolve <pacchetto>  # scarica anche le dipendenze per poter (ad esempio) installare il pacchetto e tutte le dipendenze quando siamo offline
rpm2cpio <pacchetto.rpm> > <pacchetto.cpio> #  estrae da un pacchetto .rpm il relativo archivio in formato .cpio
cpio -id < <archivio.cpio>  # estrae (-i) il contenuto dell'archivio nella directory corrente creando, se necessario, le relative directory (-d)
cpio -t < <archivio.cpio>  # mostra il contenuto di un archivio .cpio
rpm2cpio <pacchetto.rpm> | cpio -t  # mostra il contenuto del pacchetto .rpm
ls <files-da-archiviare> | cpio -ov > archivio.cpio  # crea (-o) un archivio .cpio in modalità verbosa (-v)

103.3 - Il leggendario comando dd

premessa : le dimensioni per i calcolatori

I computer ragionano in modalità binaria:

Simbologia:

Block Size (bs) : i filesystem generalmente scrivono su disco a blocchi di 4K (4.096 bytes).

Questo significa che, anche se il mio file è di dimensione inferiore, occuperà lo spazio minimo del blocco andando a “sprecare” spazio su disco. Questo però avviene per diminuire il numeri di volte che viene effettuata la scrittura su disco in quanto file di piccole dimensioni sono sempre più rari e più cicli di scrittura significa un utilizzo maggiore della risorsa più preziosa, ovvero la CPU.

Facciamo un esempio, devo scrivere su disco 2 file:

Lo posso fare su 2 filesystem diversi:

Sul primo filesystem i 2 files occuperanno uno spazio di 1.028K (4K per il file di 1K e 1.024K per il file di 1M) “sprecando” spazio su disco per 3K mentre sul secondo =filesystem= occuperò 1.025K, esattamente lo spazio necessario ai file in quanto il block-size è proprio di 1K pari al file più piccolo.

Tutto comporta che il primo filesystem necessiterà di 257 cicli di scrittura da 4K ciascuno (la dimensione del bs) mentre il secondo 1.025K cicli. Quindi la scelta è tra spreco di spazio (residuale all’effettiva grandezza dei singoli file) e spreco di risorse (CPU).

La dimensione minima del Block Size è pari a 512 bytes.

LEZIONE : Il leggendario comando dd

Alcune funzioni del comando dd sono:

Vediamo solamente alcuni esempi:

dd if=<text-lowercase> of=<text-uppercase> conv=ucase  # tutto maiuscolo
dd if=<text-uppercase> of=<text-lowercase> conv=lcase  # tutto minuscolo
dd if=/dev/zero of=<new-file> bs=1024 count=1  # crea un file di 1 KB (anche se la dimensione occupata su disco sarà 4kb perchè il filesystem legge blocchi minimi di tali dimensione)
dd if=/dev/zero of=<new-file> bs=4096 count=1  # crea un file di 4 KB (che occuperanno per intero lo spazio che il filesystem ha riservato al file su disco a differenza del comando precedente)
dd if=/dev/zero of=<new-file> bs=4K count=1  # crea un file di 4KB
dd if=/dev/zero of=<new-file> bs=1M count=1  # crea un file di 1.024KB

Eliminazione sicura di un file (in quanto rm elimina solo l’indice del file e non effettivamente il file).

Il metodo è sovrascrivere il file con dati casuali in modo da cancellare tutto quanto conteneva e poi eliminarlo con rm:

dd if=/dev/urandom of=<file-da-eliminare> bs=<dimensione> count=1
rm <file-da-eliminare>

103.3 - find, locate, updatedb

Il comando locate indicizza i file presenti sul disco in un database e permette una ricerca veloce quando stiamo cercando qualcosa:

locate <nome-file-o-cartella>  # cerca file o cartelle
sudo updatedb  # aggiorna il db

Il problema di locate è che il db viene aggiornato periodicamente e pertanto potrebbe trovare un file che è appena stato eliminato o non trovare un file che è appena stato creato.

Il comando che effettua ricerche in tempo reale è find:

find <percorso> -name <nome>  # ricerca case sensitive
find <percorso> -iname <nome>  # case insensitive
find <percorso> -iname <espressione>  # cerca file e cartelle tramite espressione
find <percorso> -name <nome> 2>/dev/null  # sopprime gli errori (tipo permesso negato)
find <percorso> -iname <nome> -exec ls -lhd {} \;  # esegue un comando (anche 'rm') su ogni singolo risultato della ricerca

Spiegazione del parametro -exec ls -lhd {} \;.
Viene eseguita una concatenazione di comandi bash inserendo tra le parentesi graffe ogni singolo risultato della ricerca, infatti l’espressione finisce con ; (che è un carattere che deve essere preceduto dal carattere escape \).

Altri esempi di utilizzo del comando find:

find <percorso> -iname <file> -type f  # cerca solo i file
find <percorso> -iname <file> -type d  # cerca solo le cartelle
find <percorso> -maxdepth 1 -iname <file>  # specifica la profondità massima in cui cercare in termini numeri di cartelle all'interno del percorso
find <percorso> -iname '*' -mtime +365 -exec ls -lhdt {} \;  # cerca file più vecchi di 365 giorni
find <percorso> -iname '*' -mtime -30 -exec ls -lhdt {} \;  # cerca file creati/modificati negli ultimi 30 giorni
find <percorso> -iname '*' -size +4k  # cerca file di dimensione maggiore di 4 kilobytes
find <percorso> -iname '*' -size -4M  # cerca file di dimensione minore di 4 Mega

103.3 - bunzip2, unxz

Le estensioni .bz2 e .xz caratterizzano file compressi che utilizzano i seguenti 2 comandi per estrarre il file che è stato appunto compresso in precedenza:

bunzip2 <file.bz2>
unxz <file.xz>

103.4 - streams, redirection e pipe

I simboli di redirezione in Linux sono:

Vediamo come può essere gestito il flusso output di un comando che si divide tra standard output e standard error:

L’opzione noclobber della shell bash impedisce la sovrascrittura di un file esistente in caso di redirezione dello standard output (che non sia tramite >> chiaramente) generando un errore:

set -o noclobber  # attiva l'opzione
set +o noclobber  # disattiva l'opzione
set -o  # visualizza tutte le opzioni di bash, sia attive che non attive

La pipe dopo il simbolo di redirezione dell’output (i.e. |) forza la sovrascrittura del file esistente anche se è attiva l’opzione noclobber.

Il comando tee riceve lo standard input da una pipe e fa 2 cose:

Esempio di pipe:

ls -l | sort -k9 -r  # ordine alfabetico inverso della colonna 9 (quella del nome)

Il comando xargs prende in ingresso una serie di elementi separati da spazi, \newline ed il carattere di tabulazione e li utilizza uno alla volta oppure più alla volta a seconda del parametro -n (di default li utilizza tutti insieme) a loro volta come parametro al comando che segue, vediamo degli esempi:

echo {1..9} | xargs
echo {1..9} | xargs -n1
echo {1..9} | xargs -n3
echo "1 2 3" | xargs -n1 echo "N"
echo "1 2 3" | xargs echo "N"
find <percorso> -iname 'espressione' | xargs tar -czvf <archivio.tar.gz>

Redirezione standard input:

cat <file> | sort | <elenco>  # ordina gli elementi di 'file' e liscrive in 'elenco'
sort < <file> > <elenco>  # fa la stessa cosa del comando precedente senza pipe
sort < <file> | <elenco>  # fa sempre la stessa cosa

Il simbolo di redirezione <<< (here strings) non fa altro che redirigere allo standard input del comando che precede tutto quanto come stringa di testo e non come file:

echo "1 2 3" | xargs -n1  # elenca gli argomenti andando a capo uno alla volta
xargs -n1 <<< "1 2 3"  # stesso risultato come sopra
cat <<< "prova"  # visualizza la stringa "prova"
cat < "prova"  # visualizza il contenuto del file 'prova'

103.5 - I jobs in Linux

Quando un comando tiene impegnata la shell per un certo tempo posso premere:

A questo punto i seguenti comandi:

jobs  # elenca i jobs che sta gestendo la shell con l'informazione (tra le altre) del loro stato (ossia in esecuzione o stoppato)
bg 1  # in caso di presenza di un processo in stato stoppato posso proseguirlo lasciandolo in background
fg 1  # oppure posso proseguirlo riportandolo in foreground (dopo 'bg' ed 'fg' metto il numero di job)

Posso anche decidere di lanciare un comando e metterlo subito in esecuzione in background aggiungendo il simbolo & alla fine della riga di comando. Occorre anche tenere presente che, se non redirigo gli standard output ed error dei comandi che metto in background, questi continueranno ad essere visualizzati nella shell corrente dando comunque fastidio.

E’ pertanto consigliabile redirigerli in qualche file oppure verso /dev/null quando si mette in background un comando.

103.5 - Eseguire processi con nohup

Se voglio eseguire un comando che necessiterà di molto tempo per terminare la sua esecuzione, e che quindi dovrà continuare anche quando io mi sconnetterò dalla shell o dal sistema, posso utilizzare il comando nohup:

nohup <comando> &

Questo redirige standard output e standard error ad un file dal nome nohup.out in modo che quando tornerò ad accedere alla macchina potrà verificare quanto il comando avrebbe mostrato durante la sua esecuzione se fossi rimando connesso con la shell aperta ed il job in primo piano.

103.5 - top, free, uptime

Il comando top mostra parecchie informazioni sul sistema e sui processi in esecuzione. Lanciando il comando senza nessun parametro permette di eseguirlo in modo interattivo. In questa modalità posso interagire (appunto) tramite la pressione di tasti:

All’interno di Field Management posso premere:

Per invertire l’ordinamento predefinito basta premere il tasto R quando siamo in modalità interattiva e stiamo visualizzando tutti i processi.

Il comando top può anche essere eseguito in batch ossia in modalità non interattiva tramite l’utilizzo del parametro -b:

top -b -n2 -d5  # esegue 'top' 2 volte a distanza di 5 secondi

Vediamo questi ultimi 2 comandi che forniscono alcune informazioni che troviamo all’interno di top:

uptime  # restituisce la prima riga di top
free -m  # restituisce le stesse informazioni di top riguardo l'utilizzo della memoria

103.5 - ps, pgrep, pkill, killall

Il comando ps restituisce l’elenco dei processi della bash corrente:

ps  # tipicamente restituisce 2 processi, ossia 'bash' (la shell) ed il comando 'ps' appena lanciato
ps -f  # restituisce alcune informazioni in più compreso il 'parent pid' che sarà presumibilmente sempre bash
ps -lf  # restituisce un elenco ancora più dettagliato
ps -e  # restituisce l'elenco di tutti i processi in esecuzione
ps -u <nome-utente>  # restituisce tutti processi che appartengono a 'nome-utente'

Notazione sul valore di nice, esso può assumere valori da -20 a +19:

Vediamo ora il comando pgrep:

pgrep <nome-processo>  # restituisce il pid dei processi che contengono 'nome-processo'
ps -e | grep <nome-processo>  # restituisce gli stessi processi del comando precedente

I processi si possono terminare/uccidere con vari comandi che ruotano attorno al comando kill.

Lanciandolo con l’opzione -l viene elencato il numero di segnale che si può utilizzare tra cui:

Vediamo ancora qualche esempio:

kill <numero-pid>  # chiede al processo di terminarsi utilizzando il codice 15 (default)
kill -s -9 <numero-pid>  # uccide il processo (codice -9)
pkill <nome-processo>  # termina tutti i processi che contengono 'nome-processo' nel loro nome (pericoloso)
killall <nome-processo>  # come il precedente ma richiede il nome esatto del processo
killall -s 9 -u <nome-utente> <nome-processo>  # uccide tutti i processi che si chiamano 'nome-processo' e che appartengono all'utente 'nome-utente'

103.5 - La cartella /proc

La cartella /proc/ è un filesystem virtuale per i processi.

All’interno di questa cartella troviamo, tra le altre cose, tante sottocartelle quanti sono i processi in esecuzione il cui nome corrisponde al numero di pid del processo. In ognuna di queste sottocartelle vi sono molte informazioni utili che riguardano il relativo processo.

Tutte le varie applicazioni di monitoraggio dei processi (come ad esempio top) attingono a queste cartelle per esporre le varie informazioni sui processi attivi.

103.5 - watch : tenere monitorati i comandi

Il comando watch è una utility che esegue periodicamente (default 2 secondi) un altro comando che viene fornito come parametro, vediamo un paio di esempi:

watch -n 1 ls -lh /var/log  # esegue il comando 'ls' ad intervalli di 1 secondo
watch -n 1 ping -c 1 google.com  # esegue 1 ping a google ogni secondo

103.5 - tmux moltiplicatore di terminali

Il comando tmux aggira i limiti che si hanno generalmente quando ci si collega tramite ssh o quando si utilizza un terminale a caratteri dove si deve gestire tutto da una sola finestra. Inoltre permette anche di gestire delle sessioni di lavoro che possono essere lasciate attive nella macchina anche dopo il nostro logout fino al successivo collegamento.

All’avvio di tmux vediamo una barra in basso che gestisce le nostre varie shell.

L’interazione con il programma avviene tramite la combinazione C-b seguita dal carattere che identifica il comando che vogliamo eseguire, vediamo i principali:

Dopo essersi scollegati con d la sessione rimane attiva sulla macchina anche se ci scolleghiamo dalla macchina stessa.

Una volta che torniamo ad effettuare il login ci possiamo ricollegare:

tmux ls  # elenca le sessioni attive sulla macchina
tmux attach -t <nome sessione>  # si collega ad una sessione

Una nuova sessione può essere creata con il semplice comando tmux e gli può essere assegnato un nome tramite il parametro -s:

tmux new -s <nome-sessione>

103.6 - nice e renice

Il valore di nice di un processo influenza il suo livello di priority:

ps -l  # visualizza l'elenco dei processi della shell comprese le colonne con il valore di nice (NI default 0) ed il valore di priority (PRI default 80)
nice -n 5 <comando>  # lancia un comando con valore di nice 5 e priority 85 (aggiunge il valore di nice)
renice -n 7 <PID>  # cambia il valore di nice di un processo

Da evidenziare com l’utente normale può lanciare comandi con valori di nice da 0 a +19 mentre l’utente root può utilizzare anche valori negativi.

Allo stesso modo l’utente root può variare il valore di nice dei processi in esecuzione senza limitazioni mentre un utente normale può solo aumentarne il valore e non diminuirlo.

Il valore di nice può essere variato anche dal comando top lanciato in modalità interattiva premento il tasto r. Prima viene chiesto il PID del processo sul quale si vuole operare (default quello su cui abbiamo il cursore) e poi il nuovo valore di nice che vogliamo assegnare.

Vediamo ancora un paio di comandi che possono servire per cambiare il valore di nice a più processi contemporaneamente:

renice -n 0 -u <nome-utente>  # azzera il valore di nice di tutti i processi lanciari da un determinato utente
renice -n 0 -g <gruppo>  # fa la stessa cosa per tutti i processi di un determinato gruppo

103.7 - Le espressioni regolari (regex)

grep morro <file>  # restituisce tutte le linee che contengono 'morro', case sensitive
grep -i morro <file>  # restituisce tutte le linee che contengono 'morro', case insensitive
grep '[Mm]orro' <file>  # restituisce le righe che contengono 'Morro' oppure 'morro'
grep '[^L]inux' <file>  # restituisce tutte le linee che NON hanno un carattere 'L' seguito da 'inux' (es. 'Sinux', 'tinux', ecc..)
grep -w linux <file>  # cerca un parola intera, case sensitive
grep -iw linux <file>  # cerca un parola intera, case insensitive
grep '[0-9]' <file>  # cerca la ricorrenza di un numero da 0 a 9
grep -E '[0-9]{4}' <file>  # cerca 4 numeri 0-9 consecutivi, il parametro '-E' interpreta le espressioni regolari
egrep '[0-9]{4}' <file>  # come il precedente ma deprecato
fgrep 'espressione' <file>  # cerca esattamente l'espressione senza interpretare simboli quali parentesi quadre o graffe
sed 's/<ricerca>/<sostituzione>/' <file>  # sostituisce la prima ricorrenza (vedi il capitolo su 'sed')
sed -i 's/<ricerca>/<sostituzione>/' <file>  # modifica direttamente 'file'
sed '/^linux/d' <file>  # elimina le righe che iniziano con la parola 'linux'

103.8 - introduzione a VI

Spesso vi è un collegamento a vim:

vi 'file'  # apre un file esistente o lo crea

Esistono 3 diverse modalità in cui si può operare in vi:

Nella modalità motion mode la pressione dei tasti esegue delle azioni, iniziamo a vedere come spostarsi con il cursore:

In vi si può sempre specificare quante volte eseguire un comando premettendo il relativo numero al comando:

Copiare e incollare in vi:

Vediamo altri comandi da utilizzare in motion mode:

Cercare del testo con vi:

Alcuni comandi invece da digitare in command mode:

Alcuni modi per entrare in insert mode:

Cercare e sostituire testo (molto simile a sed):

103.8 - Default EDITOR e nano

In Linux esistono altri editor di testo oltre a vi, ad esempio:

Possiamo impostare l’editor di testo predefinito:

Test pratico 2 : Test sul capitolo 103

Lezioni da n. 39 a n. 75.

Risposte giuste: 21 su 23.

Riassunto comandi più comuni

alias e type

type <comando>  # distingue il comando tra alias, shell builtin, o percorso dell'eseguibile
alias abbreviazione='<comando-completo>'  # crea un alias per digitare 'abbreviazione' anziché 'comando-completo'

shell history

history  # mostra i comandi digitati in precedenza
history -c  # pulisce la cronologia dei comandi
C-r  # inizia la ricerca nella history
!!  # esegue nuovamente l'ultimo comando invocato
!$  # richiama il parametro usato nell'ultimo comando invocato

manuale

man <comando>  # apre il manuale utente per il comando specificato (il manuale utente non è presente per i comandi shell builtin)
<comando> --help  # mostra l'help per il programma specificato (solitamente più sintetico del man, presente anche per i builtin)

informazioni sul sistema

uname -a  # mostra tutte le info sul kernel in esecuzione
uname -r  # mostra solo il numero di versione del kernel

lavorare con i file di testo

Premessa: l’operatore > ha sempre il compito di redirigere l’output (stdout) sul file indicato:

split -l 100 file.txt  # divide file.txt in 'n' file da 100 righe ciascuno
split -b 5m video.mp4  # divide video.mp4 (file binario, non di testo!) in porzioni di 5mb
cat video* > unito.mp4  # unisce nuovamente tutti i file con prefisso nominale 'video' nel file unito.mp4 (funziona con qualunque formato)
cut -d ':' -f 1 ab.txt  # imposta come separatore di colonna il carattere ':' e stampa la prima colonna
cut -d ':' -f1,7 ab.txt  # come sopra, ma stampa le colonne da 1 a 7
head -n 10 file.txt  # mostra solo le prime 10 righe di file.txt
tail -n 10 file.txt  # mostra solo le ultime 10 righe di file.txt
wc -l file.txt  # conta il numero di righe presenti in file.txt
wc -c file.txt  # conta il numero di caratteri presenti in file.txt
less file.txt  # visualizza file.txt poco per volta. usare le frecce per navigare il documento (Up/Down)
zcat file.txt.gz  # visualizza file.txt.gz (compresso) senza bisogno di decomprimerlo
bzcat file.txt.bz2  # come sopra, per i file compressi in formato .bz2
xzcat file.txt.xz  # come sopra, per i file compressi in formato .xz

gestione dei file

pwd  # mostra il percorso della cartella in cui ci troviamo (directory locale)
ls  # mostra il contenuto della cartella in cui ci troviamo (directory locale)
cd Scrivania  # ci spostiamo con il terminale nella cartella chiamata 'Scrivania' (deve esistere, con quel nome, nella directory locale)
du -sh folder  # restituisce la dimensione della cartella 'folder', comprensiva di tutti i file in essa contenuti
md5sum file  # calcola la somma md5 per 'file', può essere utile se confrontato con un una copia del file sicuramente integra
cp file.txt new.txt  # crea una copia di 'file.txt' chiamata 'new.txt'
cp -r cart1 cart2  # crea una copia della cartella 'cart1' (e ricorsivamente il suo contenuto) chiamata 'cart2'
mv old new  # rinomina il file 'old' come 'new'
mv old Desktop/new  # sposta il file 'old' sul 'Desktop', con il nome cambiato in 'new'
rm file.txt  # elimina 'file.txt'
rm -r folder  # elimina la cartella 'folder' ed il suo contenuto (utilizzare anche il parametro '-f' per rimuovere forzatamente)
mkdir folder  # crea la cartella 'folder'
mkdir -p folder/ciao  # crea la cartella 'folder' e al suo interno la sottocartella 'ciao' con un unico comando
gzip file  # genera un file compresso chiamato 'file.gz' ELIMINANDO il file originale
gunzip file.gz  # estrae 'file.gz' ELIMINANDO la sua verisone compressa con estensione .gz
tar -czf var.tar.gz var  # comprime la cartella 'var' in un archivio chiamato 'var.tar.gz'
tar -xzf var.tar.gz  # estrae nella cartella locale il contenuto dell'archivio 'var.tar.gz'
dd if=/file of=new  # copia bit a bit il contenuto di 'file' in 'new', spesso usato per clonare supporti di memorizzazione di massa
find / -iname "ciao"  # cerca su tutto il filesystem i file chiamati 'ciao' senza distinguere tra maiuscole e minuscole ('-iname')

streams, redireciton e pipes

ls > out.txt  # redirige l'output di 'ls' nel file 'out.txt'
ls 2> err.txt  # redirige eventuali errori di 'ls' nel file 'err.txt'
ls &> out.txt  # redirige errori e output di 'ls' nel file 'out.txt'
cat < in.txt  # redirige l'input di 'cat' per leggere 'in.txt' (ma non è necessario usare l'operatore di redirezione con 'cat')
cat in.txt  # stesso effetto del comando precedente
prog1 | prog2  # redirige l'output del programma 'prog1' sull'input di 'prog2'

gestione processi e risorse

C-z  # manda un processo in background (ed in pausa)
jobs  # mostra i processi in background
fg <n>  # riporta in foreground (in primo piano) il processo 'n'
ps aux  # mostra tutti i processi in esecuzione sulla macchina
top  # mostra in maniera interattiva tutti i processi in esecuzione, compreso utilizzo delle risorse (stile task manager)
htop  # versione più evoluta di 'top'
free -m  # mostra l'uso di memoria RAM
df -h  # mostra l'uso del disco (spazio libero per ogni partizione)
iotop  # (extra) mostra l'uso del disco (letture/scritture correnti, e da quali processi)
nohup <cmd>  # esegue il comando 'cmd' in modo che non venga interrotto quando chiudiamo la shell o ci scolleghiamo
kill [-S] <pid>  # uccide il processo con un dato 'pid' (il parametro '-S' specifica il codice di terminazione come, ad esempio, -15 o -9)
killall <nome>  # uccide tutti i processi con un certo nome

Sezione 5 : 104 - Dispositivi, Filesystem Linux, Filesystem Hierarchy Standard

104.1 - MBR vs GPT

MBR (Master Boot Record), è stato introdotto da IBM nel 1983 ed è caratterizzato dal fatto che i primi 512 bytes del disco contengono il bootloader. Pertanto il BIOS non deve fare altro che leggerli per dare inizio all’avvio della macchina.

MBR ha delle limitazioni quali:

GPT (GUID Partition Table) è stato progettato da Intel a fine anni 90 ed introdotto intorno al 2010, vediamo alcune caratteristiche e differenze con MBR:

GPT si è diffuso insieme a UEFI, il nuovo standard di boot che prevede la presenza di una partizione UEFI (Fat32) di qualche centinaio di MB che contiene il bootloader e sostituisce i primi settori del disco, tipici di MBR.

Per formattare un disco usando MBR o GPT posso usare il comando =parted=:

parted /dev/sdb
(parted) mklabel

104.1 - FAT32 vs exFAT ; Btrfs

FAT32 (32 Bit File Allocation Table)

E’ sicuramente il filesystem più diffuso perchè compatibile trasversalmente con Windows, MacOS e Linux, ha però forti limitazioni quali:

exFAT

Introdotto più recentemente, risolve queste limitazioni mantenendo la compatibilità multipiattaforma:

Creazione filesystem

Sia nel caso di FAT32 che exFAT, dopo aver creato la partizione tramite =parted=, dovrò provvedere a creare il filesystem. Esempio nel caso di filesystem exFAT:

apt install exfat-fuse  # permette di montare e leggere partizioni exFAT
apt install exfat-utils  # permette di creare partizioni exFAT
mkfs.exfat 'partizione'

Btrfs (B-Tree FileSystem)

Lato Linux abbiamo il filesystem Btrfs che fornisce diverse funzionalità avanzate (ma è ancora in fase di sviluppo) quali:

Soprattutto l’ultima caratteristica permette di passare a Btrfs senza dover formattare il disco se questo è formattato con ext2, ext3 o ext4.

104.2 - Mantenere l’integrità del filesystem

Ad ogni montaggio del filesystem c’è un contatore che viene incrementato e, se è abilitata la funzione di check, viene controllata l’integrità del filesystem ogni X montaggi:

tune2fs -l <partizione>  # genera un po' di informazioni tra cui 'Mount count' e 'Maximum mount count' (se questo è impostato a '-1' non viene mai effettuato il check
tune2fs -c 15 <partizione>  # imposta il numero di montaggi dopo cui effettuare il check: '-1' significa mai, '1' significa ogni volta che viene montato

Nel file /etc/fstab sono elencate le partizioni di cui facciamo il mount in fase di boot, il valore indicato alla sesta colonna (l’ultima) può essere 0 (significa che non è previsto fare il check della partizione) oppure un numero positivo che, oltre a prevedere il check della partizione, ne stabilisce anche l’ordine (1,2,3…).

Il controllo può anche essere lanciato manualmente tramite l’utility fsck che sul sistema è presente con vari comandi a seconda del tipo di filesystem che si vuole controllare e spesso sono presenti link simbolici tra i vari comandi per avere più opzioni di lancio del comando stesso:

e2fsck  # per le famiglie 'ext2', 'ext3', 'ext4'
dosfsck  # per le famiglie 'fat' (è un link simbolico a 'fsck.fat')
reiserfsck  # per i filesystem di tipo 'reiser'
xfs_repair  # per filesystem di tipo 'XFS' (necessita del pacchetto 'xfsprogs')

Se la partizione è montata in scrittura non è possibile effettuare il check:

mount -o remount,ro <partizione>  # rimonta la partizione in sola lettura

E’ comunque vivamente sconsigliato fare il check di partizioni montate in sola lettura ma questo può essere forzato tramite:

e2fsck <partizione> -f

Il check è consigliato quando la partizione non è montata (infatti avviene al boot).

Vediamo un paio di utilizzi dell’utility fsck:

fsck -A  # fa il controllo di tutte le unità che devono essere controllate in base alle impostazioni che sono scritte nel file '/etc/fstab'
fsck -a  # esegue la riparazione in automatico senza chiedere conferma

debugfs è una shell interattiva per analizzare i filesystem ext2/ext3/ext4 e che, tra le altre cose, permette di mostrare l’inode dove è salvato un file e anche l’inode dove sono scritti i file cancellati di recente per cercare di recuperarli (questa procedura di undelete si è complicata dalla versione ext3 in poi):

debugfs  # lancia l'utility in modalità interattiva
debugfs: ?  # visualizza l'elenco dei comandi disponibili
debugfs: open <partizione>  # 'apre' una partizione
debugfs: pwd  # verifica in quale directory siamo
debugfs: cd <cartella>  # cambia directory
debugfs: ls  # elenca files e directories con i relativi 'inode'
debugfs: ls -d  # elenca anche i files e directries (questi ultimi con inode tra '<>')
debugfs: quit  # esce dalla shell interattiva di debugfs

dumpe2fs è un altro comando che fornisce informazioni sulle partizioni ext2/ext3/ext4:

dumpe2fs -h <partizione>  # fornisce informazioni analoghe a 'tune2fs -l'

104.3 - Montare e smontare filesystems : mount e umount

mount  # mostra tutti i filesystem montati
fdisk -l  # mostra i dispositivi fisicamente collegate al computer
parted -l  # mostra sostanzialmente le stesse informazioni di 'fdisk -l'
mount <partizione> <punto-di-mount>  # monta una partizione
umount <punto-di-mount>  # smonta una partizione

In Linux sono stati dedicati 2 punti di mount:

Montare una partizione maschera il contenuto della directory su cui è montato, che torna visibile quando smontiamo la partizione.

Montare una partizione con il comando mount inoltre non è un’operazione permanente nel senso che al successivo riavvio questa risulterà ancora non montata.

Per poter rendere permanente ad ogni avvio il montaggio di una partizione dobbiamo modificare il file /etc/fstab.

Il file /etc/fstab è suddiviso in 6 colonne di informazioni:

Nel primo campo è meglio usare gli UUID invece che il percorso della partizione in quanto, nel caso si dovessero montare più dischi, in fase di boot potrebbe cambiare l’ordine di riconoscimento degli stessi da parte del sistema assegnando una lettera diversa e creando confusione poi in fase di montaggio.

L’utilizzo di UUID non corre questo rischio in quanto è un riferimento univoco:

blkid <partizione>  # mostra lo 'UUID' della partizione

104.3 - lsblk, blkid, systemd mount units

lsblk  # mostra i block-devices o dispositivi di memorizzazione di massa, sia fisici che vistuali
blkid <device>  # mostra 'UUID' e 'PARTUUID' della partizione, del disco o del dispositivo

Vediamo la differenza tra UUID e PARTUUID:

Nel file /etc/fstab posso indicare indifferentemente l’uno o l’altro UUID.

Un metodo alternativo per montare dischi e le partizioni all’avvio è utilizzare systemd tramite la creazione delle mount units.

Per fare un esempio concreto provo a creare una unità che mi faccia il mount di una partizione non montata con /etc/fstab.

Devo creare l’unità al percorso /etc/systemd/system/mnt-part4.mount che indica il montaggio della partizione al percorso /mnt/part4 dove per convenzione - viene interpretato come fosse il separatore delle directory (dovremo chiaramente creare la directory).

[Unit]
Description=Monta la partizione 4

[Mount]
What=/dev/disk/by-uuid/UUID-della-partizione
Where=/mnt/part4
Type=ext4
Options=defaults

[Install]
WantedBy=local-fs.target

Successivamente:

systemctl daemon-reload  # carica la nuova 'unit'
systemctl start mnt-part4.mount  # monta la partizione
systemctl status mnt-part4.mount  # verifica che l'unità è stata caricata e di conseguenza la partizione montata
systemctl enable mnt-part4  # imposta il montaggio della partizione al boot

104.4 - Limitare gli utenti con Disk Quota

Installiamo i pacchetti necessari:

apt install quota quotatool

Successivamente, nel file /etc/fstab, aggiungo le opzioni usrquota e grpquota in corrispondenza del filesystem che voglio monitorare:

mount -o remount <partizione>  # rimonto la partizione di `/etc/fstab` per fargli acquisire le nuove opzioni
quotacheck -avugmc

L’ultimo comando fa tutto quanto necessario per inizializzare e monitorare le quote. spiego gli argomenti:

L’opzione c è necessaria solo al primo quotacheck per creare i seguenti files alla radice del filesystem che si vuole controllare:

Per abilitare o disabilitare il controllo ora posso eseguire:

quotaon -a  # abilita il monitoraggio su tutti i filesystem impostati
quotaon <mount-point>  # abilita il monitoraggio di un particolare filesystem
quotaoff -a  # disabilita il monitoraggio su tutti i filesystem impostati
quotaoff <mount-point>  # disabilita il monitoraggio di un particolare filesystem

I check successivi possono anche essere lanciati manualmente:

quotacheck -avugm

Vediamo gli ultimi comandi:

edquota <nome-utente>  # modifica le quote di un utente
edquota -g <group>  # modifica le quote di un gruppo
quota <username>  # mostra la situazione di un utente
quota -g <group>  # mostra la situazione di un gruppo
repquota <mount-point>  # mastra la situazione per tutti gli utenti con riferimento ad un particolare filesystem

104.5 - chown, chmod, permessi

permessi

Le triplette dei permessi si riferiscono a:

E possono essere di:

I permessi possono essere cambiati tramite comando chmod:

chmod o+w <file>  # aggiunge il permesso di scrittura ('w') a tutti gli utenti ('o')
chmod ugo-x <file>  # elimina il permesso di esecuzione ('x') al proprietario ('u'), agli utenti che fanno parte del gruppo di appartenenza ('g') ed a tutti gli altri ('o')

L’assegnazione dei permessi è generalmente effettuata con la modalità ottale. Il significato dei numeri, per utente proprietario, gruppo proprietario e resto degli utenti, è il seguente e vanno sommati per ogni gruppo:

Pertanto i permessi possono essere anche assegnati come segue:

chmod 775 <file>  # corrisponde a `'rwxrwxrw-'`
chmod 644 <file>  # corrisponde a `'rw-r--r--'`
chmod 444 <file>  # corrisponde a `'r--r--r--'`

sticky-bit

L’assegnazione del permesso di scrittura (w) permette l’eliminazione del file anche a chi non ne è strettamente il proprietario.

Per ovviare a ciò, ossia assegnare solo al proprietario la possibilità di eliminare il file ma lasciare agli altri utenti la possibilità di modificarlo (data dal permesso w), è possibile attribuire ad un file uno sticky-bit tramite il seguente comando:

chmod +t <file>

In questo modo l’ultimo carattere dei permessi diventa:

Quindi anche se ci sono i permessi di scrittura per il gruppo o gli altri utenti, questi non possono rinominare o eliminare il file ma lo potrà fare solo il proprietario (la cancellazione del file è comunque permessa al proprietario della cartella in cui si trova il file).

Anche lo sticky-bit può essere assegnato con il metodo ottale aggiungendo il numero 1 all’inizio della tripletta dei permessi ottale, ad esempio:

chmod 1777 <file>  # corrisponde a 'rwxrwxrwT'
chmod 1776 <file>  # corrisponde a 'rwxrwxrwt'
chmod 1770 <file>  # corrisponde a 'rwxrwx--t'

Lo sticky-bit può anche essere aggiunto ad una cartella:

chmod +t <cartella>

In questo modo i files presenti all’interno della cartella potranno essere eliminati solamente dal proprietario del file (o dal proprietario della cartella, come per i file). Si noti come la cartella di sistema /tmp/ abbia lo sticky-bit e ne si capisce anche il senso: chiunque può scrivere nella cartella ma solo il proprietario del file creato può eliminarlo.

set-UID e set-GID

Consentono di eseguire un file (naturalmente a chi ne ha i permessi di esecuzione) con gli stessi privilegi del proprietario (set-UID) o del gruppo (set-GID) di appartenenza del file stesso (per questioni di sicurezza si può applicare soltanto agli eseguibile compilati e non agli script).

Il set-UID e set-GID vengono identificati con il carattere s che si sostituisce al carattere x rispettivamente ai permessi del proprietario o del gruppo.

Il permesso viene attribuito in maniera ottale, insieme allo sticky-bit, tenuto conto dei seguenti valori (da sommare) e mettere in testa alla tripletta ottale di assegnazione dei permessi:

A esempio:

chmod 4775 <file>  # solo set-UID
chmod 6644 <file>  # sia set-UID che set-GID
chmod 7600 <file>  # contemporaneamente set-UID, set-GID e sticky-bit

104.5 - chown e chgrp (i permessi)

chgrp <gruppo> <file>  # cambia il gruppo proprietario di 'file'
chown <proprietario> <file>  # cambia il proprietario di 'file'
chown <proprietario>:<gruppo> <file>  # cambia il proprietario ed il gruppo proprietario di 'file'

Nota bene:

Il comando umask restituisce la “maschera” che viene applicata ai permessi di default di files (666) e cartelle (777). Ciò significa che, quando un utente crea un file oppure una cartella, i permessi per proprietario, gruppo e altri utenti descritti in forma ottale, saranno uguali ai permessi default meno i permessi di umask.

Ad esempio, il valore di default di umask per un utente Linux è pari a 0002 (il primo numero non si conta), quindi i permessi di una cartella creata saranno 775 e per un file invece 664. Per cambiare il valore di umask eseguo:

umask <nuova-maschera-ottale>

Per i punti che precedono un hardlink non può puntare ad un file che si trova su un filesystem diverso mentre symlink può farlo perchè il path è descritto nei dati a cui punta (questi dati si che si trovano nel medesimo filesystem).

Sempre per le medesime ragioni quando viene spostato (mv) nome-file (all’interno del medesimo filesystem) viene solo variato l’inode del nome e non l’inode dei dati a cui punta e quindi:

Se invece nome-file viene spostato su un diverso filesystem quello che succede è che anche i dati del file vengono duplicati e quindi:

La conclusione è che spostamenti di hard-link possono causare la duplicazione di un file senza accorgersi, cosa che non può avvenire con il symlink che si corrompe sempre ad ogni spostamento di nome-file.

Ultima due note:

104.7 - type, which, whereis, FHS

type <comando>  # mostra l'alias se esiste altrimenti...
type <comando>  # ...mostra se è un comando interno della shell oppure...
type <comando>  # ...mostra il percorso dell'eseguibile
which <comando>  # restituisce il primo percorso del comando trovato in '$PATH'
whereis <comando> # restituisce il percorso ed il manuale del comando

Filesystem Hierarchy Standard (FHS) è uno standard che definisce le directory principali ed il loro contenuto nel file-system dei sistemi operativi Unix-like, tra cui i sistemi Linux.

Test sul capitolo 104

Lezioni da n. 77 a n. 86.

Risposte giuste: 12 su 13.

Sezione 6 : Conclusione

Simulazione d’esame - Exam 101

Risposte giuste: 58 su 70.

Certificazione completamento del corso