├── .gitignore ├── README.md ├── books ├── cooper - guida avanzata di scripting bash.pdf ├── garrels - la guida di bash per i principianti.pdf └── ramey, fox - bash reference manual.pdf ├── code ├── extra │ ├── benchmark │ │ └── benchmark.sh │ ├── getopt │ │ └── example.sh │ └── python │ │ ├── script.py │ │ └── script.sh ├── lab01 │ └── README.md ├── lab02 │ └── README.md └── lab03 │ ├── README.md │ ├── es21.sh │ ├── es210.sh │ ├── es22.sh │ ├── es23.sh │ ├── es24.sh │ ├── es25.sh │ ├── es26.sh │ ├── es27.sh │ ├── es28.sh │ ├── es29.sh │ ├── es31_find.sh │ ├── es31_recursive.sh │ ├── es32.sh │ ├── es33.sh │ ├── es34.sh │ ├── es35.sh │ ├── es36.sh │ ├── es37.sh │ ├── es38.sh │ ├── es39.sh │ ├── es41.sh │ ├── es42.sh │ ├── es43.sh │ ├── es44.sh │ ├── es45.sh │ ├── es46.sh │ ├── es47.sh │ ├── es48.sh │ └── es49.sh ├── install_guide ├── README.md ├── eos_1.webp ├── eos_2.webp ├── eos_3.webp ├── eos_4.webp ├── eos_5.webp ├── eos_6.webp └── eos_7.webp └── slides ├── 01 - Introduzione a Unix.md ├── 02 - Bash shell.md ├── 03 - Bash scripting.md └── images ├── architettura-interna.avif ├── bit-di-protezione.avif ├── chmod-chown.avif ├── combinare-comandi.avif ├── de-cinnamon.avif ├── de-elementary.avif ├── de-gnome-mac.avif ├── de-gnome.avif ├── de-hyprland.avif ├── de-plasma.avif ├── esempi-1.avif ├── esempi-2.avif ├── esempio-mount.avif ├── file-e-metadati.avif ├── flussi-dati-standard.avif ├── flussi-dati.avif ├── funzionalità-principali-os.avif ├── genealogia-famiglia-unix.avif ├── i-node.avif ├── implementazione-pipes.avif ├── implementazione-ridirezione-input.avif ├── implementazione-ridirezione-output.avif ├── interazione-kernel-applicazioni.avif ├── links-cp.svg ├── links-ln.svg ├── links-lns.svg ├── links-mv.svg ├── linux-kernel-map.avif ├── mobile-market-share.avif ├── monolitici-micro-ibridi.avif ├── multitasking.avif ├── nomi-assoluti-e-relativi.avif ├── quanto-è-complesso-un-kernel.avif ├── standard-filesystem-hierarchy.svg ├── struttura-file-system.avif ├── terminale-testuale.avif ├── top-btop.avif ├── top-gtop.avif ├── top-htop.avif ├── top.avif ├── total-market-share.avif └── unix-history.svg /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 3 | 4 | # Jupyter # 5 | ############ 6 | .ipynb_checkpoints/ 7 | __pycache__/ 8 | *.pyc 9 | 10 | # IntelliJ # 11 | ############ 12 | .idea/ 13 | 14 | # VSCode # 15 | ########## 16 | .vscode 17 | 18 | # Packages # 19 | ############ 20 | # it's better to unpack these files and commit the raw source 21 | # git has its own built in compression methods 22 | *.7z 23 | *.dmg 24 | *.gz 25 | *.iso 26 | *.jar 27 | *.rar 28 | *.tar 29 | *.zip 30 | 31 | # Logs and databases # 32 | ###################### 33 | *.log 34 | *.sql 35 | *.sqlite 36 | 37 | # OS generated files # 38 | ###################### 39 | .DS_Store 40 | .DS_Store? 41 | ._* 42 | .Spotlight-V100 43 | .Trashes 44 | ehthumbs.db 45 | Thumbs.db 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Learn Bash 2 | 3 | ## Software 4 | * [VirtualBox](https://www.virtualbox.org/wiki/Downloads), [UTM](https://mac.getutm.app/) (Mac M*) 5 | * [EndeavourOS](https://endeavouros.com/) 6 | 7 | ## Libri 8 | * Garrels - Bash Guide for Beginners 9 | * Ramey, Fox - Bash reference manual 10 | 11 | ## Video 12 | * [YouTube Bash](https://www.youtube.com/watch?v=62-hJarauK4&list=PLhlcRDRHVUzR-5TKDC1VPMtyhEyyQ5uwy) 13 | 14 | ## Tutorial, Esercizi Online 15 | * https://www.tutorialspoint.com/unix/ 16 | * https://explainshell.com/ 17 | 18 | ## Materiale didattico 19 | Una volta terminata l'installazione, aprire un terminale ed inserire i seguenti comandi. 20 | 21 | ``` 22 | $ sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply nbicocchi 23 | $ ./install_scripts/install_script_eos.sh 24 | $ git clone https://github.com/nbicocchi/learn-bash.git 25 | ``` 26 | 27 | ## Moduli 28 | [M1] Introduzione a Unix 29 | * Varianti e caratteristiche Unix 30 | * Fondamenti per l'utilizzo pratico 31 | * Utenti e gruppi 32 | * Filesystem 33 | * Processi 34 | * Comandi rilevanti 35 | 36 | [M2] Bash Shell 37 | * Utilizzo interattivo 38 | * Ridirezione 39 | * Combinazione di comandi 40 | * Variabili 41 | * Espansioni 42 | 43 | [M3] Bash Scripting 44 | * Scrittura ed esecuzione 45 | * Costrutti condizionali 46 | * Costrutti iterativi 47 | * Funzioni 48 | * Trattamento argomenti avanzato (getopts) 49 | * Script multi-file 50 | * Buone pratiche 51 | -------------------------------------------------------------------------------- /books/cooper - guida avanzata di scripting bash.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/books/cooper - guida avanzata di scripting bash.pdf -------------------------------------------------------------------------------- /books/garrels - la guida di bash per i principianti.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/books/garrels - la guida di bash per i principianti.pdf -------------------------------------------------------------------------------- /books/ramey, fox - bash reference manual.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/books/ramey, fox - bash reference manual.pdf -------------------------------------------------------------------------------- /code/extra/benchmark/benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FNAME="/tmp/ftest" 4 | 5 | function benchmark() { 6 | echo "* executing $1..." 7 | start=$(python -c "import time; print (int(time.time() * 1000))") 8 | echo "output=$(bash -c "$1")" 9 | end=$(python -c "import time; print (int(time.time() * 1000))") 10 | echo "time=$(expr $end - $start)ms" 11 | echo "" 12 | } 13 | 14 | echo "Generating test file..." 15 | python -c "for i in range(10000000): print(i)" > "$FNAME" 16 | 17 | echo "Starting benchmarks..." 18 | benchmark "cat $FNAME | wc -l" 19 | benchmark "wc -l $FNAME" 20 | benchmark "wc -l < $FNAME" 21 | 22 | rm -rf "$FNAME" 23 | 24 | exit 0 25 | 26 | -------------------------------------------------------------------------------- /code/extra/getopt/example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TIME=5 4 | MESSAGE="Hello World!" 5 | DEBUG=FALSE 6 | 7 | usage() { 8 | echo "Usage: $0 [-t <1|5|10>] [-m ] [-d] filename" 1>&2; 9 | exit 1; 10 | } 11 | 12 | while getopts "t:m:dh" o; do 13 | case "$o" in 14 | t) TIME="$OPTARG" 15 | [ $TIME -eq 1 ] || [ $TIME -eq 5 ] || [ $TIME -eq 10 ] || usage 16 | ;; 17 | m) MESSAGE="$OPTARG" 18 | ;; 19 | d) DEBUG=TRUE 20 | ;; 21 | h) usage 22 | ;; 23 | *) usage 24 | ;; 25 | esac 26 | done 27 | 28 | # Shift parameters away. $1 becomes filename 29 | shift $(expr $OPTIND - 1) 30 | 31 | # Check if filename exists 32 | [ -e "$1" ] || usage 33 | 34 | echo t = "$TIME" 35 | echo m = "$MESSAGE" 36 | echo d = "$DEBUG" 37 | echo filename = "$1" 38 | 39 | exit 0 -------------------------------------------------------------------------------- /code/extra/python/script.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import subprocess 5 | 6 | if len(sys.argv) < 2: 7 | sys.stderr.write("usage: %s f1 .. fn\n" % (sys.argv[0])) 8 | sys.exit(1) 9 | 10 | for fname in sys.argv[1:]: 11 | out = subprocess.check_output(['wc', '-l', fname]) 12 | print("%s: %s" % (fname, out.split(' ')[0])) 13 | 14 | sys.exit(0) 15 | -------------------------------------------------------------------------------- /code/extra/python/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $# -lt 1 ]; then 4 | echo "usage: $0 f1 .. fn" 5 | exit 1 6 | fi 7 | 8 | l=0 9 | for fname in $*; do 10 | l=$(wc -l "$fname" | cut -d ' ' -f 1) 11 | echo "$fname": "$l" 12 | done 13 | 14 | exit 0 15 | 16 | -------------------------------------------------------------------------------- /code/lab01/README.md: -------------------------------------------------------------------------------- 1 | # Esercitazione Bash #1 2 | Per utilizzare questa guida con vim utilizzare i seguenti due comandi 3 | 4 | >:set wrap 5 | > 6 | >:set linebreak 7 | > 8 | >:set number 9 | 10 | ## Esercizi 11 | 01. Posizionarsi all'interno della propria home directory (*cd*) 12 | 02. Elencare i file nella directory corrente (*ls*) 13 | 03. Elencare i file nella directory corrente visualizzandone i permessi associati ed includendo anche i file nascosti (i.e., file che iniziano con il carattere .) (*ls*) 14 | 04. Visualizzare il percorso assoluto della directory corrente (*pwd*) 15 | 05. Spostarsi alla radice del filesystem (*cd*) 16 | 06. Spostarsi nella propria home directory utilizzando percorso assoluto (*cd*) 17 | 07. Spostarsi nella radice del filesystem utilizzando un percorso relativo (*cd*) 18 | 08. Ritornare alla propria home utilizzando il comando cd senza argomenti (*cd*) 19 | 09. Copiare il file /etc/passwd nella directory corrente (*cp*) 20 | 10. Copiare il file /etc/passwd nella directory corrente attribuendo al nuovo file il nome passwd.alt (*cp*) 21 | 11. Rinominare il file passwd.alt in passwd.alt.bak e verificare che passwd e passwd.alt.bak siano identici (*mv, diff*) 22 | 12. Verificare che il file passwd contenga la stringa root (*grep*) 23 | 13. Contare il numero di caratteri di cui è composto il file passwd (*wc*) 24 | 14. Contare il numero di linee di cui è composto il file passwd (*wc*) 25 | 15. Cancellare i file passwd e passwd.alt.bak con un'unica istruzione (*rm*) 26 | 16. Creare un file vuoto di nome test (*touch*) 27 | 17. Utilizzando un editor di testo, scrivere all'interno del file test la stringa 'Hello World!' (*nano/micro/vim*) 28 | 18. Visualizzare il contenuto del file test (*cat*) 29 | 19. Rendere il file test leggibile da tutti gli utenti (*chmod*) 30 | 20. Rendere il file test leggibile e scrivibile dal solo proprietario (*chmod*) 31 | 21. Costruire un link hard da test a test2 (*ln*) 32 | 22. Costruire un link soft (simbolico) da test a test3 (*ln*) 33 | 23. Rendere il link test3 *stale* (*rm*) 34 | 24. Creare una directory di nome testdir nella propria home directory (*mkdir*) 35 | 25. Verificare che testdir sia vuota (*ls*) 36 | 26. Creare un file vuoto di nome empty all'interno di testdir senza cambiare directory (*touch*) 37 | 27. Eliminare la directory testdir (*rm/rmdir*) 38 | 28. Visualizzare PID della shell correntemente in esecuzione (*ps*) 39 | 29. Visualizzare il nome del processo con PID = 1 (*ps*) 40 | 30. Visualizzare il nome del proprio utente (*whoami*) 41 | 31. Visualizzare il nome dei gruppi a cui appartiene il proprio utente (*id*) 42 | 32. Utilizzando un editor di testo, creare un file test.sh contenente le seguenti linee: 43 | 44 | ``` 45 | #!/bin/bash 46 | echo "Hello World!" 47 | exit 0 48 | ``` 49 | 50 | 34. Rendere eseguibile test.sh (*chmod*) 51 | 35. Eseguire test.sh 52 | 36. Spostare test.sh all'interno della cartella /tmp (*mv*) 53 | 37. Eseguire test.sh rimanendo nella propria home directory 54 | 38. Mostrare il numero di inode del file test.sh (*stat*) 55 | 39. Acquisire i diritti di amministrazione (*sudo*) 56 | 40. Creare l'utente giuda (*useradd*) 57 | 41. Cambiare la password dell'utente giuda (*passwd*) 58 | 42. Eliminare l'utente giuda (*userdel*) 59 | 43. Abbandonare i diritti di amministrazione (*exit*) 60 | 44. Cambiare la password del proprio utente (*passwd*) 61 | 45. Visualizzare il percorso completo del comando ls (*which*) 62 | 46. Visualizzare il contenuto html della pagina https://www.gnome.org (*curl*) 63 | 47. Visualizzare la data corrente nel formato gg-mm-aaaa (*date*) 64 | 48. Utilizzare il comando expr per sommare e moltiplicare 3 e 7 (*expr*) 65 | 49. Utilizzare il comando expr per sottrarre e dividere 9 e 3 (*expr*) 66 | 50. Avviare il comando mc su un terminale e chiuderlo utilizzando un secondo terminale (*kill/killall*) 67 | 68 | ## Soluzioni 69 | 1. Posizionarsi all'interno della propria home directory (*cd*) 70 | > cd 71 | 72 | 2. Elencare i file nella directory corrente (*ls*) 73 | > ls 74 | 75 | 3. Elencare i file nella directory corrente visualizzandone i permessi associati ed includendo anche i file nascosti (i.e., file che iniziano con il carattere .) (*ls*) 76 | > ls -al 77 | 78 | 4. Visualizzare il percorso assoluto della directory corrente (*pwd*) 79 | > pwd 80 | 81 | 5. Spostarsi alla radice del filesystem (*cd*) 82 | > cd / 83 | 84 | 6. Spostarsi nella propria home directory utilizzando percorso assoluto (*cd*) 85 | > cd /home/nome_utente 86 | 87 | 7. Spostarsi nella radice del filesystem utilizzando un percorso relativo (*cd*) 88 | > cd ../.. 89 | 90 | 8. Ritornare alla propria home (*cd*) 91 | > cd 92 | 93 | 9. Copiare il file /etc/passwd nella directory corrente (*cp*) 94 | > cp /etc/passwd . 95 | 96 | 10. Copiare il file /etc/passwd nella directory corrente attribuendo al nuovo file il nome passwd.alt (*cp*) 97 | > cp /etc/passwd passwd.alt 98 | 99 | 11. Rinominare il file passwd.alt in passwd.alt.bak e verificare che passwd e passwd.alt.bak siano identici (*mv, diff*) 100 | > mv passwd.alt passwd.alt.bak 101 | 102 | > diff passwd passwd.alt.bak (nessun output significa che sono uguali) 103 | 104 | 12. Verificare che il file passwd contenga la stringa root (*grep*) 105 | > grep root /etc/passwd 106 | 107 | 13. Contare il numero di caratteri di cui è composto il file passwd (*wc*) 108 | > wc -c passwd 109 | 110 | 14. Contare il numero di linee di cui è composto il file passwd (*wc*) 111 | > wc -l passwd 112 | 113 | 15. Cancellare i file passwd e passwd.alt.bak con un'unica istruzione (*rm*) 114 | > rm passwd* 115 | 116 | 16. Creare un file vuoto di nome test (*touch*) 117 | > touch test 118 | 119 | 17. Utilizzando un editor di testo, scrivere all'interno del file test la stringa 'Hello World!' (*nano/micro/vim*) 120 | > nano test 121 | 122 | 18. Visualizzare il contenuto del file test (*cat*) 123 | > cat test 124 | 125 | 19. Rendere il file test leggibile da tutti gli utenti (*chmod*) 126 | > chmod 444 test 127 | 128 | 20. Rendere il file test leggibile e scrivibile dal solo proprietario (*chmod*) 129 | > chmod 600 test 130 | 131 | 21. Costruire un link hard da test a test2 (*ln*) 132 | > ln test test2 133 | 134 | 22. Costruire un link soft (simbolico) da test a test3 (*ln*) 135 | > ln -s test test3 136 | 137 | 23. Rendere il link test3 *stale* (*rm*) 138 | > rm test 139 | 140 | 24. Creare una directory di nome testdir nella propria home directory (*mkdir*) 141 | > mkdir testdir 142 | 143 | 25. Verificare che testdir sia vuota (*ls*) 144 | > ls -al testdir 145 | 146 | 26. Creare un file vuoto di nome empty all'interno di testdir senza cambiare directory (*touch*) 147 | > touch testdir/empty 148 | 149 | 27. Eliminare la directory testdir (*rm/rmdir*) 150 | > rm -rf testdir 151 | 152 | 28. Visualizzare PID della shell correntemente in esecuzione (*ps*) 153 | > ps 154 | 155 | 29. Visualizzare il nome del processo con PID = 1 (*ps*) 156 | > ps fax 157 | 158 | 30. Visualizzare il nome del proprio utente (*whoami*) 159 | > whoami 160 | 161 | 31. Visualizzare il nome dei gruppi a cui appartiene il proprio utente (*id*) 162 | > id 163 | 164 | 32. Utilizzando un editor di testo, creare un file test.sh contenente le seguenti linee: 165 | 166 | ``` 167 | #!/bin/bash 168 | echo "Hello World!" 169 | exit 0 170 | ``` 171 | 172 | > nano test.sh 173 | 174 | 34. Rendere eseguibile test.sh (*chmod*) 175 | > chmod 755 test.sh 176 | 177 | 35. Eseguire test.sh 178 | > ./test.sh 179 | 180 | 36. Spostare test.sh all'interno della cartella /tmp (*mv*) 181 | > mv test.sh /tmp 182 | 183 | 37. Eseguire test.sh rimanendo nella propria home directory 184 | > /tmp/test.sh 185 | 186 | 38. Mostrare il numero di inode del file test.sh (*stat*) 187 | > stat /tmp/test.sh 188 | 189 | 39. Acquisire i diritti di amministrazione (*sudo*) 190 | > sudo -i 191 | 192 | 40. Creare l'utente giuda (*useradd*) 193 | > useradd --create-home giuda 194 | 195 | 41. Cambiare la password dell'utente giuda (*passwd*) 196 | > passwd giuda 197 | 198 | 42. Eliminare l'utente giuda (*userdel*) 199 | > userdel --remove giuda 200 | 201 | 43. Abbandonare i diritti di amministrazione (*exit*) 202 | > exit 203 | 204 | 44. Cambiare la password del proprio utente (*passwd*) 205 | > passwd 206 | 207 | 45. Visualizzare il percorso completo del comando ls (*which*) 208 | > which ls 209 | 210 | 46. Visualizzare il contenuto html della pagina https://www.gnome.org (*curl*) 211 | > curl https://www.gnome.org 212 | 213 | 47. Visualizzare la data corrente nel formato gg-mm-aaaa (*date*) 214 | > date +%d-%m-%Y 215 | 216 | 48. Utilizzare il comando expr per sommare e moltiplicare 3 e 7 (*expr*) 217 | > expr 3 + 7 218 | 219 | > expr 3 \\* 7 220 | 221 | 49. Utilizzare il comando expr per sottrarre e dividere 9 e 3 (*expr*) 222 | > expr 9 - 3 223 | 224 | > expr 9 / 3 225 | 226 | 50. Avviare il comando mc su un terminale e chiuderlo utilizzando un secondo terminale (*kill/killall*) 227 | > mc (primo terminale) 228 | 229 | > ps fax (secondo terminale, guardare PID) 230 | 231 | > kill -9 PID (secondo terminale) 232 | 233 | 234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /code/lab02/README.md: -------------------------------------------------------------------------------- 1 | # Esercitazione Bash #2 2 | Per utilizzare questa guida con vim utilizzare i seguenti due comandi 3 | 4 | >:set wrap 5 | > 6 | >:set linebreak 7 | > 8 | >:set number 9 | 10 | ## Esercizi 11 | 01. Creare una directory con nome `esercizi` e spostarsi al suo interno con un solo comando concatenato (*mkdir, cd*). 12 | 02. Copiare il file `/etc/passwd` nella directory corrente (*cp*). 13 | 03. Visualizzare le prime 10 righe del file `/etc/passwd` (*head*). 14 | 04. Visualizzare le penultime 5 righe del file `/etc/passwd` (*head, tail*). 15 | 05. Contare il numero di utenti nel sistema (*wc*). 16 | 06. Visualizzare l'elenco degli utenti con shell bash (*cat*, *cut*, *grep*). 17 | 07. Creare un *alias* per il comando `ls -al` (*alias*). 18 | 08. Visualizzare l'elenco dei file nella directory corrente ordinati per dimensione (*ls*). 19 | 09. Visualizzare l'elenco dei file nella directory corrente ordinati per data di modifica (*ls*). 20 | 10. Creare un file con nome `esempio.txt` e scrivere al suo interno `Questa è una riga di testo` (*echo*). 21 | 11. Appendere `Questa è la seconda riga di testo` al file `esempio.txt` (*echo*). 22 | 12. Visualizzare il contenuto del file `esempio.txt` (*cat*). 23 | 13. Visualizzare l'elenco dei file nella directory corrente con estensione `.txt` (*ls*). 24 | 14. Visualizzare l'elenco dei file nella directory `/etc` con dimensione superiore a 100 KB (*find*). 25 | 15. Eliminare il file `esempio.txt` (*rm*). 26 | 16. Cambiare la directory corrente in `/tmp` (*cd*). 27 | 17. Visualizzare la directory corrente (*pwd*). 28 | 18. Uscire dalla shell (*exit*). 29 | 19. Definire una variabile `nome`, assegnarle il proprio nome, e stamparla (*echo*). 30 | 20. Definire una variabile `cognome`, assegnarle il proprio cognome, e stamparla (*echo*). 31 | 21. Concatenare le variabili `nome` e `cognome` in un'unica stringa (*echo*). 32 | 22. Creare una variabile `numero` con il valore 42, incrementarla di 10 e stamparla. 33 | 23. Creare una variabile `lista` contenente tutti i file nella directory corrente e stamparla (*ls*). 34 | 24. Stampare il nome del file più recentemente modificato nella directory corrente dopo averlo assegnato ad una variabile (*ls, head*). 35 | 25. Creare una variabile `comando` contenente il comando *date* ed eseguire `comando` per ottenere la data corrente. 36 | 26. Calcolare la somma dei numeri da 1 a 5 utilizzando l'espansione aritmetica. 37 | 27. Visualizzare l'elenco dei file nella directory corrente che iniziano per la lettera `a` (*ls*). 38 | 28. Visualizzare l'elenco dei file nella directory `/etc` che contengono la parola `config` (*grep*). 39 | 29. Ordinare i file nella directory corrente per dimensione in ordine crescente (*ls*). 40 | 30. Visualizzare le prime 10 righe del file `/etc/passwd` ordinati per nome utente (*sort, head*). 41 | 31. Trovare tutte le directory all'interno di `/etc` che non sono accessibili e mostrare solo i messaggi di errore (*find*). 42 | 32. Creare un file di 10 KB con caratteri casuali usando il file `/dev/urandom` (*dd*). 43 | 33. Eseguire il comando *ls* solo se il comando *true* ha successo (*true, ls*). 44 | 34. Contare il numero di utenti unici nel file `/etc/passwd` (*sort, uniq, wc*). 45 | 35. Eseguire un comando che stampi il risultato dell'operazione aritmetica 3 * (2 + 5). 46 | 36. Contare il numero di directory nella directory corrente (*ls, grep, wc*). 47 | 37. Stampare la stringa `La directory corrente è -- nome directory corrente --` (*pwd*). 48 | 38. Invocare i comandi *true* e *false* e visualizzarne il valore di uscita (*true, false*). 49 | 39. Mostrare il testo `Directory non trovata` se il comando *ls* fallisce su una directory non esistente (*ls*). 50 | 40. Se la copia del file `file1.txt` in `file2.txt` fallisce, mostrare un messaggio di errore (*cp, echo*). 51 | 41. Utilizzare la ridirezione in input per visualizzare il contenuto di `/etc/passwd` (*cat*). 52 | 42. Utilizzare la ridirezione in output per creare un file `test` contenente la linea `GNU is Not Unix` (*echo*). 53 | 43. Individuare tutti i processi che eseguono una shell (e.g., bash, zsh, ...) utilizzando un file temporaneo (*ps aux, grep*). 54 | 44. Individuare tutti i processi che eseguono una shell (e.g., bash, zsh, ...) utilizzando una pipe (*ps aux, grep*). 55 | 45. Cosa e' cambiato? Il sistema operativo ha creato in modo automatico un file temporaneo oppure no? 56 | 46. Contare tutti i processi che eseguono con i diritti del nostro utente e salvare risultato all'interno di un file di log (*ps, grep, wc*). 57 | 47. Creare la variabile d'ambiente `LOGFILE` ed assegnarle il valore `service.log` (*export*). 58 | 48. Visualizzare il contenuto della variabile `LOGFILE` (*echo*). 59 | 49. Verificare che `LOGFILE` sia una variabile d'ambiente. 60 | 50. Elencare tutti i nomi di file della directory corrente che iniziano con un carattere numerico (*echo*). 61 | 62 | ## Soluzioni 63 | 01. Creare una directory con nome `esercizi` e spostarsi al suo interno con un solo comando concatenato (*mkdir, cd*). 64 | > mkdir esercizi; cd esercizi 65 | 66 | 02. Copiare il file `/etc/passwd` nella directory corrente (*cp*). 67 | > cp /etc/passwd ./ 68 | 69 | 03. Visualizzare le prime 10 righe del file `/etc/passwd` (*head*). 70 | > head -n 10 /etc/passwd 71 | 72 | 04. Visualizzare le penultime 5 righe del file `/etc/passwd` (*head, tail*). 73 | > tail -n 10 /etc/passwd | head -n 5 74 | 75 | 05. Contare il numero di utenti nel sistema (*wc*). 76 | > wc -l /etc/passwd 77 | 78 | 06. Visualizzare l'elenco degli utenti con shell bash (*cat*, *cut*, *grep*). 79 | > cat /etc/passwd | cut -d ":" -f 7 | grep -E "^/bin/bash$" 80 | 81 | 07. Creare un *alias* per il comando `ls -al` (*alias*). 82 | > alias ll='ls -al' 83 | 84 | 08. Visualizzare l'elenco dei file nella directory corrente ordinati per dimensione (*ls*). 85 | > ls -lS 86 | 87 | 09. Visualizzare l'elenco dei file nella directory corrente ordinati per data di modifica (*ls*). 88 | > ls -lt 89 | 90 | 10. Creare un file con nome `esempio.txt` e scrivere al suo interno `Questa è una riga di testo` (*echo*). 91 | > echo "Questa è una riga di testo" > esempio.txt 92 | 93 | 11. Appendere `Questa è la seconda riga di testo` al file `esempio.txt` (*echo*). 94 | > echo "Questa è la seconda riga di testo" >> esempio.txt 95 | 96 | 12. Visualizzare il contenuto del file `esempio.txt` (*cat*). 97 | > cat esempio.txt 98 | 99 | 13. Visualizzare l'elenco dei file nella directory corrente con estensione `.txt` (*ls*). 100 | > ls \*.txt 101 | 102 | 14. Visualizzare l'elenco dei file nella directory `/etc` con dimensione superiore a 100 KB (*find*). 103 | > find /etc -size +100k 104 | 105 | 15. Eliminare il file `esempio.txt` (*rm*). 106 | > rm esempio.txt 107 | 108 | 16. Cambiare la directory corrente in `/tmp` (*cd*). 109 | > cd /tmp 110 | 111 | 17. Visualizzare la directory corrente (*pwd*). 112 | > pwd 113 | 114 | 18. Uscire dalla shell (*exit*). 115 | > exit 116 | 117 | 19. Definire una variabile `nome`, assegnarle il proprio nome, e stamparla (*echo*). 118 | > nome=John 119 | 120 | 20. Definire una variabile `cognome`, assegnarle il proprio cognome, e stamparla (*echo*). 121 | > cognome=Doe 122 | 123 | 21. Concatenare le variabili `nome` e `cognome` in un'unica stringa (*echo*). 124 | > echo "$nome $cognome" 125 | 126 | 22. Creare una variabile `numero` con il valore 42, incrementarla di 10 e stamparla. 127 | > numero=42; ((numero+=10)); echo $numero 128 | 129 | 23. Creare una variabile `lista` contenente tutti i file nella directory corrente e stamparla (*ls*). 130 | > lista=$(ls); echo $lista 131 | 132 | 24. Stampare il nome del file più recentemente modificato nella directory corrente dopo averlo assegnato ad una variabile (*ls, head*). 133 | > file=$(ls -t | head -n 1); echo $file 134 | 135 | 25. Creare una variabile `comando` contenente il comando *date* ed eseguire `comando` per ottenere la data corrente. 136 | > comando="date"; $comando 137 | 138 | 26. Calcolare la somma dei numeri da 1 a 5 utilizzando l'espansione aritmetica. 139 | > echo $((1 + 2 + 3 + 4 + 5)) 140 | 141 | 27. Visualizzare l'elenco dei file nella directory corrente che iniziano per la lettera `a` (*ls*). 142 | > ls a* 143 | 144 | 28. Visualizzare l'elenco dei file nella directory `/etc` che contengono la parola `config` (*grep*). 145 | > grep -r config /etc 146 | 147 | 29. Ordinare i file nella directory corrente per dimensione in ordine crescente (*ls*). 148 | > ls -lSr 149 | 150 | 30. Visualizzare le prime 10 righe del file `/etc/passwd` ordinati per nome utente (*sort, head*). 151 | > sort /etc/passwd | head -n 10 152 | 153 | 31. Trovare tutte le directory all'interno di `/etc` che non sono accessibili e mostrare solo i messaggi di errore (*find*). 154 | > find /etc -type d 1>/dev/null 155 | 156 | 32. Creare un file di 10 KB con caratteri casuali usando il file `/dev/urandom` (*dd*). 157 | > dd if=/dev/urandom of=fout bs=1K count=10 158 | 159 | 33. Eseguire il comando *ls* solo se il comando *true* ha successo (*true, ls*). 160 | > true && ls 161 | 162 | 34. Contare il numero di utenti unici nel file `/etc/passwd` (*sort, uniq, wc*). 163 | > sort /etc/passwd | uniq -u | wc -l 164 | 165 | 35. Eseguire un comando che stampi il risultato dell'operazione aritmetica 3 * (2 + 5). 166 | > echo $((3 * (2 + 5))) 167 | 168 | 36. Contare il numero di directory nella directory corrente (*ls, grep, wc*). 169 | > ls -l | grep ^d | wc -l 170 | 171 | 37. Stampare la stringa `La directory corrente è -- nome directory corrente --` (*pwd*). 172 | > echo "La directory corrente è $(pwd)" 173 | 174 | 38. Invocare i comandi *true* e *false* e visualizzarne il valore di uscita (*true, false*). 175 | > true; echo $?; false; echo $? 176 | 177 | 39. Mostrare il testo `Directory non trovata` se il comando *ls* fallisce su una directory non esistente (*ls*). 178 | > ls directory_inesistente || echo "Directory non trovata" 179 | 180 | 40. Se la copia del file `file1.txt` in `file2.txt` fallisce, mostrare un messaggio di errore (*cp, echo*). 181 | > cp file1.txt file2.txt || echo "Errore durante la copia dei file" 182 | 183 | 41. Utilizzare la ridirezione in input per visualizzare il contenuto di `/etc/passwd` (*cat*). 184 | > cat < /etc/passwd 185 | 186 | 42. Utilizzare la ridirezione in output per creare un file `test` contenente la linea `GNU is Not Unix` (*echo*). 187 | > echo "GNU is Not Unix" > test 188 | 189 | 43. Individuare tutti i processi che eseguono una shell (e.g., bash, zsh, ...) utilizzando un file temporaneo (*ps aux, grep*). 190 | > ps aux > tmpfile; grep bash tmpfile 191 | 192 | 44. Individuare tutti i processi che eseguono una shell (e.g., bash, zsh, ...) utilizzando una pipe (*ps aux, grep*). 193 | > ps aux | grep bash 194 | 195 | 45. Cosa e' cambiato? Il sistema operativo ha creato in modo automatico un file temporaneo oppure no? 196 | > No, la comunicazione fra i due processi è mediata dal kernel. Non avvengono scritture su disco. 197 | 198 | 46. Contare tutti i processi che eseguono con i diritti del nostro utente e salvare risultato all'interno di un file di log (*ps, grep, wc*). 199 | > ps -u $(whoami) | grep -v "grep" | wc -l > log 200 | 201 | 47. Creare la variabile d'ambiente `LOGFILE` ed assegnarle il valore `service.log` (*export*). 202 | > export LOGFILE="service.log" 203 | 204 | 48. Visualizzare il contenuto della variabile `LOGFILE` (*echo*). 205 | > echo $LOGFILE 206 | 207 | 49. Verificare che `LOGFILE` sia una variabile d'ambiente. 208 | > env | grep LOGFILE 209 | 210 | 50. Elencare tutti i nomi di file della directory corrente che iniziano con un carattere numerico (*echo*). 211 | > echo [0-9]* 212 | 213 | -------------------------------------------------------------------------------- /code/lab03/README.md: -------------------------------------------------------------------------------- 1 | # Esercitazione Bash #3 2 | Per utilizzare questa guida con vim utilizzare i seguenti due comandi 3 | 4 | >:set wrap 5 | > 6 | >:set linebreak 7 | > 8 | >:set number 9 | 10 | ## Esercizi 11 | **(es21.sh)** Scrivere uno script che accetta 2 argomenti. Lo script crea un file regolare vuoto il cui nome è specificato dal primo argomento ed i cui permessi (notazione ottale) sono specificati dal secondo parametro. 12 | 13 | **(es22.sh)** Scrivere uno script che accetta 1 argomento. Lo script crea un file regolare il cui nome è specificato dal primo argomento. Il file creato deve a sua volta essere uno script che, quando eseguito, stampa la stringa "Hello World!". 14 | 15 | **(es23.sh)** Scrivere uno script che controlla di essere invocato con 1 argomento che rappresenta un nome di directory esistente e leggibile nel filesystem. 16 | 17 | **(es24.sh)** Scrivere uno script che controlla di essere invocato con 2 argomenti. Il primo argomento deve rappresentare una directory esistente e scrivibile (D), il secondo un nome di file (F). Lo script crea all'interno della directory D un file di nome F contenente il suo percorso assoluto. 18 | 19 | **(es25.sh)** Scrivere uno script che riporta su stdout il primo e l'ultimo argomento con cui è stato invocato. 20 | 21 | **(es26.sh)** Scrivere uno script che accetta 1 argomento e che verifica se tale argomento rappresenta un nome assoluto, relativo, o relativo semplice. 22 | 23 | **(es27.sh)** Scrivere uno script che visualizza tutti i file della directory corrente. Tutti i file con estensione .sh devono essere stampati su stderr invece che stdout. 24 | 25 | **(es28.sh)** Scrivere uno script che visualizza tutti i file della directory corrente che contengono la stringa #!/bin/sh 26 | 27 | **(es29.sh)** Scrivere uno script che controlla di essere invocato con 1 argomento e che tale argomento rappresenta un nome di directory esistente. Lo script scorre il contenuto della directory e stampa una F prima di ogni file regolare ed una D prima di ogni directory. Lo script infine riporta su stdout il numero totale di file e di directory stampate. 28 | 29 | **(es210.sh)** Scrivere una script che controlla di essere invocato con 2 argomenti che rappresentano una directory (D) e un numero positivo (N). Lo script, 1 volta ogni N secondi (vedi sleep), conta i file e le directory presenti in D. Se il numero cambia rispetto al controllo precedente, stampa un messaggio di avviso. 30 | 31 | --- 32 | 33 | **(es31.sh)** Scrivere uno script che prevede 1 parametro che deve essere il nome assoluto di una directory (D). Lo script deve esplorare ricorsivamente D e stampare, per ogni sotto cartella, il livello di profondità raggiunto. D avrà livello 0, le sottocartelle di D avranno livello 1, e così via. Da risolvere sia utilizzando una funzione ricorsiva, che utilizzando il comando find. Nel caso di find, l'opzione -printf può essere utile (vedi manuale). 34 | 35 | **(es32.sh)** Scrivere uno script che prevede 4 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), il secondo deve essere una stringa (F), il terzo (N1) ed il quarto (N2) vanno trattati come numeri interi positivi. Lo script cerca ricorsivamente in D tutti i file di nome F la cui lunghezza in linee è compresa fra N1 e N2. 36 | 37 | **(es33.sh)** Scrivere uno script che prevede 2 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo è un singolo carattere numerico (N). Lo script cerca ricorsivamente in D tutti i file che contengono sia nel nome che nel contenuto N. 38 | 39 | **(es34.sh)** Scrivere uno script che prevede 2 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo va trattato come un numero intero positivo (N). Lo script cerca ricorsivamente in D tutte le directory che contengono un numero di file (solo file non directory) compreso fra 1 e N. 40 | 41 | **(es35.sh)** Scrivere uno script che prevede 2 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo va trattato come un numero intero positivo (N). Lo script cerca ricorsivamente in D tutte le directory che contengono almeno un file (solo file non directory) con dimensione in byte maggiore di N. Ordinare (in modo decrescente) le directory trovate in base numero di file (che contengono) che rispettano il criterio specificato sopra. 42 | 43 | **(es36.sh)** Scrivere uno script che prevede 2 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo va trattato come un nome semplice di file (F). Lo script cerca ricorsivamente in D tutti i file di nome F, ne stampa il percorso completo su standard output, e mostra anche le prima e le ultime due linee del suo contenuto. 44 | 45 | **(es37.sh)** Scrivere uno script che prevede 2 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo va trattato come un nome semplice di file (F). Lo script cerca ricorsivamente in D tutti i file di nome F, e crea un link simbolico ad ognuno di essi all'interno della cartella /tmp. I link simbolici creati devono avere un nome nella forma /tmp/link_1 .. /tmp/link_n. 46 | 47 | **(es38.sh)** Scrivere uno script che prevede 3 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo ed il terzo vanno trattati come nomi semplici di file (F1), (F2). Lo script cerca ricorsivamente in D tutte le directory che contengono entrambi i file F1 ed F2, ne stampa il percorso completo, e mostra anche la somma della dimensione in byte di F1 ed F2. 48 | 49 | **(es39.sh)** Scrivere uno script che prevede 3 parametri. Il primo parametro deve essere il nome assoluto di una directory (D), mentre il secondo ed il terzo vanno trattati come nomi semplici di file (F1), (F2). Lo script cerca ricorsivamente in D per verificare che esistano (anche in directory diverse) almeno 1 file di nome F1 ed almeno 1 file di nome F2. Stampare il numero di occorrenze di F1 ed F2. 50 | 51 | --- 52 | 53 | **(es41.sh)** Lo script prevede 2 parametri. Il parametro -d specifica il nome assoluto di una directory. Il parametro -m può assumere due valori EQ|NE. Lo script cerca ricorsivamente nella directory indicata da -d, tutte le directory in cui il numero di file contenuti e il numero di sotto directory contenute sia uguale (se -m=EQ) o diverso (se -m=NE). 54 | 55 | ``` 56 | usage: $0 [-h] -d dirname -m EQ|NE 57 | ``` 58 | 59 | **(es42.sh)** Lo script prevede un numero variabile di parametri. Il parametro -f indica un nome semplice di file, mentre gli altri devono essere nomi assoluti di directory (d1, ..., dn). Per ognuna delle n directory, lo script cerca al suo interno tutti i file leggibili di nome indicato da -f. Lo script stampa su stdout la somma delle dimensioni dei file trovati sia per ogni directory d1, ..., dn che globalmente. 60 | 61 | ``` 62 | usage: $0 [-h] -f filename d1 .. dn 63 | ``` 64 | 65 | **(es43.sh)** Lo script prevede un numero variabile di parametri. Il parametro -f indica un nome semplice di file. Il parametro -l indica un numero positivo. Se -l non viene utilizzato il valore di default è 10. Gli altri devono essere nomi assoluti di directory (d1, ..., dn). Per ognuna delle n directory, lo script cerca al suo interno tutti i file leggibili di nome indicato da -f la cui dimensione in linee sia maggiore o uguale al valore indicato da -l. Per ogni file trovato, lo script ne riporta la penultima linea all'interno del file /tmp/script.log. 66 | 67 | ``` 68 | usage: $0 [-h] [-l lines] -f filename d1 .. dn 69 | ``` 70 | 71 | **(es44.sh)** Lo script prevede un numero variabile di parametri. Il parametro -f indica un nome semplice di file, mentre gli altri devono essere nomi assoluti di directory (d1, ..., dn). Per ognuna delle n directory, lo script cerca al suo interno tutti i file leggibili di nome indicato da -f. Per ogni file trovato, lo script ne riporta il nome e la dimensione (in bytes e in linee) all'interno del file /tmp/script.log con il seguente formalismo: nomefile:bytes:linee. 72 | 73 | ``` 74 | usage: $0 [-h] -f filename d1 .. dn 75 | ``` 76 | 77 | **(es45.sh)** Lo script prevede un numero variabile di parametri che rappresentano nomi assoluti di directory (d1, ..., dn). Per ognuna delle n directory, lo script cerca al suo interno tutti i file leggibili modificati nelle ultime 24h (vedi opzioni del comando find). Per ogni file trovato, lo script ne riporta il nome su stdout e ne crea un link simbolico all'interno della cartella /tmp/links. Per evitare omonimie i link terminano con un intero crescente. Ad esempio, fa.1, fb.2, .. , fxyz.n 78 | 79 | ``` 80 | usage: $0 [-h] d1 .. dn 81 | ``` 82 | 83 | **(es46.sh)** Lo script prevede 3 parametri. Il parametro -d specifica il nome assoluto di una directory. I parametri -a, -b rappresentano numeri positivi (A),(B). Lo script cerca ricorsivamente nella directory indicata da -d, tutte le directory in cui la somma delle dimensioni dei file leggibili presenti è compresa fra A e B. Tutte le directory trovate, vengono inoltre salvate all'interno del file /tmp/script.log e ordinate in base alla somma delle dimensioni dei file contenuti. 84 | 85 | ``` 86 | usage: $0 [-h] -a A -b B -d dirname 87 | ``` 88 | 89 | **(es47.sh)** Lo script prevede un numero variabile di parametri che rappresentano nomi semplici di file (f1, ..., fn). Lo script cerca ricorsivamente nella directory indicata da -d, tutte le directory che contengono almeno un file fra quelli indicati. Tutte le directory trovate, vengono salvate all'interno del file /tmp/script.log e ordinate per il numero di file trovati fra quelli indicati. 90 | 91 | ``` 92 | usage: $0 [-h] -d dirname f1 .. fn 93 | ``` 94 | 95 | **(es48.sh)** Lo script prevede un numero variabile di parametri che rappresentano nomi semplici di file (f1, ..., fn). Lo script verifica che *globalmente* nella directory indicata da -d, esistano i files f1 .. fn. In caso non siano tutti presenti, lo script mostra i nomi dei files non trovati. 96 | 97 | ``` 98 | usage: $0 [-h] -d dirname f1 .. fn 99 | ``` 100 | 101 | **(es49.sh)** Lo script prevede un numero variabile di parametri che rappresentano nomi assoluti di directory (d1, ..., dn). Lo script prevede inoltre l'utilizzo del parametro -t per specificare un'estensione di file. Lo script calcola la dimensione media (in bytes) dei file che terminano con l'estensione indicata per ogni directory indicata. 102 | 103 | ``` 104 | usage: $0 [-h] -t extension d1 .. dn 105 | ``` -------------------------------------------------------------------------------- /code/lab03/es21.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 filename permissions" 5 | exit 1 6 | } 7 | 8 | if [ $# -ne 2 ]; then 9 | usage 10 | fi 11 | 12 | if [ -f "$1" ]; then 13 | usage 14 | fi 15 | 16 | touch "$1" 17 | chmod "$2" "$1" 18 | 19 | exit 0 20 | 21 | -------------------------------------------------------------------------------- /code/lab03/es210.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | USAGE="usage: $0 dirname seconds" 4 | 5 | count() { 6 | tmpF=0 7 | tmpD=0 8 | for fname in "$1"/*; do 9 | if [ -f "$fname" ]; then 10 | tmpF=$(( tmpF + 1 )) 11 | elif [ -d "$fname" ]; then 12 | tmpD=$(( tmpD + 1 )) 13 | fi 14 | done 15 | } 16 | 17 | # Check the number of parameters 18 | if [ $# -ne 2 ]; then 19 | echo "$USAGE" 20 | exit 1 21 | fi 22 | 23 | # Check if "$1" is a directory and explorable 24 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 25 | echo "$USAGE" 26 | exit 1 27 | fi 28 | 29 | # checks if "$2" is a number 30 | case "$2" in 31 | ''|*[!0-9]*) 32 | echo "$USAGE" 33 | exit 1 34 | ;; 35 | *) 36 | ;; 37 | esac 38 | 39 | if [ $? -gt 1 ]; then 40 | echo "$USAGE" 41 | exit 1 42 | fi 43 | 44 | # init phase 45 | count "$1" 46 | lastF=$tmpF 47 | lastD=$tmpD 48 | 49 | # check phase 50 | echo "[$(date +%H:%M)] $1 monitored..." 51 | while true; do 52 | count "$1" 53 | if [ $lastF -ne $tmpF ] || [ $lastD -ne $tmpD ]; then 54 | echo "[$(date +%H:%M)] $1 changed" 55 | lastF=$tmpF 56 | lastD=$tmpD 57 | fi 58 | sleep "$2" 59 | done 60 | 61 | exit 0 62 | 63 | -------------------------------------------------------------------------------- /code/lab03/es22.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 filename" 5 | exit 1 6 | } 7 | if [ $# -ne 1 ]; then 8 | usage 9 | fi 10 | 11 | echo '#!/bin/bash' > "$1" 12 | echo 'echo "Hello World!"' >> "$1" 13 | echo 'exit 0' >> "$1" 14 | 15 | chmod 755 "$1" 16 | 17 | exit 0 18 | 19 | -------------------------------------------------------------------------------- /code/lab03/es23.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 arg (readable directory)" 5 | exit 1 6 | } 7 | 8 | if [ $# -ne 1 ]; then 9 | usage 10 | fi 11 | 12 | if [ ! -d "$1" ] || [ ! -r "$1" ]; then 13 | usage 14 | fi 15 | 16 | exit 0 17 | -------------------------------------------------------------------------------- /code/lab03/es24.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 arg1 (writable directory) arg2" 5 | exit 1 6 | } 7 | 8 | if [ $# -ne 2 ]; then 9 | usage 10 | fi 11 | 12 | if [ ! -d "$1" ] || [ ! -w "$1" ]; then 13 | usage 14 | fi 15 | 16 | cd "$1" || exit 1 17 | pwd > "$2" 18 | 19 | exit 0 20 | 21 | -------------------------------------------------------------------------------- /code/lab03/es25.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 arg1 arg2 .. argn" 5 | exit 1 6 | } 7 | 8 | if [ $# -lt 2 ]; then 9 | usage 10 | fi 11 | 12 | echo "first arg: $1" 13 | 14 | while [ $# -gt 1 ]; do 15 | shift 16 | done 17 | 18 | echo "last arg: $1" 19 | 20 | exit 0 21 | -------------------------------------------------------------------------------- /code/lab03/es26.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 arg" 5 | exit 1 6 | } 7 | 8 | if [ $# -ne 1 ]; then 9 | usage 10 | fi 11 | 12 | case "$1" in 13 | /*) echo "Absolute filename" 14 | ;; 15 | */*) echo "Relative filename" 16 | ;; 17 | *) echo "Simple, relative filename" 18 | ;; 19 | esac 20 | 21 | exit 0 22 | -------------------------------------------------------------------------------- /code/lab03/es27.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Both stdout and stderr are attached to the same terminal 4 | # For testing try the following commands: 5 | # $ ./es27.sh 1>/dev/null (shows only stderr) 6 | # $ ./es27.sh 2>/dev/null (shows only stout) 7 | 8 | for fname in *; do 9 | case "$fname" in 10 | *.sh) echo "$fname" 1>&2 11 | ;; 12 | *) echo "$fname" 13 | ;; 14 | esac 15 | done 16 | 17 | exit 0 18 | 19 | -------------------------------------------------------------------------------- /code/lab03/es28.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for fname in *; do 4 | # output is discarded because we are looking for $? 5 | if grep "#!/bin/bash" "$fname" 1>/dev/null 2>&1; then 6 | echo "$fname matches!" 7 | fi 8 | done 9 | 10 | exit 0 11 | 12 | -------------------------------------------------------------------------------- /code/lab03/es29.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname" 5 | exit 1 6 | } 7 | 8 | # Check the number of parameters 9 | if [ $# -ne 1 ]; then 10 | usage 11 | fi 12 | 13 | # Check if "$1" is a directory and explorable 14 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 15 | usage 16 | fi 17 | 18 | # Main body 19 | F=0 20 | D=0 21 | for fname in "$1"/*; do 22 | if [ -f "$fname" ]; then 23 | echo [F] "$fname" 24 | F=$(( F + 1 )) 25 | fi 26 | 27 | if [ -d "$fname" ]; then 28 | echo [D] "$fname" 29 | D=$(( D + 1 )) 30 | fi 31 | done 32 | 33 | echo "#files =" "$F" 34 | echo "#directories =" "$D" 35 | 36 | exit 0 37 | -------------------------------------------------------------------------------- /code/lab03/es31_find.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs)" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 1 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | # Main body 24 | find "$1" -type d -printf "[%d] %p\n" 25 | 26 | exit 0 27 | -------------------------------------------------------------------------------- /code/lab03/es31_recursive.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs)" 5 | exit 1 6 | } 7 | 8 | # Functions 9 | recurse_dir() { 10 | # $1 is the directory name 11 | # $2 is current depth level 12 | 13 | cd "$1" || return 14 | 15 | # Scan for sub-directories 16 | for fname in *; do 17 | if [ -d "$fname" ] && [ -x "$fname" ]; then 18 | recurse_dir "$fname" $(( "$2" + 1 )) 19 | fi 20 | done 21 | 22 | # Process current directory 23 | echo ["$2"] "$(pwd)" 24 | 25 | cd .. 26 | } 27 | 28 | # Check arguments 29 | if [ $# -ne 1 ]; then 30 | usage 31 | fi 32 | 33 | case "$1" in 34 | /*) ;; 35 | *) usage 36 | ;; 37 | esac 38 | 39 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 40 | usage 41 | fi 42 | 43 | # Variables 44 | D="$1" 45 | L=0 46 | 47 | # Main body 48 | recurse_dir "$D" "$L" 49 | 50 | exit 0 51 | 52 | -------------------------------------------------------------------------------- /code/lab03/es32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dir(abs) str n1 n2" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 4 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$3" in 24 | ''|*[!0-9]*) usage 25 | ;; 26 | *) 27 | ;; 28 | esac 29 | 30 | case "$4" in 31 | ''|*[!0-9]*) 32 | usage 33 | ;; 34 | *) 35 | ;; 36 | esac 37 | 38 | # Main body 39 | list=$(find "$1" -type f -name "$2" -readable 2>/dev/null) 40 | for item in $list; do 41 | lines=$(wc -l < "$item") 42 | if [ "$lines" -ge "$3" ] && [ "$lines" -le "$4" ]; then 43 | echo "$item" ["$lines" lines] 44 | fi 45 | done 46 | 47 | exit 0 48 | -------------------------------------------------------------------------------- /code/lab03/es33.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) n" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 2 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | [0-9]) ;; 25 | *) usage 26 | ;; 27 | esac 28 | 29 | # Main body 30 | list=$(find "$1" -type f -name "*$2*" -readable 2>/dev/null) 31 | for item in $list; do 32 | if grep "$2" <"$item" 1>/dev/null 2>&1; then 33 | echo "$item" 34 | fi 35 | done 36 | 37 | exit 0 -------------------------------------------------------------------------------- /code/lab03/es34.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) n" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 2 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | ''|*[!0-9]*) usage 25 | ;; 26 | *) 27 | ;; 28 | esac 29 | 30 | # Main body 31 | list=$(find "$1" -type d -executable 2>/dev/null) 32 | for item in $list; do 33 | nfiles=$(find "$item" -maxdepth 1 -type f | wc -l 2>/dev/null) 34 | if [ "$nfiles" -ge 1 ] && [ "$nfiles" -le "$2" ]; then 35 | echo "$item" ["$nfiles" files] 36 | fi 37 | done 38 | 39 | exit 0 -------------------------------------------------------------------------------- /code/lab03/es35.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Da invocare nel seguente modo per rispettare la specifica riguardo l'ordinamento 4 | # $ ./es35.sh D N | sort -rn | cut -d ':' -f 2 5 | 6 | usage() { 7 | echo "usage: $0 dirname(abs) n" 8 | exit 1 9 | } 10 | 11 | # Check arguments 12 | if [ $# -ne 2 ]; then 13 | usage 14 | fi 15 | 16 | case "$1" in 17 | /*) ;; 18 | *) usage 19 | ;; 20 | esac 21 | 22 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 23 | usage 24 | fi 25 | 26 | case "$2" in 27 | ''|*[!0-9]*) usage 28 | ;; 29 | *) 30 | ;; 31 | esac 32 | 33 | # Main body 34 | list=$(find "$1" -type d -executable 2>/dev/null) 35 | for item in $list; do 36 | nfiles=$(find "$item" -maxdepth 1 -type f -size +"$2"c | wc -l 2>/dev/null) 37 | if [ "$nfiles" -ge 1 ]; then 38 | echo "$nfiles":"$item" 39 | fi 40 | done 41 | 42 | exit 0 -------------------------------------------------------------------------------- /code/lab03/es36.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) filename" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 2 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | */*) usage 25 | ;; 26 | *) ;; 27 | esac 28 | 29 | # Main body 30 | list=$(find "$1" -type f -readable -name "$2" 2>/dev/null) 31 | for item in $list; do 32 | nlines=$(wc -l < "$item") 33 | echo "$item" 34 | 35 | echo -n "1: " 36 | head -n 1 < "$item" 37 | 38 | echo -n "2: " 39 | head -n 2 < "$item" | tail -n 1 40 | 41 | echo -n "$(( nlines - 1 )): " 42 | tail -n 2 < "$item" | head -n 1 43 | 44 | echo -n "$nlines: " 45 | tail -n 1 < "$item" 46 | done 47 | 48 | exit 0 49 | -------------------------------------------------------------------------------- /code/lab03/es37.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) filename" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 2 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | */*) usage 25 | ;; 26 | *) ;; 27 | esac 28 | 29 | # Main body 30 | n=0 31 | list=$(find "$1" -type f -readable -name "$2" 2>/dev/null) 32 | for src in $list; do 33 | dst=/tmp/link_"$n" 34 | echo "$src" "-->" "$dst" 35 | rm -rf "$dst" 36 | ln -s "$src" "$dst" 37 | n=$(( n + 1 )) 38 | done 39 | 40 | exit 0 41 | -------------------------------------------------------------------------------- /code/lab03/es38.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) filename filename" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 3 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | */*) usage 25 | ;; 26 | *) ;; 27 | esac 28 | 29 | case "$3" in 30 | */*) usage 31 | ;; 32 | *) ;; 33 | esac 34 | 35 | # Main body 36 | list=$(find "$1" -type d -executable 2>/dev/null) 37 | for item in $list; do 38 | [ ! -f "$item"/"$2" ] || [ ! -r "$item"/"$2" ] && continue 39 | [ ! -f "$item"/"$3" ] || [ ! -r "$item"/"$3" ] && continue 40 | size_1=$(wc -c < "$item"/"$2") 41 | size_2=$(wc -c < "$item"/"$3") 42 | echo ["$item": size=$(( size_1 + size_2))] 43 | done 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /code/lab03/es39.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 dirname(abs) filename filename" 5 | exit 1 6 | } 7 | 8 | # Check arguments 9 | if [ $# -ne 3 ]; then 10 | usage 11 | fi 12 | 13 | case "$1" in 14 | /*) ;; 15 | *) usage 16 | ;; 17 | esac 18 | 19 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 20 | usage 21 | fi 22 | 23 | case "$2" in 24 | */*) usage 25 | ;; 26 | *) ;; 27 | esac 28 | 29 | case "$3" in 30 | */*) usage 31 | ;; 32 | *) ;; 33 | esac 34 | 35 | # Main body 36 | count_f1=$(find "$1" -type f -readable -name "$2" 2>/dev/null | wc -l) 37 | count_f2=$(find "$1" -type f -readable -name "$3" 2>/dev/null | wc -l) 38 | 39 | if [ "$count_f1" -gt 0 ] && [ "$count_f2" -gt 0 ]; then 40 | echo "Test passed:" "$1" 41 | echo "$2": "$count_f1" 42 | echo "$3": "$count_f2" 43 | fi 44 | 45 | exit 0 46 | -------------------------------------------------------------------------------- /code/lab03/es41.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 [-h] -d dirname -m EQ|NE" 1>&2 5 | exit 1 6 | } 7 | 8 | while getopts "d:m:h" o; do 9 | case "$o" in 10 | d) D="$OPTARG" 11 | case "$D" in 12 | /*) [ ! -d "$D" ] && usage 13 | [ ! -x "$D" ] && usage 14 | ;; 15 | *) usage 16 | ;; 17 | esac 18 | ;; 19 | m) M="$OPTARG" 20 | case "$M" in 21 | EQ|NE) ;; 22 | *) usage 23 | ;; 24 | esac 25 | ;; 26 | h) usage 27 | ;; 28 | *) usage 29 | ;; 30 | esac 31 | done 32 | 33 | # Shift parameters away. In this case, useless. 34 | shift $(( OPTIND - 1 )) 35 | 36 | # Check if parameters have been acquired 37 | [ -z "$D" ] || [ -z "$M" ] && usage 38 | 39 | # Main body 40 | list=$(find "$D" -type d 2>/dev/null) 41 | for item in $list; do 42 | ndirs=$(find "$item" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l) 43 | nfiles=$(find "$item" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l) 44 | [ "$M" == "EQ" ] && [ "$ndirs" -eq "$nfiles" ] && echo "$item" ["$ndirs" dirs, "$nfiles" files] 45 | [ "$M" == "NE" ] && [ "$ndirs" -ne "$nfiles" ] && echo "$item" ["$ndirs" dirs, "$nfiles" files] 46 | done 47 | 48 | exit 0 49 | -------------------------------------------------------------------------------- /code/lab03/es42.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 [-h] -f filename d1 .. dn" 1>&2 5 | exit 1 6 | } 7 | 8 | # Process arguments 9 | while getopts "f:h" o; do 10 | case "$o" in 11 | f) F="$OPTARG" 12 | case "$F" in 13 | */*) usage 14 | ;; 15 | *) ;; 16 | esac 17 | ;; 18 | h) usage 19 | ;; 20 | *) usage 21 | ;; 22 | esac 23 | done 24 | 25 | # Shift parameters away. $1 become first directory 26 | shift $(( OPTIND - 1 )) 27 | 28 | # Check parameters 29 | [ $# -lt 1 ] && usage 30 | [ -z "$F" ] && usage 31 | 32 | # Check dirs 33 | for dname in "$@"; do 34 | case "$dname" in 35 | /*) 36 | [ ! -d "$dname" ] || [ ! -x "$dname" ] && usage 37 | ;; 38 | *) 39 | usage 40 | ;; 41 | esac 42 | done 43 | 44 | # Main body 45 | tsize=0 46 | for dname in "$@"; do 47 | dsize=0 48 | list=$(find "$dname" -type f -readable -name "$F" 2>/dev/null) 49 | for item in $list; do 50 | size=$(wc -c < "$item") 51 | dsize=$(( dsize + size )) 52 | echo "$item" ["$size" bytes] 53 | done 54 | tsize=$(( tsize + dsize )) 55 | echo ["$dname": "$dsize" bytes] 56 | done 57 | 58 | echo [total: "$tsize" bytes] 59 | 60 | exit 0 61 | -------------------------------------------------------------------------------- /code/lab03/es43.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG="/tmp/script.log" 4 | LINES=10 5 | 6 | usage() { 7 | echo "usage: $0 [-h] [-l lines] -f filename d1 .. dn" 1>&2 8 | exit 1 9 | } 10 | 11 | # Process arguments 12 | while getopts "f:l:h" o; do 13 | case "$o" in 14 | f) F="$OPTARG" 15 | case "$F" in 16 | */*) usage 17 | ;; 18 | *) ;; 19 | esac 20 | ;; 21 | l) LINES="$OPTARG" 22 | case "$LINES" in 23 | ''|*[!0-9]*) 24 | usage 25 | ;; 26 | *) 27 | [ "$LINES" -lt 2 ] && usage 28 | ;; 29 | esac 30 | ;; 31 | h) usage 32 | ;; 33 | *) usage 34 | ;; 35 | esac 36 | done 37 | 38 | # Shift parameters away. $1 become first directory 39 | shift $(( OPTIND - 1 )) 40 | 41 | # Check parameters 42 | [ $# -lt 1 ] && usage 43 | [ -z "$F" ] && usage 44 | 45 | # Check dirs 46 | for dname in "$@"; do 47 | case "$dname" in 48 | /*) 49 | [ ! -d "$dname" ] || [ ! -x "$dname" ] && usage 50 | ;; 51 | *) 52 | usage 53 | ;; 54 | esac 55 | done 56 | 57 | # Main body 58 | rm -rf "$LOG" 59 | for dname in "$@"; do 60 | list=$(find "$dname" -type f -readable -name "$F" 2>/dev/null) 61 | for item in $list; do 62 | if [ "$(wc -l < "$item")" -ge "$LINES" ]; then 63 | echo "$item" 64 | tail -n 2 "$item" | head -n 1 >> "$LOG" 65 | fi 66 | done 67 | done 68 | 69 | exit 0 70 | -------------------------------------------------------------------------------- /code/lab03/es44.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG="/tmp/script.log" 4 | 5 | usage() { 6 | echo "usage: $0 [-h] -f filename d1 .. dn" 1>&2 7 | exit 1 8 | } 9 | 10 | # Process arguments 11 | while getopts "f:h" o; do 12 | case "$o" in 13 | f) F="$OPTARG" 14 | case "$F" in 15 | */*) usage 16 | ;; 17 | *) ;; 18 | esac 19 | ;; 20 | h) usage 21 | ;; 22 | *) usage 23 | ;; 24 | esac 25 | done 26 | 27 | # Shift parameters away. $1 become first directory 28 | shift $(( OPTIND - 1 )) 29 | 30 | # Check parameters 31 | [ $# -lt 1 ] && usage 32 | [ -z "$F" ] && usage 33 | 34 | # Check dirs 35 | for dname in "$@"; do 36 | case "$dname" in 37 | /*) 38 | [ ! -d "$dname" ] || [ ! -x "$dname" ] && usage 39 | ;; 40 | *) 41 | usage 42 | ;; 43 | esac 44 | done 45 | 46 | # Main body 47 | rm -rf "$LOG" 48 | for dname in "$@"; do 49 | list=$(find "$dname" -type f -readable -name "$F" 2>/dev/null) 50 | for item in $list; do 51 | bytes=$(wc -c < "$item") 52 | lines=$(wc -l < "$item") 53 | echo "$item" 54 | echo "$item":"$bytes":"$lines" >> "$LOG" 55 | done 56 | done 57 | 58 | exit 0 -------------------------------------------------------------------------------- /code/lab03/es45.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LINKDIR="/tmp/links" 4 | 5 | usage() { 6 | echo "usage: $0 d1 .. dn" 1>&2 7 | exit 1 8 | } 9 | 10 | # Process arguments 11 | while getopts "h" o; do 12 | case "$o" in 13 | h) usage 14 | ;; 15 | *) usage 16 | ;; 17 | esac 18 | done 19 | 20 | # Shift parameters away. $1 become first directory 21 | shift $(( OPTIND - 1 )) 22 | 23 | # Check parameters 24 | [ $# -lt 1 ] && usage 25 | 26 | # Check dirs 27 | for dname in "$@"; do 28 | case "$dname" in 29 | /*) 30 | [ ! -d "$dname" ] || [ ! -x "$dname" ] && usage 31 | ;; 32 | *) 33 | usage 34 | ;; 35 | esac 36 | done 37 | 38 | # Main body 39 | rm -rf "$LINKDIR" 40 | mkdir -p "$LINKDIR" 41 | count=0 42 | for dname in "$@"; do 43 | list=$(find "$dname" -type f -readable -mtime -1 2>/dev/null) 44 | for item in $list; do 45 | echo "$item" 46 | ln -s "$item" "$LINKDIR"/"$(basename "$item")"."$count" 47 | count=$(( count + 1 )) 48 | done 49 | done 50 | 51 | exit 0 -------------------------------------------------------------------------------- /code/lab03/es46.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG=/tmp/script.log 4 | 5 | usage() { 6 | echo "usage: $0 [-h] -a A -b B -d dirname" 1>&2 7 | exit 1 8 | } 9 | 10 | while getopts "d:a:b:h" o; do 11 | case "$o" in 12 | d) D="$OPTARG" 13 | case "$D" in 14 | /*) 15 | [ ! -d "$D" ] || [ ! -x "$D" ] && usage 16 | ;; 17 | *) 18 | usage 19 | ;; 20 | esac 21 | ;; 22 | a) A="$OPTARG" 23 | case "$A" in 24 | ''|*[!0-9]*) 25 | usage 26 | ;; 27 | *) 28 | [ "$A" -lt 0 ] && usage 29 | ;; 30 | esac 31 | ;; 32 | b) B="$OPTARG" 33 | case "$B" in 34 | ''|*[!0-9]*) 35 | usage 36 | ;; 37 | *) 38 | [ "$B" -lt 0 ] && usage 39 | ;; 40 | esac 41 | ;; 42 | h) usage 43 | ;; 44 | *) usage 45 | ;; 46 | esac 47 | done 48 | 49 | # Shift parameters away. In this case, useless. 50 | shift $(( OPTIND - 1 )) 51 | 52 | # Check if parameters have been acquired 53 | [ -z "$D" ] && usage 54 | [ -z "$A" ] && usage 55 | [ -z "$B" ] && usage 56 | 57 | # Main body 58 | echo "processing..." 59 | rm -rf "$LOG" 60 | list=$(find "$D" -type d 2>/dev/null) 61 | for item in $list; do 62 | files=$(find "$item" -maxdepth 1 -type f -readable 2>/dev/null) 63 | tsize=0 64 | for file in $files; do 65 | fsize=$(wc -c < "$file") 66 | tsize=$(( tsize + fsize )) 67 | done 68 | if [ "$tsize" -ge "$A" ] && [ "$tsize" -le "$B" ]; then 69 | echo "$tsize;""$item" 70 | echo "$tsize;""$item" >> "$LOG" 71 | fi 72 | done 73 | 74 | echo "summary..." 75 | sort -n "$LOG" 76 | 77 | exit 0 78 | -------------------------------------------------------------------------------- /code/lab03/es47.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LOG=/tmp/script.log 4 | 5 | usage() { 6 | echo "usage: $0 [-h] -d dirname f1 .. fn" 1>&2 7 | exit 1 8 | } 9 | 10 | while getopts "d:h" o; do 11 | case "$o" in 12 | d) D="$OPTARG" 13 | case "$D" in 14 | /*) [ ! -d "$D" ] || [ ! -x "$D" ] && usage 15 | ;; 16 | *) usage 17 | ;; 18 | esac 19 | ;; 20 | h) usage 21 | ;; 22 | *) usage 23 | ;; 24 | esac 25 | done 26 | 27 | # Shift parameters away. In this case, useless. 28 | shift $(( OPTIND - 1 )) 29 | 30 | # Check if parameters have been acquired 31 | [ $# -eq 0 ] && usage 32 | [ -z "$D" ] && usage 33 | 34 | # Main body 35 | echo "processing..." 36 | rm -rf "$LOG" 37 | dirs=$(find "$D" -type d 2>/dev/null) 38 | for dir in $dirs; do 39 | found=0 40 | for file in "$@"; do 41 | if [ -f "$dir"/"$file" ] && [ -r "$dir"/"$file" ]; then 42 | found=$(( found + 1 )) 43 | fi 44 | done 45 | if [ $found -gt 0 ]; then 46 | echo "$found;""$dir" 47 | echo "$found;""$dir" >> "$LOG" 48 | fi 49 | done 50 | 51 | echo "summary..." 52 | sort -n "$LOG" 53 | 54 | exit 0 55 | -------------------------------------------------------------------------------- /code/lab03/es48.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | usage() { 4 | echo "usage: $0 [-h] -d dirname f1 .. fn" 1>&2 5 | exit 1 6 | } 7 | 8 | while getopts "d:h" o; do 9 | case "$o" in 10 | d) D="$OPTARG" 11 | case "$D" in 12 | /*) [ ! -d "$D" ] || [ ! -x "$D" ] && usage 13 | ;; 14 | *) usage 15 | ;; 16 | esac 17 | ;; 18 | h) usage 19 | ;; 20 | *) usage 21 | ;; 22 | esac 23 | done 24 | 25 | # Shift parameters away. In this case, useless. 26 | shift $(( OPTIND - 1 )) 27 | 28 | # Check if parameters have been acquired 29 | [ $# -eq 0 ] && usage 30 | [ -z "$D" ] && usage 31 | 32 | for file in "$@"; do 33 | case "$file" in 34 | */*) 35 | usage 36 | ;; 37 | *) 38 | ;; 39 | esac 40 | done 41 | 42 | # Main body 43 | missing=false 44 | missing_list="" 45 | for file in "$@"; do 46 | nfound=$(find "$D" -type f -name "$file" 2>/dev/null | wc -l) 47 | if [ "$nfound" -eq 0 ]; then 48 | missing=true 49 | missing_list="$missing_list $file" 50 | fi 51 | done 52 | 53 | if [ "$missing" == "false" ]; then 54 | echo "[Test passed]" 55 | else 56 | echo "[Test failed]" 57 | for file in $missing_list; do 58 | echo "[missing] $file" 59 | done 60 | fi 61 | 62 | exit 0 63 | -------------------------------------------------------------------------------- /code/lab03/es49.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DEBUG=N 4 | 5 | usage() { 6 | echo "usage: $0 [-h] -t extension d1 .. dn" 1>&2 7 | exit 1 8 | } 9 | 10 | while getopts "t:h" o; do 11 | case "$o" in 12 | t) EXT="$OPTARG" 13 | ;; 14 | h) usage 15 | ;; 16 | *) usage 17 | ;; 18 | esac 19 | done 20 | 21 | # Shift parameters away. In this case, useless. 22 | shift $(( OPTIND - 1 )) 23 | 24 | # Check if parameters have been acquired 25 | [ $# -eq 0 ] && usage 26 | [ -z "$EXT" ] && usage 27 | 28 | for dir in "$@"; do 29 | case "$dir" in 30 | /*) ;; 31 | *) usage 32 | ;; 33 | esac 34 | done 35 | 36 | # Main body 37 | echo "processing..." 38 | for dir in "$@"; do 39 | files=$(find "$dir" -type f -readable -name '*'"$EXT" 2>/dev/null) 40 | 41 | if [ -z "$files" ]; then 42 | average=0 43 | else 44 | tsize=0 45 | nfiles=0 46 | for file in $files; do 47 | size=$(wc -c < "$file") 48 | tsize=$(( tsize + size )) 49 | nfiles=$(( nfiles + 1 )) 50 | [ $DEBUG = "Y" ] && echo ["$nfiles"] [size="$size"] "$file" 51 | done 52 | average=$(( tsize / nfiles )) 53 | fi 54 | echo "$dir"\;"$nfiles"\;"$average" 55 | done 56 | 57 | exit 0 58 | -------------------------------------------------------------------------------- /install_guide/README.md: -------------------------------------------------------------------------------- 1 | # EndeavourOS installation guide 2 | 3 | ![](eos_1.webp) 4 | Select: **Start the installer** 5 | 6 | ![](eos_2.webp) 7 | Select: **Online** 8 | 9 | ![](eos_3.webp) 10 | Select: **Cinnamon** 11 | 12 | ![](eos_4.webp) 13 | Leave defaults 14 | 15 | ![](eos_5.webp) 16 | Leave defaults 17 | 18 | ![](eos_6.webp) 19 | Select: **Erase disk -> Swap to file** 20 | 21 | ![](eos_7.webp) 22 | Insert: **Username and password** -------------------------------------------------------------------------------- /install_guide/eos_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_1.webp -------------------------------------------------------------------------------- /install_guide/eos_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_2.webp -------------------------------------------------------------------------------- /install_guide/eos_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_3.webp -------------------------------------------------------------------------------- /install_guide/eos_4.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_4.webp -------------------------------------------------------------------------------- /install_guide/eos_5.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_5.webp -------------------------------------------------------------------------------- /install_guide/eos_6.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_6.webp -------------------------------------------------------------------------------- /install_guide/eos_7.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/install_guide/eos_7.webp -------------------------------------------------------------------------------- /slides/01 - Introduzione a Unix.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: Marzo 2024 3 | author: Dr. Nicola Bicocchi 4 | institute: DIEF/UNIMORE 5 | title: Introduzione a UNIX ed allo scripting Bash 6 | subtitle: Corso di Programmazione di Sistema 7 | section-titles: true 8 | theme: Madrid 9 | colortheme: crane 10 | fontsize: 10pt 11 | aspectratio: 1610 12 | lang: it 13 | --- 14 | 15 | # Introduzione a Unix 16 | 17 | ## Total Market Share 18 | ![Total Market Share](images/total-market-share.avif) 19 | 20 | ## Mobile Market Share 21 | ![Mobile Market Share](images/mobile-market-share.avif) 22 | 23 | ## Unix 24 | Si tratta di un sistema operativo multiutente e multitasking che fornisce una piattaforma stabile, efficiente e flessibile per varie attività di elaborazione. È stato sviluppato nel 1969 presso i Bell Labs da Ken Thompson e Dennis Ritchie. Nel corso degli anni, Unix si è evoluto e sono state create molte varianti, tra cui Linux, Android, MacOS, IoS. Storia completa [qui](https://en.wikipedia.org/wiki/History_of_Unix). 25 | 26 | Il sistema operativo Unix può essere riassunto in base alle seguenti caratteristiche: 27 | * **Multiutente**: più utenti possono interagire contemporaneamente (da terminali diversi) con il sistema, che evita interferenze tra le attività dei vari utenti; 28 | * **Multitasking**: il suo nucleo o kernel può supportare la contemporanea esecuzione di più processi gestiti a divisione di tempo (timesharing); 29 | * **Gestione della memoria virtuale**: il sistema di gestione della memoria virtuale in Unix si basa su paginazione e segmentazione. Queste caratteristiche consentono ad ogni processo di indirizzare un'area di memoria di dimensioni eventualmente superiori a quelle della memoria centrale effettivamente disponibile; 30 | * **Portabile**: grazie all'impiego del linguaggio C nella realizzazione del sistema, esso gode di un'elevata portabilità, ed è oggi disponibile su una vasta gamma di architetture hardware di processori; 31 | * **Aperto**: soprattutto nelle versioni più recenti le caratteristiche di Unix si sono via via uniformate allo standard POSIX. Inoltre, Unix realizza alcuni dei più diffusi servizi e protocolli di comunicazione della rete Internet. 32 | 33 | ## Varianti Unix 34 | ![](images/genealogia-famiglia-unix.avif) 35 | 36 | ## Varianti Linux 37 | 38 | Le distribuzioni linux (unione di kernel, utilità di sistema e pacchetti applicativi) sono centinaia. La maggior parte di quelle utilizzate oggi derivano da una delle famiglie seguenti: 39 | * Debian/Ubuntu (apt) 40 | * RedHat (rpm) 41 | * **Arch (pacman)** 42 | * Gentoo (portage) 43 | 44 | Vedi [tassonomia completa](https://en.wikipedia.org/wiki/List_of_Linux_distributions) delle distribuzioni linux. 45 | 46 | 47 | ## KISS principle 48 | * **Keep It Simple, Stupid** 49 | * In riferimento al codice sorgente di un programma, **significa mantenere uno stile di progettazione semplice e lineare** demandando le ottimizzazioni al compilatore o a successive fasi dello sviluppo 50 | * In ambito UNIX, tanti semplici strumenti in grado di funzionare in modo orchestrato piuttosto che una sola struttura monolitica 51 | * Richiama in parte il principio filosofico del Rasoio di Occam: a parità di fattori la soluzione più semplice è da preferire. 52 | * (https://en.wikipedia.org/Wiki/Unix-philosophy) 53 | 54 | ## Architettetura sistema operativo 55 | 56 | Quattro livelli fondamentali: 57 | 58 | * **Hardware**: la macchina fisica, ovvero la base del sistema, che è costituita dalla memoria (RAM), dal processore (o CPU), dai dispositivi di input/output (I/O) quali storage e reti, e dalle funzioni di elaborazione grafica. La CPU esegue i calcoli e le attività di lettura e scrittura sulla memoria. 59 | * **Kernel**: il nucleo del sistema operativo. Si trova esattamente nel mezzo. È il software che risiede in memoria e che indica alla CPU cosa fare. 60 | * **Sistema di base**: si tratta di una gruppo di programmi e librerie necessari all'utilizzo del sistema. Il solo kernel sarebbe inutilizzabile. 61 | * **Processi utente**: si tratta dei programmi in esecuzione, gestiti dal kernel. Collettivamente, costituiscono lo spazio utente e sono noti anche solo come processi. Il kernel consente anche la comunicazione tra questi processi e server, un'attività nota come comunicazione tra processi o IPC. 62 | 63 | ![Architettura interna](images/architettura-interna.avif) 64 | 65 | ## Kernel 66 | Il kernel è il componente principale di un sistema operativo e l'interfaccia primaria tra l'hardware di un sistema e i suoi processi, ai quali consente di comunicare gestendo le risorse nel modo più efficiente possibile. 67 | 68 | Il kernel (in italiano "nocciolo") si chiama così perché, come un seme nel guscio, è situato all'interno del sistema operativo e controlla tutte le principali funzioni dell'hardware, che sia un telefono, un laptop, un server o qualsiasi altro tipo di computer. 69 | 70 | ## Funzionalità principali del kernel 71 | Il kernel svolge quattro funzioni principali: 72 | 73 | * **Gestione della memoria**: tiene traccia della quantità di memoria utilizzata per lo storage dei dati e della loro posizione 74 | * **Gestione dei processi**: stabilisce quali processi possono utilizzare la CPU e per quanto tempo 75 | * **Gestione dei dispositivi**: funge da elemento di mediazione tra l'hardware e i processi 76 | 77 | Se implementato in maniera corretta, il kernel opera autonomamente all'interno del suo spazio, nel quale alloca la memoria e tiene traccia di ogni elemento archiviato, risultando invisibile all'utente. Ciò che è visibile all'utente, ovvero i browser web, i file, etc è noto come spazio utente. Queste applicazioni interagiscono con il kernel mediante un'interfaccia per le chiamate di sistema (*system calls*). 78 | 79 | Il codice dei programmi viene eseguito sulle CPU in modalità kernel o in modalità utente. Il codice eseguito con la modalità kernel ha accesso illimitato all'hardware, mentre la modalità utente limita l'accesso di CPU e memoria. 80 | 81 | ![Interazione Kernel-Applicazioni](images/interazione-kernel-applicazioni.avif) 82 | 83 | ## Quanto è complesso un kernel 84 | * 20K SLOC (XV6), https://github.com/mit-pdos/xv6-public 85 | * 30M SLOC (Linux Kernel 6), https://www.kernel.org/ 86 | 87 | ![Linux kernel map](images/linux-kernel-map.avif) 88 | 89 | 90 | # Fondamenti per l'utilizzo pratico 91 | 92 | ## Terminale 93 | Con il termine **console** o **terminale** si definisce una coppia tastiera/video collegata alla macchina. Storicamente, per rendere accessibile una macchina da più utenti, era possibile collegare più tastiere e video allo stesso computer. Oggi i terminali sono virtuali (programmi che simulano una coppia tastiera/mouse monitor). 94 | 95 | Caratteristiche: 96 | * Accesso completo alla configurazione del sistema e dei servizi 97 | * Automatizzazione e scripting 98 | * Basso consumo di risorse computazionali 99 | * Esistono applicazioni in cui un terminale grafico non viene installato perchè inutile (non esiste monitor) o per risparmiare risorse (apparati rete/applicazioni IoT) 100 | 101 | Per chiudere un terminale: 102 | * exit 103 | * logout 104 | * ^d 105 | 106 | ![Terminale testuale](images/terminale-testuale.avif) 107 | 108 | ## Ambiente grafico 109 | Caratteristiche: 110 | * Semplicità di utilizzo 111 | * Maggiore consumo di risorse computazionali 112 | 113 | Nella maggiorparte dei sistemi Unix, l'ambiente grafico è slegato dal resto del sistema e può essere sostituito (anche dopo l'installazione del sistema). Ne esistono [decine](https://opensource.com/article/20/5/linux-desktops), i principali sono elencati sotto: 114 | 115 | * GNOME (gtk) 116 | * Cinnamon (gtk) 117 | * Budgie (gtk) 118 | * Xfce (gtk) 119 | * KDE (qt) 120 | * awesome 121 | * hyprland 122 | 123 | [unixporn](https://www.reddit.com/r/unixporn/) è un canale Reddit dedicato alla condivisione di configurazioni di ambienti grafici Unix. Alcuni esempi sotto: 124 | 125 | ![](images/de-gnome-mac.avif) 126 | ![](images/de-cinnamon.avif) 127 | ![](images/de-elementary.avif) 128 | ![](images/de-hyprland.avif) 129 | 130 | ## Shell 131 | Programma che permette di far interagire l'utente con il sistema operativo tramite comandi inseriti in modo interattivo da tastiera oppure letti in modo automatico da file (script). Gli script shell sono la base della maggior parte degli automatismi nei sistemi Unix. 132 | 133 | **La shell non é unica**, un sistema può metterne a disposizione varie: 134 | * Bash (/bin/bash) 135 | * Zsh (/bin/zsh) 136 | * Fish (/bin/fish) 137 | 138 | ## Editor di testo 139 | Un editor di testo è un programma per la composizione di testi. Un semplice editor è generalmente incluso in ogni sistema operativo. 140 | Poiché la scrittura di un testo è pratica comune a molte attività legate alla gestione o all'uso di un sistema operativo, esistono editor di ogni sorta, dai più semplici ai più complessi e raffinati: 141 | * micro/nano/mcedit 142 | * vi/vim/nvim 143 | * VSCode (solo in ambiente grafico) 144 | 145 | ## Manuale 146 | Esiste un manuale (man), consultabile per informazioni su ogni comando Linux. Per ogni comando, il manuale indica: 147 | * formato del comando (input) e risultato atteso (output) 148 | * descrizione delle opzioni 149 | * possibili restrizioni 150 | * file di sistema interessati dal comando 151 | * comandi correlati 152 | * eventuali bug per uscire dal manuale 153 | 154 | Comandi base per il manuale: 155 | * per cercare una stringa: **/** 156 | * per uscire: **q** 157 | 158 | ``` 159 | # apre la pagina di manuale del comando ls 160 | $ man ls 161 | ``` 162 | 163 | Per semplificare le cose, esiste una convenzione per regolare l'invocazione dei comandi: **nome comando** *opzioni* argomenti 164 | 165 | ``` 166 | $ ls -al / 167 | ``` 168 | 169 | * se un'opzione o un argomento possono essere omessi, si indicano tra quadre [opzione] 170 | * quando un argomento può essere ripetuto n volte, si aggiungono dei puntini argomento... 171 | * se due opzioni/argomenti sono mutuamente esclusivi, vengono separati da |. Ad esempio: arg1 | arg2 172 | 173 | ``` 174 | $ ls --help 175 | Usage: ls [OPTION]... [FILE]... 176 | ... 177 | ``` 178 | 179 | Infine, come mostrato sopra, la maggior parte dei comandi mostra un breve sommario delle opzioni disponibili quando invocata con l'opzione *--help* 180 | 181 | ## Gestione pacchetti 182 | 183 | ### pacman (arch) 184 | ``` 185 | # aggiorna la lista dei pacchetti disponibili 186 | $ sudo pacman -Syy 187 | 188 | # cerca pacchetti in base a parole chiave 189 | $ yay nome_pacchetto 190 | 191 | # installa un pacchetto e le sue dipendenze 192 | $ sudo pacman -S nome_pacchetto 193 | 194 | # rimuove un pacchetto 195 | $ sudo pacman -R nome_pacchetto 196 | 197 | # rimuove pacchetti inutili (dipendenze di pacchetti già rimossi in precedenza) 198 | $ sudo pacman -Qtdq | pacman -Rns - 199 | 200 | # rimuove tutti i pacchetti scaricati 201 | $ sudo pacman -Sc 202 | 203 | # aggiorna intera distribuzione 204 | $ sudo pacman -Syyu 205 | ``` 206 | 207 | ### apt (debian) 208 | ``` 209 | # aggiorna la lista dei pacchetti disponibili 210 | $ sudo apt update 211 | 212 | # cerca pacchetti in base a parole chiave 213 | $ sudo apt search nome_pacchetto 214 | 215 | # installa un pacchetto e le sue dipendenze 216 | $ sudo apt install nome_pacchetto 217 | 218 | # rimuove un pacchetto 219 | $ sudo apt remove nome_pacchetto 220 | 221 | # rimuove pacchetti inutili (dipendenze di pacchetti già rimossi in precedenza) 222 | $ sudo apt autoremove 223 | 224 | # rimuove tutti i pacchetti scaricati 225 | $ sudo apt clean 226 | 227 | # aggiorna intera distribuzione 228 | $ sudo apt dist-upgrade 229 | ``` 230 | 231 | # Utenti e gruppi 232 | 233 | ## Multiutenza e multitasking 234 | Un sistema operativo **multitasking** permette di eseguire più programmi (task) contemporaneamente. Ad esempio, se viene chiesto al sistema di eseguire due processi, A e B, la CPU eseguirà per qualche istante (10-20ms) il processo A, poi il processo B, e così via. Il componente del kernel delegato a questa funzione viene chiamato **scheduler**. 235 | 236 | Un sistema operativo **multiutente** può essere utilizzato da più utenti. Ad ogni utente del sistema viene assegnato: 237 | * uno username/userid (/etc/passwd) 238 | * una password (/etc/shadow) 239 | * uno o giù gruppi di appartenza (/etc/group) 240 | * una cartella personale (/home/utente). 241 | 242 | Esiste un utente privilegiato, il cui username è **root**, che viene assegnato all'amministratore del sistema. **root** può modificare la configurazione dell'intero sistema. Quando si inizia una sessione di lavoro si *entra* nel sistema tramite nome utente e relativa password. 243 | 244 | ``` 245 | Password: ****** 246 | Last login: Fri Mar 06 10:27:08 on tty2 247 | 248 | $ _ 249 | ``` 250 | 251 | ## /etc/passwd 252 | **Username**: username dell'utente 253 | **Password**: la x indica che la password cifrata è presente nel file /etc/shadow 254 | **User ID (UID)**: ID utente 255 | **Group ID (GID)**: ID del gruppo (primario) dell'utente 256 | **User ID Info**: Informazioni aggiuntive 257 | **Home directory**: percorso assoluto home directory utente 258 | **Shell**: percorso assoluto shell utente 259 | 260 | ``` 261 | $ cat /etc/passwd 262 | 263 | root:x:0:0::/root:/bin/zsh 264 | bin:x:1:1::/:/usr/bin/nologin 265 | daemon:x:2:2::/:/usr/bin/nologin 266 | ... 267 | usbmux:x:140:140:usbmux user:/:/usr/bin/nologin 268 | nicola:x:1000:1000:Nicola Bicocchi:/home/nicola:/bin/zsh 269 | ``` 270 | 271 | ## /etc/shadow 272 | ``` 273 | $ sudo cat /etc/shadow 274 | 275 | root:$6$uWHwIUSzGaxw/3hw$awb8CUBdLboSNDfPBckGpHrZg5Fi3YmMJCAYtzBZ1d/yn7c69kIPfq48UAspkSRsGl4OnOPHh8es1PIA6Rpa6.:19576:::::: 276 | bin:!*:19576:::::: 277 | daemon:!*:19576:::::: 278 | ... 279 | usbmux:!*:19576:::::: 280 | nicola:$6$fgSYXa7ydO/y9g2z$qyV/JOWdzULc72Co02ZBCFFSiReixWN9M60iZ0H7.lvqC/ZfJ.ZdjM1v0KtJEaynnvL7PhI.Cnsct6xuMPk/C.:19576:0:99999:7::: 281 | ``` 282 | 283 | ## /etc/group 284 | **Group name**: nome del gruppo 285 | **Password**: generalmente non utilizzato. Si possono definire password di gruppo 286 | **Group ID (GID)**: ID del gruppo 287 | **Group List**: lista degli utenti che appartengono al gruppo 288 | 289 | ``` 290 | $ cat /etc/group 291 | 292 | root:x:0:root 293 | sys:x:3:bin,nicola 294 | mem:x:8: 295 | ... 296 | usbmux:x:140: 297 | nicola:x:1000: 298 | ``` 299 | 300 | ## who, whoami, id 301 | **who** mostra gli utenti attivi nel sistema 302 | 303 | ``` 304 | $ who 305 | nicola tty7 2023-08-23 21:35 (:0) 306 | ``` 307 | 308 | **whoami** mostra il nome dell'utente corrente 309 | 310 | ``` 311 | $ whoami 312 | nicola 313 | ``` 314 | 315 | **id** mostra UID, GID, gruppi secondari 316 | 317 | ``` 318 | $ id 319 | uid=1000(nicola) gid=1000(nicola) groups=1000(nicola),3(sys),982(rfkill),998(wheel) 320 | ``` 321 | 322 | ## passwd 323 | Il comando **passwd** imposta e modifica le password per gli utenti. Utilizzare questo comando per modificare la propria password (tutti gli utenti) o quella di un altro utente (solo admin). 324 | 325 | Per modificare la propria password il comando passwd richiede all'utente la vecchia password e quindi la nuova password due volte. Se le due voci della nuova password non corrispondono, richiede nuovamente la nuova password. 326 | 327 | ``` 328 | $ passwd 329 | (current) UNIX password: 330 | Enter new UNIX password: 331 | Retype new UNIX password: 332 | ``` 333 | 334 | ## sudo 335 | **sudo** eleva i diritti di esecuzione (da utente a root) per un solo comando. In questo esempio, l'aggiornamento del sistema richiede i diritti di amministrazione. 336 | ``` 337 | $ sudo pacman -Syyu 338 | $ <-- Prompt utente normale 339 | ``` 340 | 341 | **sudo -i** eleva i diritti di esecuzione (da utente a root) per una nuova shell. 342 | ``` 343 | $ sudo -i 344 | # <-- Prompt utente root 345 | ``` 346 | 347 | Eleva i diritti di esecuzione in modo permanente. 348 | 349 | ## useradd, userdel 350 | In caso non si voglia modificare manualmente i file /etc/passwd e /etc/group e /etc/shadow, è possibile utilizzare i comandi **useradd** e **userdel** per aggiungere e rimuovere utenti dal sistema. 351 | 352 | ``` 353 | # aggiunge un nuovo utente 354 | $ sudo useradd --create-home username 355 | 356 | # rimuove un utente 357 | $ sudo userdel --remove username 358 | ``` 359 | 360 | # Filesystem 361 | 362 | ## Tutto è file 363 | File come risorsa logica costituita da sequenza di bit, a cui viene dato un nome. Astrazione che consente di trattare allo stesso modo entità fisicamente diverse come file, directory, link, dispositivi di storage, tastiera, video etc. Esistono tre tipi di file: 364 | * **ordinari**: archivi di dati, comandi, programmi sorgente 365 | * **directory**: contengono riferimenti a file 366 | * **speciali**: dispositivi hardware, soft links, FIFO, etc 367 | 368 | I file ordinari si dividono in: 369 | * **file di testo**: leggibile da un essere umano. I dati contenuti rappresentano caratteri (ASCII o Unicode) 370 | * **file binarii**: richiede specifica interpretazione di un software per essere letto (mp3, avif, mp4) 371 | 372 | Si definisce **estensione** il gruppo di 2-4 caratteri terminali del nome di un file che alcuni sistemi, ad esempio Windows le utilizza per rappresentare il tipo del file (ad es. img.avif, song.mp3 etc). I sistemi Unix analizzano invece il contenuto del file per determinarne il tipo. 373 | 374 | ``` 375 | $ file /etc/passwd 376 | /etc/passwd: ASCII text 377 | 378 | $ file /bin/ls 379 | /bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV)... 380 | ``` 381 | 382 | ## Struttura file system 383 | Ogni sotto-directory di / raggruppa file con uno scopo preciso. Può variare leggermente fra diversi sistemi, ma in generale: 384 | * **/bin** binari di sistema 385 | * **/dev** file speciali (che rappresentano dispositivi) 386 | * **/etc** file di configurazione 387 | * **/home** home degli utenti 388 | * **/lib** librerie 389 | * **/proc** interfaccia (human-readable) verso il kernel 390 | * **/sbin** binari di sistema 391 | * **/tmp** file temporanei 392 | * **/usr** binari di applicativi 393 | * **/var** log di sistema 394 | 395 | ![standard-filesystem-hierarchy.svg](images/standard-filesystem-hierarchy.svg) 396 | 397 | ## mount e umount 398 | Un file system (contenuto su qualsiasi dispositivo, ad esempio una chiavetta usb) per essere utilizzato deve essere montato su un file system esistente, usando una directory come punto di attacco. La directory di aggancio prende il nome di mount point. 399 | 400 | Un file system può essere accoppiato a un mount point tramite l'operazione di **mount**. Un file system può essere disaccoppiato dal suo mount point tramite l'operazione di **umount** (inversa di **mount**). 401 | 402 | Per motivi di efficienza, le scritture su di un file system sono eseguite in blocco, al momento più favorevole. Estrarre fisicamente un dispositivo senza aver smontato il suo file system può portare corruzione dei dati! 403 | 404 | ![esempio-mount](images/esempio-mount.avif) 405 | 406 | 407 | ## Percorsi assoluti e relativi 408 | Ogni file può essere identificato da: 409 | * 1 **percorso assoluto**: riferito alla radice della gerarchia. Inizia SEMPRE con / 410 | 411 | ``` 412 | $ cat /home/local/README (osservato dalla radice /) 413 | ``` 414 | * n **percorso relativi**: riferiti alla posizione dell'utente nel file system (n=numero di directory nell'intero filesystem). 415 | 416 | ``` 417 | $ cat ../local/README (osservato da /home/users) 418 | ``` 419 | 420 | ![nomi assoluti e relativi](images/nomi-assoluti-e-relativi.avif) 421 | 422 | 423 | Nomi speciali: 424 | * **.** direttorio corrente 425 | * **..** direttorio genitore (su di 1 livello) 426 | * **~** home dell'utente (ad es. /home/nicola) 427 | 428 | ## Protezione dei file 429 | Multiutenza implica necessità di regolare gli accessi alle informazioni. Ogni file è marcato con UID e GID del proprietario. 430 | 431 | Per ogni file, esistono 3 tipi di utilizzatori: 432 | * **proprietario** (o=owner) 433 | * **gruppo del proprietario** (g=group) 434 | * **tutti gli altri utenti** (a=all) 435 | 436 | Per ogni utilizzatore, si distinguono tre diritti di accesso al file: 437 | * **lettura** (r=read) 438 | * **scrittura** (w=write) 439 | * **esecuzione** (x=execute) (per una directory significa list del contenuto) 440 | 441 | ![file e metadati](images/file-e-metadati.avif) 442 | 443 | Per ogni terna, per ogni bit di protezione: 1 = *attivato*, 0 = *disattivato*. Nell'esempio seguente, il proprietario del file ha tutti i diritti attivati, mentre gli utenti del suo gruppo, e tutti gli altri utenti possono solo leggere il file. 444 | 445 | ![bit di protezione](images/bit-di-protezione.avif) 446 | 447 | ## SUID, SGID, Sticky 448 | **SUID (Set User ID)**: si applica ad un file di programma eseguibile. Se attivo, l'utente che esegue assume i diritti del proprietario (per la durata dell'esecuzione). 449 | 450 | **SGID (Set Group ID)**: si applica ad un file di programma eseguibile. Se attivo, l'utente che esegue assume i diritti del gruppo proprietario (per la durata dell'esecuzione). 451 | 452 | **STICKY**: il sistema cerca di mantenere in memoria l'immagine del programma, anche se non è in esecuzione 453 | 454 | L'esempio seguente mostra una **s** al posto della **x** sulla terna di sinistra. Il processo acquisirà i diritti del proprietario del file (root) e NON dell'utente che lo lancia (implicazioni di sicurezza). 455 | ``` 456 | $ ls -al /usr/bin/passwd 457 | -rwsr-xr-x 1 root root 51552 gen 25 2023 /usr/bin/passwd 458 | ``` 459 | 460 | ## chmod, chown 461 | ``` 462 | # sposta la directory corrente all'interno della home directory 463 | $ cd 464 | 465 | # crea un file vuoto 466 | $ touch test.txt 467 | ``` 468 | 469 | ``` 470 | # assegna nuovi diritti 471 | $ chmod 0755 test.txt 472 | ``` 473 | 474 | ``` 475 | # assegna proprietario root e gruppo root 476 | # sudo è necessario in questo caso 477 | $ sudo chown root:root test.txt 478 | ``` 479 | 480 | ## i-node 481 | Ogni file può avere uno o più nomi simbolici ma è associato un solo **i-node** 482 | 483 | ``` 484 | // XV6 OS, file.h 485 | // https://github.com/mit-pdos/xv6-public 486 | 487 | struct inode { 488 | uint dev; // Device number 489 | uint inum; // Inode number 490 | int ref; // Reference count 491 | int flags; // I*BUSY, I*VALID 492 | 493 | short type; 494 | short major; 495 | short minor; 496 | short link; 497 | uint size; 498 | uint addrs[NDIRECT+1]; 499 | }; 500 | ``` 501 | 502 | ![i-node](images/i-node.avif) 503 | 504 | ## Links 505 | I **link hard** sono nomi logici diversi riferiti allo stesso inode (allo stesso file fisico, permessi, data di modifica, owner etc). Il sistema operativo gestisce la molteplicità di nomi logici: il file fisico (inode) è eliminato solo quando il numero di nomi logici è pari a zero. 506 | 507 | I **link simbolici** sono piccoli file che puntano ad altri file. Questi piccoli file hanno un loro i-node autonomo (con relativo nome logico) e puntano ad altri file. Appaiono con i permessi aperti a tutti gli utenti (di fatto sono trasparenti e riflettono i permessi del file puntato). Si possono visualizzare con il comando: 508 | 509 | ``` 510 | $ ls -l 511 | ``` 512 | 513 | I link simbolici sono molto utilizzati nell'amministrazione di sistemi Unix e corrispondono ai *collegamenti* di Windows. Ad esempio Arch Linux li utilizza per trattare /bin e /sbin come sinonimi di /usr/bin e /lib e /lib64 come sinonimi di /usr/lib. 514 | 515 | ``` 516 | $ ls -l / 517 | 518 | drwxr-xr-x 17 root root 4096 ago 14 22:08 . 519 | drwxr-xr-x 17 root root 4096 ago 14 22:08 .. 520 | lrwxrwxrwx 1 root root 7 gen 31 2023 bin -> usr/bin 521 | drwxr-xr-x 3 root root 4096 ago 14 15:51 boot 522 | drwxr-xr-x 21 root root 3900 ago 22 13:10 dev 523 | drwxr-xr-x 107 root root 12288 ago 22 13:10 etc 524 | drwxr-xr-x 3 root root 4096 ago 7 10:44 home 525 | lrwxrwxrwx 1 root root 7 gen 31 2023 lib -> usr/lib 526 | lrwxrwxrwx 1 root root 7 gen 31 2023 lib64 -> usr/lib 527 | ... 528 | lrwxrwxrwx 1 root root 7 gen 31 2023 sbin -> usr/bin 529 | ... 530 | ``` 531 | 532 | Se viene cancellato o spostato il file a cui puntano, continuano ad esistere (stale link). 533 | 534 | ## touch 535 | **touch** crea un un nuovo file (coppia di file file fisico e nome logico) vuoto. 536 | 537 | ``` 538 | touch [OPTION]... FILE... 539 | $ touch /home/nicola/f1 540 | ``` 541 | 542 | ## rm 543 | **rm** rimuove il file specificato. In presenza di più nomi logici dello stesso file, rimuove solo il nome logico. 544 | 545 | ``` 546 | rm [OPTION]... FILE... 547 | $ rm /home/nicola/f1 548 | ``` 549 | 550 | E' possibile anche utilizzarlo in modo ricorsivo per cancellare intere directory. 551 | 552 | ``` 553 | $ rm -rf Downloads 554 | ``` 555 | 556 | ## cp 557 | **cp** esegue la copia di un file. Duplica il file fisico e crea un nuovo nome logico. 558 | 559 | ``` 560 | cp [OPTION]... SOURCE DEST 561 | $ cp /home/nicola/f1 /home/nicola/f2 562 | ``` 563 | 564 | ![](images/links-cp.svg) 565 | 566 | ## mv 567 | **mv** sposta un file. Aggancia il file fisico ad un nuovo nome logico e sgancia il precedente nome logico. 568 | 569 | ``` 570 | mv [OPTION]... SOURCE DEST 571 | $ mv /home/nicola/f1 /home/nicola/f2 572 | ``` 573 | 574 | ![](images/links-mv.svg) 575 | 576 | 577 | ## ln 578 | **ln** crea un nuovo nome logico per un file fisico esistente (link hard). 579 | 580 | ``` 581 | ln [OPTION]... SOURCE DEST 582 | $ ln /home/nicola/f1 /home/nicola/f2 583 | ``` 584 | 585 | ![](images/links-ln.svg) 586 | 587 | **ln** con opzione **-s** crea un nuovo file fisico (con annesso nome logico) che si riferisce ad un file fisico esistente (link simbolico). 588 | 589 | ``` 590 | $ ln -s /home/nicola/f1 /home/nicola/f2 591 | ``` 592 | 593 | ![](images/links-lns.svg) 594 | 595 | ## stat 596 | **stat** visualizza informazioni dettagliate su un file. 597 | 598 | ``` 599 | stat [OPTION]... FILE... 600 | $ stat /etc/passwd 601 | ``` 602 | 603 | Alcuni utenti considerano il comando stat come una versione migliorata del comando ls -l. Quando viene richiamato senza alcuna opzione, visualizza le seguenti informazioni: 604 | 605 | ``` 606 | File: /etc/passwd 607 | Size: 2084 Blocks: 8 IO Block: 4096 regular file 608 | Device: 8,1 Inode: 1705802 Links: 1 609 | Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) 610 | Access: 2023-08-21 23:05:14.616666816 +0200 611 | Modify: 2023-08-16 13:49:12.694168758 +0200 612 | Change: 2023-08-16 13:49:12.697501986 +0200 613 | Birth: 2023-08-16 13:49:12.694168758 +0200 614 | ``` 615 | 616 | ## mkdir 617 | **mkdir** crea una directory vuota. 618 | ``` 619 | mkdir [OPTION]... DIRECTORY... 620 | ``` 621 | 622 | E' possibile anche utilizzarlo in modo ricorsivo per creare alberi di directory. 623 | 624 | ``` 625 | $ mkdir -p a/b/c/d 626 | ``` 627 | 628 | 629 | # Processi 630 | 631 | ## Processi e utenti 632 | **Ogni operazione eseguita su una macchina Unix viene effettuata a nome e per conto di un determinato utente.** 633 | * Non esistono processi (programmi in esecuzione) in modalità anonima 634 | * Ogni processo aquisisce i vincoli dell'utente che lo esegue (se l'utente ha il diritto di eseguirlo). 635 | 636 | ## ps 637 | **ps** mostra la lista dei processi attivi (statica). 638 | 639 | ``` 640 | # mostra i processi avviati dalla shell corrente 641 | $ ps 642 | PID TTY TIME CMD 643 | 3322 pts/0 00:00:01 zsh 644 | 5154 pts/0 00:00:00 ps 645 | ``` 646 | 647 | ``` 648 | # mostra tutti i processi (con utente che li sta eseguendo) 649 | $ ps auxf 650 | ``` 651 | 652 | ## top 653 | **top** mostra la lista dei processi attivi (dinamica). 654 | 655 | ![](images/top.avif) 656 | 657 | **top - linea #1** 658 | * Ora attuale (21:34:21) 659 | * Uptime della macchina (3:51) 660 | * Utenti attualmente connessi (2 users) 661 | * Media del carico di sistema. I 3 valori si riferiscono a: ultimo minuto, ultimi 5 minuti, ultimi 15 minuti. 662 | 663 | **top - linea #2** 664 | * Processi totali in esecuzione (134 total) 665 | * Processi attivi (1 runnning) 666 | * Processi dormienti (133 slepping) 667 | * Processi in stop (0 stopped) 668 | * Processi che aspettano di essere gestiti dal processo padre (0 zombie) 669 | 670 | **top - linea #3** 671 | * Percentuale del carico dei processi utente (0.7% us) 672 | * Percentuale del carico dei processi di sistema (0.3% sy) 673 | * Percentuale del carico dei processi con priorità di aggiornamento *nice* (0.0% ni) 674 | * Percentuale di inattività della cpu (99.0% id) 675 | * Percentuale dei processi in attesa di operazioni I/O (0.0% wa) 676 | 677 | ## htop, btop, gtop 678 | 679 | ![](images/top-htop.avif) 680 | 681 | ![](images/top-btop.avif) 682 | 683 | ![](images/top-gtop.avif) 684 | 685 | ## kill 686 | **kill** invia dei segnali (ogni segnale è identificato da un numero intero < 255) ai processi (la comunicazione è mediata dal kernel). Ad esempio, ^C che interrompe il processo in esecuzione è uno shortcut di tastiera per inviare al processo in esecuzione il segnale **SIGINT (n=2)**. Vedi: 687 | 688 | ``` 689 | $ man kill # manuale del comando kill 690 | $ man 7 signal # lista completa dei segnali 691 | ``` 692 | 693 | La maggior parte dei segnali sono riservati o vengono usati per programmare applicazioni multi-processo. L'utilizzo più concreto e frequente che si fa dei segnale è richiedere al kernel la terminazione forzata di un processo. E' infatti possibile utiizzare il segnale **SIGKILL (n=9)**. Sotto sono riportate tre diverse invocazioni per terminare, a titolo di esempio, la shell in uso. 694 | 695 | ``` 696 | $ ps 697 | PID TTY TIME CMD 698 | 20504 pts/0 00:00:00 zsh 699 | 20542 pts/0 00:00:00 ps 700 | ``` 701 | 702 | ``` 703 | $ kill -9 20504 # oppure 704 | $ kill -s 9 20504 # oppure 705 | $ kill -s SIGKILL 20504 706 | ``` 707 | 708 | ## Foreground e background 709 | 710 | E' possibile eseguire comandi in background istruendo la shell di ritornare immediatamente un prompt con il carattere **&** 711 | 712 | ``` 713 | $ cat/dev/zero > /dev/null & 714 | [1] 20454 715 | ``` 716 | 717 | E' inoltre possibile riportare in foreground (primo piano) un processo in background con il comando builtin **fg** 718 | 719 | ``` 720 | $ fg 721 | [1] + running cat /dev/zero > /dev/null 722 | ``` 723 | --- 724 | 725 | # Altri comandi rilevanti 726 | 727 | ## cat 728 | **cat** visualizza il contenuto di un file. 729 | 730 | ```shell 731 | $ cat /etc/passwd 732 | root:x:0:0::/root:/bin/bash 733 | bin:x:1:1::/:/usr/bin/nologin 734 | daemon:x:2:2::/:/usr/bin/nologin 735 | ... 736 | dhcpcd:x:960:960:dhcpcd privilege separation:/:/usr/bin/nologin 737 | brltty:x:958:958:Braille Device Daemon:/var/lib/brltty:/usr/bin/nologin 738 | saned:x:957:957:SANE daemon user:/:/usr/bin/nologin 739 | ``` 740 | 741 | ## grep 742 | **grep** visualizza le linee di un file che contengono una determinata stringa 743 | 744 | ```shell 745 | $ grep nicola /etc/passwd 746 | nicola:x:1000:1000:Nicola:/home/nicola:/bin/zsh 747 | ``` 748 | 749 | ## head 750 | **head** visualizza le prime *n* linee di un file 751 | 752 | ```shell 753 | $ head -n 3 /etc/passwd 754 | root:x:0:0::/root:/bin/bash 755 | bin:x:1:1::/:/usr/bin/nologin 756 | daemon:x:2:2::/:/usr/bin/nologin 757 | ``` 758 | 759 | ## tail 760 | **tail** visualizza le ultime *n* linee di un file 761 | 762 | ```shell 763 | $ tail -n 3 /etc/passwd 764 | dhcpcd:x:960:960:dhcpcd privilege separation:/:/usr/bin/nologin 765 | brltty:x:958:958:Braille Device Daemon:/var/lib/brltty:/usr/bin/nologin 766 | saned:x:957:957:SANE daemon user:/:/usr/bin/nologin 767 | ``` 768 | 769 | ## cut 770 | **cut** divide le linee di un file utilizzando il delimitatore *d* e visulizza il campo numero *f* 771 | 772 | ```shell 773 | $ cut -d ':' -f 1 /etc/passwd 774 | root 775 | bin 776 | daemon 777 | ... 778 | dhcpcd 779 | brltty 780 | saned 781 | ``` 782 | 783 | ## sort 784 | **sort** ordina in ordine numerico o alfabetico le linee di un file 785 | 786 | ```shell 787 | $ sort /etc/passwd 788 | avahi:x:973:973:Avahi mDNS/DNS-SD daemon:/:/usr/bin/nologin 789 | bin:x:1:1::/:/usr/bin/nologin 790 | brltty:x:958:958:Braille Device Daemon:/var/lib/brltty:/usr/bin/nologin 791 | ... 792 | tss:x:974:974:tss user for tpm2:/:/usr/bin/nologin 793 | usbmux:x:140:140:usbmux user:/:/usr/bin/nologin 794 | uuidd:x:68:68::/:/usr/bin/nologin 795 | ``` 796 | 797 | ## cmp 798 | **cmp** confronta due file. Se sono uguali non ritorna nulla. 799 | 800 | ```shell 801 | $ cmp /etc/passwd /etc/group 802 | /etc/passwd /etc/group differ: byte 10, line 1 803 | 804 | $ cmp /etc/passwd /etc/passwd 805 | 806 | ``` 807 | 808 | ## wc 809 | **wc** conta le linee *(-l)* o i caratteri *(-c)* di cui è composto un file. 810 | 811 | ```shell 812 | $ wc -l /etc/passwd 813 | 42 /etc/passwd 814 | 815 | $ wc -c /etc/passwd 816 | 2374 /etc/passwd 817 | ``` 818 | 819 | ## find 820 | **find** cerca in modo ricorsivo all'interno di una cartella file con determinate caratteristiche. Le opzioni possono essere combinate. 821 | 822 | ```shell 823 | $ find /etc -name "pass*" # cerca dentro /etc percorsi che iniziano con la stringa pass 824 | 825 | $ find /etc -type d # cerca dentro /etc percorsi che rappresentano directory 826 | 827 | $ find /etc -type f # cerca dentro /etc percorsi che rappresentano file 828 | 829 | $ find /etc -size +1M # cerca dentro /etc percorsi che rappresentano file più grandi di 1MB 830 | 831 | ``` 832 | 833 | ## date 834 | **date** mostra la data corrente (è possibile configurare il formato) 835 | 836 | ```shell 837 | $ date 838 | mer 6 set 2023, 12:40:51, CEST 839 | 840 | $ date +%d-%m-%y 841 | 06-09-23 842 | ``` 843 | 844 | ## expr 845 | **expr** è utilizzato per eseguire operazioni matematiche. Il suo utilizzo negli script è sconsigliato (vedi espansione aritmetica). 846 | 847 | * operazioni aritmetiche: +, -, *, /, % 848 | * operazioni di confronto: <, <=, ==, !=, >=, > 849 | * operazioni logiche: &, | 850 | 851 | ```shell 852 | $ expr 2 \* 6 853 | 12 854 | $ A=12 855 | $ A=$(expr $A - 1) 856 | $ echo $A 857 | 11 858 | ``` 859 | 860 | ## more 861 | **more** visualizza il contenuto di un file abilitando lo scorrimento avanti e indietro in caso il terminale non sia sufficiente a mostrare l'intero contenuto. 862 | 863 | ```shell 864 | $ more /etc/passwd 865 | ``` 866 | 867 | ## less 868 | **less** visualizza il contenuto di un file abilitando lo scorrimento avanti e indietro in caso il terminale non sia sufficiente a mostrare l'intero contenuto. 869 | 870 | ```shell 871 | $ less /etc/passwd 872 | ``` -------------------------------------------------------------------------------- /slides/02 - Bash shell.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: Marzo 2024 3 | author: Dr. Nicola Bicocchi 4 | institute: DIEF/UNIMORE 5 | title: Introduzione a UNIX ed allo scripting Bash 6 | subtitle: Corso di Programmazione di Sistema 7 | section-titles: true 8 | theme: Madrid 9 | colortheme: crane 10 | fontsize: 10pt 11 | aspectratio: 1610 12 | lang: it 13 | --- 14 | # Bash Shell 15 | ## Utilizzo interattivo 16 | La shell interpreta i comandi degli utenti. Il compito principale di una shell è di fornire un ambiente per l'utente, che può essere configurato usando i file di configurazione delle risorse di shell. 17 | 18 | ``` 19 | $ ls -al .bash* 20 | 21 | -rw------- 1 nicola nicola 13932 ago 22 00:39 .bash_history 22 | -rw-r--r-- 1 nicola nicola 21 mag 21 13:56 .bash_logout 23 | -rw-r--r-- 1 nicola nicola 57 mag 21 13:56 .bash_profile 24 | -rw-r--r-- 1 nicola nicola 2721 ago 19 20:59 .bashrc 25 | ``` 26 | 27 | I comandi possono anche essere letti da un file chiamato script shell. Gli script sono interpretati, non compilati. La shell legge i comandi dallo script riga per riga. Esistono molte varianti di shell. Tratteremo la shell bash, nonostante le sue limitazioni rispetto a shell più recenti ed adatte all'utilizzo interattivo come **zsh** e **fish**, per la sua grande diffusione. 28 | 29 | ### Freccia su, freccia giù, ctrl-r, tab 30 | **Tasti freccia (su e giù)** consentono di spostarsi all'interno della lista dei comandi precedenti (lo stesso elenco mostrato dal comando history) 31 | 32 | **ctrl-r** consente di inserire una stringa e selezionare tutti i comandi precedenti che la contengono. Ogni pressione successiva della combinazione **ctrl-r** accede agli altri comandi della stessa selezione 33 | 34 | **tab** auto-completa i nomi di file. Una doppia pressione (rapida) mostra l'elenco di tutte le possibilità disponibili 35 | 36 | ### Comandi esterni 37 | La maggior parte dei comandi impartiti ad una shell sono nomi di altri programmi shell (sperabilmente installati nel sistema) come ad esempio cp, mv, cat etc. Il comando **which** ritorna il percorso assoluto di un comando con un determinato nome. 38 | 39 | ```shell 40 | $ ls 41 | $ which ls 42 | /bin/ls 43 | $ which which 44 | /usr/bin/which 45 | ``` 46 | 47 | ### Comandi interni (builtin) 48 | Esistono però particolari comandi, detti **builtin**, che non provengono dall'esecuzione di un file binario ma sono **implementati all'interno della shell**. 49 | 50 | ```shell 51 | $ pwd 52 | $ cd 53 | $ echo 54 | $ read 55 | $ exit 56 | $ true 57 | $ false 58 | $ alias 59 | $ history 60 | $ set 61 | $ unset 62 | ... 63 | ``` 64 | 65 | Nel loro caso, l'esecuzione di **$ which comando** non ritorna un percorso perchè il file binario non esiste! Ad esempio: 66 | 67 | ``` 68 | $ which pwd 69 | pwd: shell built-in command 70 | 71 | ``` 72 | 73 | 74 | Vedi lista completa [qui](https://www.gnu.org/software/bash/manual/html_node/Bash-Builtins.html). 75 | 76 | **pwd** mostra la directory corrente 77 | 78 | ```shell 79 | $ cd 80 | $ pwd 81 | /home/nicola 82 | ``` 83 | 84 | **cd** modifica la directory corrente 85 | 86 | ```shell 87 | $ cd # home directory 88 | $ cd /tmp # directory /tmp 89 | ``` 90 | 91 | **echo** stampa sul terminal quello che gli viene passato come parametro 92 | 93 | ```shell 94 | $ echo "nicola" 95 | nicola 96 | $ echo $PATH 97 | /home/nicola/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:... 98 | ``` 99 | 100 | **read** legge una linea da stdin e la inserisce in una variabile. Utile per interazione con utente. 101 | 102 | ```shell 103 | $ read -p "What is your name? " answer 104 | $ echo "$answer" 105 | ``` 106 | 107 | **exit** termina l'esecuzione di una shell (e di conseguenza anche di uno script) e ritorna al chiamante un valore [0, 255] 108 | 109 | ```shell 110 | $ bash # (avvio sotto-shell) 111 | $ exit 15 # (terminazione sotto-shell con valore 15) 112 | $ echo $? 113 | 15 114 | $ 115 | ``` 116 | 117 | **true** comando fittizio. Non fa nulla, e ritorna vero (0) 118 | 119 | ```shell 120 | $ true; echo $? 121 | 0 122 | ``` 123 | 124 | **false** comando fittizio. Non fa nulla, e ritorna falso (1) 125 | 126 | ```shell 127 | $ false; echo $? 128 | 1 129 | ``` 130 | 131 | **alias** definisce degli alias per i comandi. Nell'esempio sotto viene definito l'alias *ll* per il comando *ls -l*. Utile quando comandi lunghi sono frequentemente utilizzati. **unalias** elimina l'alias. 132 | 133 | ```shell 134 | $ alias ll='ls -l' 135 | $ ll 136 | total 320 137 | drwx------ 29 nicola nicola 4096 ago 17 23:10 . 138 | drwxr-xr-x 3 root root 4096 ago 7 10:44 .. 139 | drwxr-xr-x 2 nicola nicola 4096 ago 12 01:14 Desktop 140 | drwxr-xr-x 3 nicola nicola 4096 ago 17 22:58 Devel 141 | drwxr-xr-x 2 nicola nicola 4096 ago 7 10:53 Documents 142 | drwxr-xr-x 6 nicola nicola 4096 ago 16 22:55 Downloads 143 | drwxr-xr-x 2 nicola nicola 4096 ago 7 10:53 Music 144 | 145 | $ unalias ll 146 | $ ll 147 | zsh: command not found: ll 148 | ``` 149 | 150 | **history** permette di visualizzare la cronologia degli ultimi comandi digitati dall'utente. Grazie alla cronologia è possibile ripetere rapidamente un comando già eseguito senza doverlo digitare di nuovo. 151 | 152 | ```shell 153 | $ history 154 | 1 uname -a 155 | 2 clear 156 | 3 exit 157 | 4 ls 158 | 159 | $ !! # esegue ultimo comando (in questo caso 4) 160 | $ !3 # esegue il comando 3 161 | ``` 162 | 163 | **set** mostra il nome ed il valore di tutte le variabili (locali e d'ambiente) e le funzioni definite all'interno della shell corrente 164 | 165 | ```shell 166 | $ set 167 | '!'=0 168 | '#'=0 169 | '$'=2204 170 | '*'=( ) 171 | -=0569BJNPXZims 172 | 0=zsh 173 | '?'=0 174 | @=( ) 175 | ARGC=0 176 | BROWSER=firefox 177 | BUFFER='' 178 | CDPATH='' 179 | ... 180 | ``` 181 | 182 | **unset** rimuove una variabile (sia locale che d'ambiente) dalla shell corrente 183 | 184 | ```shell 185 | $ A=1 186 | $ echo $A 187 | 1 188 | $ unset A 189 | $ echo $A 190 | 191 | ``` 192 | 193 | ## Ridirezione 194 | E' possibile ridirigere input e/o output di un comando facendo sì che stdin/stdout/stderr siano sostituiti da file **in modo trasparente al comando** 195 | 196 | Ridirezione dell'input 197 | ``` 198 | $ comando < filein 199 | ``` 200 | 201 | Ridirezione dell'output 202 | ```shell 203 | $ comando > fileout # sovrascrive fileout 204 | $ comando >> fileout # aggiunge alla fine di fileout 205 | ``` 206 | ![](images/flussi-dati.avif) 207 | 208 | Ad esempio: 209 | 210 | cat legge da /etc/passwd e stampa il contenuto su stdout 211 | 212 | ```shell 213 | $ cat /etc/passwd 214 | ``` 215 | 216 | cat legge da stdin, ma il flusso proviene da /etc/passwd 217 | 218 | ```shell 219 | $ cat < /etc/passwd 220 | ``` 221 | 222 | sort legge da stdin, ma il flusso proviene da fin 223 | 224 | sort scrive su stdout, ma il flusso è ridiretto su fout 225 | 226 | ```shell 227 | $ sort < fin > fout 228 | ``` 229 | 230 | head legge da fin 231 | 232 | head scrive su stdout, ma il flusso è ridiretto su fout 233 | 234 | ```shell 235 | $ head fin > fout 236 | ``` 237 | 238 | tr legge da stdin, ma il flusso proviene da fin 239 | 240 | tr scrive su stdout, ma il flusso è ridiretto su fout 241 | 242 | ```shell 243 | $ tr [:lower:] [:upper:] < fin > fout 244 | ``` 245 | 246 | who scrive su stdout, ma il flusso è ridiretto su users (append) 247 | 248 | ```shell 249 | $ who >> users 250 | ``` 251 | 252 | ### Implementazione ridirezione 253 | Ridirezione in input 254 | ![implementazione ridirezione input](images/implementazione-ridirezione-input.avif) 255 | 256 | Ridirezione in output 257 | ![implementazione ridirezione output](images/implementazione-ridirezione-output.avif) 258 | 259 | 260 | ### Separazione stdout, stderr 261 | Il comando seguente cerca tutte le directory all'interno di /etc. Alcune non sono accessibili ed il comando produce errori insieme agli elementi trovati. Gli errori vengono stampati su **stderr** mentre gli elementi trovati su **stdout**. 262 | 263 | ```shell 264 | $ find /etc -type d 265 | 266 | ... 267 | /etc/pacman.d 268 | /etc/pacman.d/gnupg 269 | /etc/pacman.d/gnupg/crls.d 270 | find: ‘/etc/pacman.d/gnupg/crls.d’: Permission denied 271 | /etc/pacman.d/gnupg/private-keys-v1.d 272 | find: ‘/etc/pacman.d/gnupg/private-keys-v1.d’: Permission denied 273 | /etc/pacman.d/gnupg/openpgp-revocs.d 274 | find: ‘/etc/pacman.d/gnupg/openpgp-revocs.d’: Permission denied 275 | /etc/openvpn 276 | /etc/openvpn/client 277 | ... 278 | ``` 279 | 280 | E' possibile separare stdout e stderr menzionandoli in modo esplicito in forma numerica (0=stdin, 1=stdout, 2=stderr) 281 | 282 | ```shell 283 | # scarta stdout, mostra solo gli errori 284 | $ find /etc -type d 1>/dev/null 285 | 286 | find: ‘/etc/cups/ssl’: Permission denied 287 | find: ‘/etc/NetworkManager/system-connections’: Permission denied 288 | find: ‘/etc/audit/plugins.d’: Permission denied 289 | find: ‘/etc/libvirt/secrets’: Permission denied 290 | find: ‘/etc/sudoers.d’: Permission denied 291 | find: ‘/etc/credstore’: Permission denied 292 | ... 293 | 294 | # scarta stderr, mostra solo elementi trovati 295 | $ find /etc -type d 2>/dev/null 296 | 297 | /etc 298 | /etc/cron.hourly 299 | /etc/xml 300 | /etc/fonts 301 | /etc/fonts/conf.d 302 | /etc/acpi 303 | ... 304 | 305 | # scarta sia sdtout che stderr, nulla viene mostrato 306 | $ find /etc -type d 1>/dev/null 2>/dev/null 307 | ``` 308 | 309 | E' inoltre possibile ridirigere un flusso all'interno di un altro flusso. Le due invocazioni seguenti sono sinonimi. 310 | 311 | ```shell 312 | $ find /etc -type d 1>/dev/null 2>/dev/null 313 | $ find /etc -type d 1>/dev/null 2>&1 314 | ``` 315 | 316 | In Python è possibile verificare questi comportamenti con lo script seguente che stampa "Hello stdout!\n" su **stdout** ed "Hello stderr!\n" su **stderr** 317 | 318 | ```python 319 | #!/usr/bin/env python3 320 | import sys 321 | sys.stdout.write("Hello stdout!\n") 322 | sys.stderr.write("Hello stderr!\n") 323 | sys.exit(0) 324 | ``` 325 | 326 | ```shell 327 | $ ./test.py 1>/dev/null 328 | Hello stderr! 329 | $ ./test.py 2>/dev/null 330 | Hello stdout! 331 | ``` 332 | 333 | ### File speciali 334 | **/dev/null** è un file speciale che scarta tutto ciò che gli viene scritto sopra. E' il buco nero di ogni sistema Unix. 335 | 336 | **/dev/zero** è un file speciale che produce zeri all'infinito quando viene letto. 337 | 338 | **/dev/urandom** è un file speciale che produce caratteri casuali all'infinito quando viene letto. 339 | 340 | ```shell 341 | # premere ctrl-c per interrompere i comandi sotto 342 | 343 | # zeri dentro fout 344 | $ cat /dev/zero > fout 345 | 346 | # caratteri casuali dentro fout 347 | $ cat /dev/urandom > fout 348 | 349 | # caratteri casuali dentro il nulla 350 | $ cat /dev/urandom > /dev/null 351 | ``` 352 | 353 | Per controllare le dimensioni lette/scritte utilizzare il comando **dd**. 354 | 355 | ```shell 356 | # 1 blocco di 1K da /dev/zero verso fout 357 | dd if=/dev/zero bs=1K count=1 of=fout 358 | 359 | # 1 blocco di 1K da /dev/urandom verso fout 360 | dd if=/dev/urandom bs=1K count=1 of=fout 361 | 362 | # 1000 blocchi di 1M da /dev/zero verso /dev/null 363 | dd if=/dev/urandom bs=1M count=1000 of=fout 364 | ``` 365 | 366 | ## Combinazione di comandi 367 | 368 | ### Concatenazione semplice 369 | **cmd1; cmd2** 370 | 371 | Esegue cmd2 a prescindere dal valore di ritorno di cmd1. 372 | 373 | ```shell 374 | $ true; ls 375 | Desktop Devel Documents Downloads... 376 | 377 | $ false; ls 378 | Desktop Devel Documents Downloads... 379 | 380 | $ true; echo $? 381 | 0 382 | 383 | $ false; echo $? 384 | 1 385 | ``` 386 | 387 | In shell, 0 è interpretato come successo (vero), > 0 come fallimento (falso). 388 | 389 | ### AND logico 390 | **cmd1 && cmd2** 391 | 392 | Esegue cmd2 solo se cmd1 termina con successo (ritorna 0) 393 | 394 | ```shell 395 | $ true && ls 396 | Desktop Devel Documents Downloads... 397 | 398 | $ false && ls 399 | ``` 400 | 401 | ### OR logico 402 | **cmd1 || cmd2** 403 | Esegue cmd2 solo se il primo fallisce (ritorna 1) 404 | 405 | ```shell 406 | $ true || ls 407 | $ false || ls 408 | Desktop Devel Documents Downloads... 409 | ``` 410 | 411 | ![Combinare comandi (&&, ||)](images/combinare-comandi.avif) 412 | 413 | ### Pipes 414 | La seguente scrittura è formalmente corretta ma poco efficiente. L'output del primo comando viene scritto su disco solo per essere letto dal secondo comando. 415 | 416 | ```shell 417 | $ find /etc -type d 2>/dev/null > fout; grep pacman fout 418 | ``` 419 | 420 | Approccio estremamente inefficiente. La memoria secondaria (SSD) è molto meno performante della memoria primaria (RAM): 421 | * SDD: ~ 0.5 GB/s 422 | * DDR5: ~ 50 GB/s 423 | 424 | L'output di un comando può esser diretto a diventare l'input di un altro comando (usando il costrutto pipe '|'). Pipe come costrutto parallelo: l'output del primo comando viene reso disponibile al secondo e consumato appena possibile, in assenza di file temporanei. 425 | 426 | La comunicazione fra i due processi non è diretta ma è mediata dal kernel. 427 | 428 | ```shell 429 | # ordina /etc/passwd e mostra le prime 10 linee 430 | $ sort /etc/passwd | head -n 10 431 | ``` 432 | 433 | ![implementazione pipes](images/implementazione-pipes.avif) 434 | 435 | Esempi: 436 | ```shell 437 | # ordina /etc/passwd e mostra le ultime 5 linee 438 | $ sort /etc/passwd | tail -n 5 439 | 440 | # ordina /etc/passwd e mostra le penultime 5 linee 441 | $ sort /etc/passwd | tail -n 10 | head -n 5 442 | 443 | # legge caratteri casuali da /dev/urandom e li conta 444 | $ cat /dev/urandom | wc -c (osservare con top) 445 | 446 | # legge /etc/passwd, lo ordina e mostra le prime 10 linee 447 | $ cat /etc/passwd | sort | head 448 | 449 | # conta gli utenti collegati al sistema 450 | $ who | wc -l 451 | 452 | # Stampa il contenuto della cartella corrente, seleziona le righe che iniziano per d (directory) ed ordina il risultato 453 | $ ls -l | grep ^d | sort 454 | 455 | # mostra shell usate nel sistema senza ripetizioni 456 | $ cat /etc/passwd | cut -d ":" -f 7 | uniq -u 457 | 458 | # mostra penultimi 5 utenti in ordine alfabetico 459 | $ sort /etc/passwd | tail -n 10 | head -n 5 460 | ``` 461 | 462 | ## Variabili 463 | E' possibile definire variabili (trattate come stringhe) ed assegnare loro un valore con l'operatore **=** 464 | 465 | ```shell 466 | $ VAR=3 467 | ``` 468 | 469 | Si accede ai valori delle variabili con il carattere speciale **\$** 470 | 471 | ```shell 472 | $ echo $VAR 473 | 3 474 | ``` 475 | 476 | Un esempio con 2 variabili: 477 | ```shell 478 | $ A=1; B=nicola 479 | $ echo $A 480 | 1 481 | $ echo $B 482 | nicola 483 | ``` 484 | 485 | La visibilità delle variabili definite all'interno di una shell è limitata alla shell stessa. **Eventuali sotto-shell non ereditano le variabili.** 486 | 487 | ```shell 488 | $ A=1 489 | $ echo $A 490 | 1 491 | $ bash (sotto-shell) 492 | $ echo $A 493 | 494 | $ 495 | ``` 496 | 497 | ### Variabili d'ambiente 498 | Per espandere la vita delle variabili anche alle sotto shell si utilizzano particolari variabili chiamate **variabili d'ambiente.** 499 | 500 | Ogni processo esegue nell'ambiente associato al processo che l'ha messo in esecuzione. Di conseguenza, ogni shell eredita l'ambiente dalla shell che l'ha messa in esecuzione. 501 | 502 | La prima shell ad eseguire dopo il login o dopo l'apertura di un terminale grafico, legge un file (es. .bash_profile/.bashrc) che contiene fra le altre cose variabili di configurazione che vengono così prorogate a tutte le shell successive (figlie). 503 | 504 | E' possibile aggiungere variabili all'ambiente utilizzando il comando **export**. Le variabili esportate si comportano come variabili locali ma sono visibili anche dalle sotto-shell. 505 | 506 | ```shell 507 | $ export A=1 508 | $ echo $A 509 | 1 510 | $ bash (sotto-shell) 511 | $ echo $A 512 | 1 513 | $ 514 | ``` 515 | 516 | ### env 517 | **env** mostra l'elenco delle variabili d'ambiente della shell corrente. 518 | 519 | ```shell 520 | $ env 521 | SHELL=/bin/bash 522 | TERM=xterm-256color 523 | USER=nicola 524 | HOME=/home/nicola 525 | LOGNAME=nicola 526 | PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin 527 | ... 528 | ``` 529 | 530 | **set (builtin)** mostra il nome ed il valore di tutte le variabili (locali e d'ambiente) e le funzioni definite all'interno della shell corrente 531 | 532 | ```shell 533 | $ set 534 | '!'=0 535 | '#'=0 536 | '$'=2204 537 | '*'=( ) 538 | -=0569BJNPXZims 539 | 0=zsh 540 | '?'=0 541 | @=( ) 542 | ARGC=0 543 | BROWSER=firefox 544 | BUFFER='' 545 | CDPATH='' 546 | ... 547 | ``` 548 | 549 | ### Variabili d'ambiente rilevanti 550 | ```shell 551 | PATH=/home/nicola/bin:/usr/local/bin:/usr/bin:/bin:... 552 | HOME=/home/nicola 553 | EDITOR=micro 554 | SHELL=/bin/bash 555 | PWD=/home/nicola 556 | ``` 557 | 558 | ## Espansioni 559 | 560 | ### Espansione metacaratteri 561 | La shell riconosce caratteri speciali (wild cards) 562 | 563 | \* = una qualunque stringa di zero o più caratteri in un nome di file 564 | ``` 565 | $ ls D* 566 | Desktop: 567 | 568 | Devel: 569 | operatingsystemsbsc 570 | 571 | Documents: 572 | 573 | Downloads: 574 | ``` 575 | 576 | ? = un qualunque carattere in un nome di file 577 | ``` 578 | $ ls ?ideos 579 | 580 | Videos: 581 | ``` 582 | 583 | [abc] = un qualunque carattere, in un nome di file compreso tra quelli nell'insieme. Anche range di valori: [a-g] 584 | ``` 585 | $ ls -l /etc/[mnop]asswd 586 | -rw-r--r-- 1 root root 2084 ago 16 13:49 /etc/passwd 587 | ``` 588 | 589 | \\ segnala di non intrerpretare il carattere successivo come speciale 590 | ``` 591 | $ echo * 592 | Desktop Devel Documents Downloads Music Pictures Public Templates Videos 593 | 594 | $ echo \* 595 | * 596 | ``` 597 | 598 | Ad esempio: 599 | ```shell 600 | # Elenca i file i cui nomi iniziano con il carattere . 601 | $ ls .* 602 | 603 | # Elenca i file i cui nomi hanno come iniziale un carattere compreso tra 'a' e 'c' oppure tra '1' e '3', e il cui penultimo carattere sia 'c' o 'f' 604 | $ ls [a-c,1-3]*[c,f]? 605 | 606 | # Elenca i file in cui nomi contengono, in qualunque posizione, il carattere * 607 | $ ls *\** 608 | ``` 609 | 610 | ### Espansione esecuzione in-line 611 | E' possibile eseguire un comando ed utilizzarne l'output all'interno di un altro comando attraverso la sintassi **$( cmd )** 612 | 613 | ```shell 614 | $ dirname=$(pwd) 615 | $ echo $dirname 616 | /home/nicola 617 | 618 | $ list=$(find /etc -type f 2>/dev/null) 619 | $ echo $list 620 | /etc/ld.so.conf 621 | /etc/lsb-release 622 | /etc/ostree-mkinitcpio.conf 623 | /etc/adjtime 624 | /etc/dhcpcd.conf 625 | ... 626 | ``` 627 | 628 | ### Espansione aritmetica 629 | E' possibile eseguire operazioni aritmetiche attraverso la sintassi **$(( cmd ))** 630 | 631 | ```shell 632 | $ echo $(( 2 + 4 )) 633 | 6 634 | $ echo $(( 2 + 3 * 2 )) 635 | 11 636 | $ echo $(( (2 + 3) * 3 )) 637 | 15 638 | ``` 639 | 640 | All'interno di questo tipo di espansione, le variabili sono identificate dal loro nome (non dal valore! e.g. $a, $b) 641 | 642 | ```shell 643 | $ a=2; b=3 644 | $ echo $(( a + b )) 645 | 5 646 | ``` 647 | 648 | ### Inibizione 649 | Ogni volta che utilizziamo un comando che contiene variabili, metacaratteri o espansioni, la shell li sostituisce nell'ordine seguente: 650 | 651 | 1. **\$( ) e \$(( ))** sono sostituiti con il risultato prodotto 652 | 2. Variabili sono espanse nei valori corrispondenti 653 | 3. Metacaratteri sono espansi nei nomi di file corrispondendi 654 | 655 | In alcuni casi è necessario inibire queste espansioni: 656 | * ' ' (singoli apici): evita ogni espansione nella stringa contenuta 657 | * " " (doppi apici): evita espansione dei soli metacaratteri nella stringa contenuta 658 | 659 | Ad esempio: 660 | ```shell 661 | # rimuove i file che cominciano con $var 662 | $ rm '$var'* 663 | 664 | # rimuove i file che cominciano con il contenuto della variabile var 665 | $ rm "$var"* 666 | 667 | # stampa il nome della directory corrente 668 | $ echo "$(pwd)" 669 | 670 | # stampa $(pwd) 671 | $ echo '$(pwd)' 672 | 673 | # stampa eventuali nomi di file che iniziano per D o E, seguiti da own e qualsiasi altro carattere 674 | $ echo [D-E]own* 675 | Downloads 676 | 677 | # stampa [D-E]own* 678 | $ echo "[D-E]own*" 679 | [D-E]own* 680 | ``` 681 | 682 | 683 | -------------------------------------------------------------------------------- /slides/03 - Bash scripting.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: Marzo 2024 3 | author: Dr. Nicola Bicocchi 4 | institute: DIEF/UNIMORE 5 | title: Introduzione a UNIX ed allo scripting Bash 6 | subtitle: Corso di Programmazione di Sistema 7 | section-titles: true 8 | theme: Madrid 9 | colortheme: crane 10 | fontsize: 10pt 11 | aspectratio: 1610 12 | lang: it 13 | --- 14 | # Bash Scripting 15 | 16 | ## Scrittura ed esecuzione 17 | Bash è un linguaggio interpretato (non compilato)! La prima linea di ogni script specifica l'interprete da utilizzare per i comandi successivi (**#!/bin/bash**). Tutte le altre linee che iniziano con **#** sono commenti nel codice.Per ottenere uno script funzionante è necessario seguire i seguenti passaggi: 18 | 19 | ### Scrittura di uno script 20 | ```shell 21 | $ vim script.sh 22 | 23 | #!/bin/bash 24 | echo Total number of inputs: $# 25 | echo First input: "$1" 26 | echo Second input: "$2" 27 | exit 0 28 | ``` 29 | 30 | ### Rendere lo script eseguibile 31 | ```shell 32 | $ chmod a+x script.sh # oppure 33 | $ chmod 755 script.sh 34 | ``` 35 | 36 | ### Invocazione di uno script 37 | ```shell 38 | $ ./script.sh AAPL GOOGL MSFT 39 | Total number of inputs: 3 40 | First input: AAPL 41 | Second input: GOOGL 42 | ``` 43 | 44 | Perchè abbiamo invocato lo script con **./script.sh** piuttosto che **script.sh**? Se specifichiamo un percorso che rappresenta **un nome semplice di file**, Bash cerca un programma eseguibile nell'elenco di directory rappresentato dalla variabile **PATH**. 45 | 46 | Se PATH non contiene la directory **.** (che indica la directory corrente), i programmi non vengono trovati anche se si trovano nella directory corrente. Per introdurre **.** all'interno di **PATH**: 47 | 48 | ```shell 49 | # Modifica alla variabile PATH (sconsigliato) 50 | $ export PATH=$PATH:. 51 | $ script.sh 52 | ``` 53 | 54 | ### Variabili speciali 55 | All'interno di uno script Bash è possibile accedere ad un gruppo di [variabili speciali](https://tiswww.case.edu/php/chet/bash/bashref.html#Special-Parameters) che rendono possibile lo sviluppo 56 | 57 | * **$0** Il nome dello script in esecuzione 58 | * **$1, $2,...., $n** n-esimo parametro passato da linea di comando 59 | * **$*** tutti i parametri passati a linea di comando 60 | * **$@** tutti i parametri passati a linea di comando 61 | * **$#** numero di parametri da linea di comando 62 | * **$$** PID della shell che esegue lo script 63 | * **$?** valore di ritorno dell'ultimo comando eseguito 64 | 65 | **shift** elimina il primo parametro dalla lista $1...$n, tutti gli altri scorrono indietro di una posizione. Può essere invocato senza parametri oppure specificando il numero di shift da eseguire. 66 | 67 | ```shell 68 | $ vim test.sh 69 | 70 | #!/bin/bash 71 | echo [$#] $* # mostra [numero dei parametri] tutti i parametri 72 | shift # elimina il primo parametro 73 | echo [$#] $* # mostra [numero dei parametri] tutti i parametri 74 | shift 3 # elimina i primi tre parametri 75 | echo [$#] $* # mostra [numero dei parametri] tutti i parametri 76 | 77 | ``` 78 | 79 | ```shell 80 | ./test.sh -a -b -c -d -e -f -g -h 81 | [8] -a -b -c -d -e -f -g -h 82 | [7] -b -c -d -e -f -g -h 83 | [4] -e -f -g -h 84 | ``` 85 | 86 | ### Commenti 87 | E' possibile inserire commenti nel codice utilizzando il carattere #. 88 | 89 | ```shell 90 | #!/bin/bash 91 | 92 | # Questo è un commento 93 | 94 | exit 0 95 | ``` 96 | 97 | ### ShellCheck (static analysis) 98 | [ShellCheck](https://github.com/koalaman/shellcheck) è uno strumento GPLv3 che fornisce avvisi e suggerimenti per gli script di shell bash: 99 | 100 | Gli obiettivi di ShellCheck sono: 101 | 102 | * Evidenziare e chiarire i tipici problemi di sintassi dei principianti che fanno sì che una shell fornisca messaggi di errore criptici. 103 | * Evidenziare e chiarire i tipici problemi semantici di livello intermedio che causano un comportamento strano e controintuitivo. 104 | * Evidenziare sottili difetti, casi limite, e insidie ​​che potrebbero causare il fallimento dello script in circostanze future. 105 | 106 | 107 | ## Costrutti condizionali 108 | 109 | ### test 110 | **test** è un comando per eseguire verifiche di varia natura sulle stringhe (interpretandole in base ai casi come stringhe, numeri o file). Un controllo avvenuto con successo ritorna 0, altrimenti 1. 111 | 112 | ```shell 113 | $ test 5 -gt 3; echo $? # 0 114 | $ test 3 -gt 5; echo $? # 1 115 | $ test "nicola" == "nicola"; echo $? # 0 116 | $ test "nicola" == "mario"; echo $? # 1 117 | $ test -f /etc/passwd; echo $? # 0 118 | $ test -d /etc/passwd; echo $? # 1 119 | ``` 120 | 121 | ### test (strings) 122 | ``` 123 | -n STRING the length of STRING is nonzero 124 | -z STRING the length of STRING is zero 125 | STRING1 = STRING2 the strings are equal 126 | STRING1 != STRING2 the strings are not equal 127 | ``` 128 | 129 | ### test (numbers) 130 | ``` 131 | INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2 132 | INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2 133 | INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2 134 | INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2 135 | INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2 136 | INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2 137 | ``` 138 | 139 | ### test (files) 140 | ``` 141 | -d FILE FILE exists and is a directory 142 | -e FILE FILE exists 143 | -f FILE FILE exists and is a regular file 144 | -h FILE FILE exists and is a symbolic link (same as -L) 145 | -O FILE FILE exists and is owned by the effective user ID 146 | -r FILE FILE exists and the user has read access 147 | -s FILE FILE exists and has a size greater than zero 148 | -w FILE FILE exists and the user has write access 149 | -x FILE FILE exists and the user has execute (or search) access 150 | ``` 151 | 152 | ### test (logic) 153 | ``` 154 | ! EXPRESSION EXPRESSION is false 155 | EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true 156 | EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true 157 | ``` 158 | 159 | Ad esempio: 160 | 161 | ```shell 162 | $ test -f /etc/passwd -a -r /etc/passwd; echo $? 163 | 0 164 | 165 | $ test ! -d /etc/passwd -o ! -w /etc/passwd; echo $? 166 | 0 167 | ``` 168 | 169 | Nonostante sia ancora supportata questa sintassi è sconsiglita e da sostiture con **&&** e **||** 170 | 171 | 172 | ```shell 173 | $ test -f /etc/passwd && test -r /etc/passwd; echo $? 174 | 0 175 | 176 | $ test ! -d /etc/passwd || test ! -w /etc/passwd; echo $? 177 | 0 178 | ``` 179 | 180 | ### [ ] 181 | Il comando test ha un alter ego che si comporta nello stesso modo ma ha nome diverso ([). Il comando è stato introdotto principalmente per aumentare la leggibilità degli script in particolare per l'utilizzo congiunto con la direttiva **if**. 182 | 183 | Gli spazi che vedete nell'esempio sotto, dopo [ e prima di ] sono obbligatori! La loro assenza produce errori! 184 | 185 | ```shell 186 | $ which test 187 | /usr/bin/test 188 | $ which [ 189 | /usr/bin/[ 190 | ``` 191 | 192 | ```shell 193 | $ [ -f /etc/passwd ] && [ -r /etc/passwd ]; echo $? 194 | 0 195 | 196 | $ [ ! -d /etc/passwd ] || [ ! -w /etc/passwd ]; echo $? 197 | 0 198 | ``` 199 | 200 | ### if 201 | **if** consente di verificare il valore di ritorno di un comando e eseguire istruzioni differenziate in caso la condizione risulti vera o falsa. 202 | 203 | ```shell 204 | if test condizione; then 205 | comando 206 | else 207 | comando 208 | fi 209 | ``` 210 | 211 | Più sinteticamente, utilizzando [ ]: 212 | 213 | ```shell 214 | if [ condizione ]; then 215 | comando 216 | else 217 | comando 218 | fi 219 | ``` 220 | 221 | Utilizzando invece la sua forma più generale: 222 | 223 | ```shell 224 | if [ condizione ]; then 225 | comando 226 | elif [ condizione ]; then 227 | comando 228 | else 229 | comando 230 | fi 231 | ``` 232 | 233 | Ad esempio: 234 | 235 | ```shell 236 | if [ -f "$1" ] && [ -r "$1" ]; then 237 | echo "$1" è un file leggibile! 238 | fi 239 | ``` 240 | 241 | ```shell 242 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 243 | echo "$1" non è una directory eseguibile! 244 | fi 245 | ``` 246 | 247 | ```shell 248 | if [ $# -ne 3 ]; then 249 | echo "params != 3" 250 | else 251 | echo "params == 3" 252 | fi 253 | ``` 254 | 255 | ```shell 256 | if [ $# -lt 2 ]; then 257 | echo "params < 2" 258 | elif [ $# -lt 4 ]; then 259 | echo "2 <= params < 4" 260 | else 261 | echo "params >= 4" 262 | fi 263 | ``` 264 | 265 | In caso una condizione determini l'esecuzione di poche istruzioni e non sia necessario differenziare il caso di successo da quello di fallimento (ma eseguirne uno solo) è possibile utilizzare una forma sintetica (&&,||) che non prevede l'utilizzo di **if** 266 | 267 | ```shell 268 | $ [ 1 –eq 0 ] && echo "pass" # 269 | $ [ 1 –eq 1 ] && echo "pass" # pass 270 | $ [ 1 –eq 0 ] || echo "fail" # fail 271 | $ [ 1 –eq 1 ] || echo "fail" # 272 | $ [ 1 –eq 1 ] && (echo "pass"; pwd) # pass /home/nicola 273 | ``` 274 | 275 | **if** può essere utilizzato per valutare l'esito di comandi arbitrari, non solo di **test**. Nell'esempio sotto, **grep** ritorna 0 in caso la stringa *nicola* sia trovata all'interno di */etc/passwd* 276 | 277 | ```shell 278 | if grep nicola /etc/passwd; then 279 | echo "nicola è un utente del sistema" 280 | else 281 | echo "nicola NON è un utente del sistema" 282 | fi 283 | ``` 284 | 285 | ### Pattern matching 286 | Il patter matching consiste nel confrontare un valore con un determinato pattern 287 | frequentemente specificato utilizzando wildcards. 288 | 289 | ``` 290 | Stringa Pattern Match? 291 | ------------------------------ 292 | ABCDEF A* Si 293 | 294 | ABCDEF AB??EF Si 295 | 296 | ABCDEF ABCNN* No 297 | ``` 298 | 299 | Il costrutto test / [ ] non supporta pattern matching! 300 | 301 | ### [[ ]] 302 | Il costrutto [[ ]] che si comporta come [ ] aggiungendo (fra altre) la funzione di [pattern matching](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html). Per approfondire, leggi [qui](http://mywiki.wooledge.org/BashFAQ/031). 303 | 304 | ```shell 305 | # [ ] fallisce 306 | if [ "$1" == n?co* ]; then 307 | echo "success" 308 | fi 309 | 310 | if [ "$1" != [0-9]* ]; then 311 | echo "success" 312 | fi 313 | ``` 314 | 315 | ```shell 316 | # [[ ]] supporta pattern matching 317 | if [[ "$1" == n?co* ]]; then 318 | echo "success" 319 | fi 320 | 321 | if [[ "$1" != [0-9]* ]]; then 322 | echo "success" 323 | fi 324 | ``` 325 | 326 | ### case 327 | Il costrutto switch-case che abbina il [pattern matching](https://www.gnu.org/software/bash/manual/html_node/Pattern-Matching.html) alla possibilità di eseguire più confronti in modo sintetico (evitando else if) 328 | 329 | ```shell 330 | case espressione in 331 | PATTERN_1) 332 | comando/i 333 | ;; 334 | 335 | PATTERN_2) 336 | comando/i 337 | ;; 338 | 339 | PATTERN_N) 340 | comando/i 341 | ;; 342 | *) 343 | comando/i 344 | ;; 345 | esac 346 | ``` 347 | 348 | In questo esempio, **case** viene utilizzato per determinare il tipo di percorso rappresentato dalla variabile **$1** 349 | 350 | ```shell 351 | case "$1" in 352 | /*) 353 | echo "Percorso assoluto" 354 | ;; 355 | */*) 356 | echo "Percorso relativo" 357 | ;; 358 | *) 359 | echo "Nome semplice di file" 360 | ;; 361 | esac 362 | ``` 363 | 364 | In questo esempio, **case** viene utilizzato per determinare se la variabile **$1** contiene un valore numerico oppure no. 365 | 366 | ```shell 367 | case "$1" in 368 | ''|*[!0-9]*) 369 | echo "Il parametro non è numerico" 370 | ;; 371 | *) 372 | echo "Il parametro è numerico" 373 | ;; 374 | esac 375 | ``` 376 | 377 | --- 378 | 379 | ## Costrutti iterativi 380 | 381 | ### for 382 | ```shell 383 | for argomento in lista; do 384 | comando/i 385 | ... 386 | done 387 | ``` 388 | 389 | ```shell 390 | # Esempio: tabellina del 5 391 | for i in 1 2 3 4 5; do 392 | echo "5 * $i = $(( 5 * i ))" 393 | done 394 | ``` 395 | 396 | ```shell 397 | # Esempio: mostra i nomi file in home directory 398 | for fname in "$HOME"/*; do 399 | echo "$fname" 400 | done 401 | ``` 402 | 403 | Per iterare su indici, è possibile generare una lista di indici validi da utilizzare con **for** utilizzando il comando **seq**. 404 | 405 | ``` 406 | $ seq 1 5 407 | 1 408 | 2 409 | 3 410 | 4 411 | 5 412 | ``` 413 | 414 | ```shell 415 | # Esempio: tabellina del 5 416 | for i in $(seq 1 5); do 417 | echo "5 * $i = $(( 5 * i ))" 418 | done 419 | 420 | 5 * 1 = 5 421 | 5 * 2 = 10 422 | 5 * 3 = 15 423 | 5 * 4 = 20 424 | 5 * 5 = 25 425 | ``` 426 | 427 | ### while 428 | 429 | ```shell 430 | while [ condizione ]; do 431 | comando/i 432 | done 433 | ``` 434 | 435 | ```shell 436 | # Esempio: contatore da 10 a 1 437 | i=10 438 | while [ "$i" -gt 0 ]; do 439 | echo $i 440 | i=$(( i - 1 )) 441 | done 442 | ``` 443 | 444 | ### Espansione nomi di file 445 | I moderni filesystems supportano nomi di file contenenti spazi. Di conseguenza, per evitare problemi, l'espansione di variabili fuori dal controllo dello sviluppatore (ad es. nomi di file) va effettuata fra doppie virgolette " ". 446 | 447 | ```shell 448 | $ vim script.sh 449 | 450 | #!/bin/bash 451 | for fname in "$HOME"/*; do 452 | if [ -f "$fname" ]; then 453 | echo F "$fname" 454 | elif [ -d "$fname" ]; then 455 | echo D "$fname" 456 | fi 457 | done 458 | ``` 459 | 460 | Se non usassimo le doppie virgolette, il seguente caso produrrebbe l'errore mostrato. 461 | 462 | ```shell 463 | $ touch "$HOME"/"Mario Rossi" 464 | $ ./script.sh 465 | ./script.sh: line 4: [: /home/nicola/Mario: binary operator expected 466 | ``` 467 | 468 | ## Argomenti avanzati 469 | 470 | ### Funzioni 471 | 472 | Bash supporta la definizione di funzioni: 473 | * Accedono a parametri di invocazione con sintassi \$1 . . . \$n (come gli script) 474 | * Ritornano al chiamante con istruzione return (script usano exit) 475 | * Valori di ritorno possono essere letti dal chiamante con sintassi $? (come gli script) 476 | * Definite con sintassi: 477 | 478 | ``` 479 | nomefunzione() { 480 | . 481 | . 482 | . 483 | } 484 | ``` 485 | 486 | L'esempio seguente definisce una funzione che ritorna 0 in caso il primo parametro sia una directory eseguibile, 1 viceversa. 487 | 488 | ```shell 489 | #!/bin/bash 490 | 491 | process() { 492 | [ -d "$1" ] && [ -x "$1" ] && return 0 493 | return 1 494 | } 495 | 496 | for f in "$@"; do 497 | echo -n "$1" 498 | if process "$f"; then 499 | echo " [pass]" 500 | else 501 | echo " [fail]" 502 | fi 503 | done 504 | 505 | exit 0 506 | ``` 507 | 508 | 509 | ### Script multi-file 510 | ```shell 511 | $ vim lib.sh 512 | 513 | #!/bin/bash 514 | process() { 515 | [ -d "$1" ] && [ -x "$1" ] && return 0 516 | return 1 517 | } 518 | ``` 519 | 520 | ``` shell 521 | $ vim script.sh 522 | 523 | #!/bin/bash 524 | 525 | source lib.sh 526 | # oppure 527 | # . lib.sh 528 | 529 | for f in "$@"; do 530 | echo -n "$1" 531 | if process "$f"; then 532 | echo " [pass]" 533 | else 534 | echo " [fail]" 535 | fi 536 | done 537 | 538 | exit 0 539 | ``` 540 | 541 | ### Arrays 542 | ```shell 543 | # array definito vuoto 544 | $ arr=() 545 | 546 | # array definito con valori all’intero 547 | $ arr=(1 2 3) 548 | 549 | # aggiunge valori ad array esistente 550 | $ arr+=(4 5) 551 | 552 | # sovrascrive valore di indice 0 553 | $ arr[0]=3 554 | 555 | # mostra i valori nell’array 556 | $ echo ${arr[@]} 557 | 558 | # mostra gli indici validi dell’array 559 | $ echo ${!arr[@]} 560 | 561 | # mostra il numero di valori nell’array 562 | $ echo ${#arr[@]} 563 | 564 | # mostra n elementi partendo da indice s 565 | $ echo ${arr[@]:s:n} 566 | ``` 567 | 568 | Ad esempio: 569 | 570 | ```shell 571 | #!/bin/bash 572 | 573 | files=(/var/log/kern.log /var/log/auth.log /var/log/syslog) 574 | keyw=(nicola marzia) 575 | 576 | echo "* searching ${#files[@]} files with ${#keyw[@]} keywords" 577 | echo "* press enter to continue..." 578 | read 579 | 580 | for f in ${files[@]}; do 581 | for k in ${keyw[@]}; do 582 | l=$(cat ${f} | grep ${k} | wc -l) 583 | echo "* ${f}: found ${l} occurrences of ${k}!" 584 | done 585 | done 586 | ``` 587 | 588 | ### getopts 589 | Funzione standard (**builtin**) per gestire parametri a linea di comando. Esiste in Java, C, Python, etc. 590 | * getopts va sempre utilizzata abbinata ad un while e un case 591 | * La stringa "m:dh" rappresenta i parametri da controllare. Le lettere singole (e.g., d e h) rappresentano parametri senza argomenti. Le lettere seguite da **:** (e.g., m) rappresentano parametri con argomenti 592 | * getopts scansiona la linea di comando e ad ogni ciclo aggiorna la variabile **o** affinchè sia analizzata dal blocco case 593 | * Il blocco case, tipicamente, assegna a delle variabili il valore degli argomenti (**OPTARG**) 594 | 595 | ```shell 596 | while getopts "m:dh" o; do 597 | case "$o" in 598 | m) 599 | MESSAGE="$OPTARG" 600 | ;; 601 | d) 602 | DEBUG=TRUE 603 | ;; 604 | h) 605 | usage 606 | ;; 607 | *) 608 | usage 609 | ;; 610 | esac 611 | done 612 | shift $(expr $OPTIND - 1) 613 | ``` 614 | 615 | Ad esempio: 616 | 617 | ```shell 618 | #!/bin/bash 619 | 620 | # Default values 621 | MESSAGE="Hello World!" 622 | DEBUG=FALSE 623 | 624 | # Usage function 625 | usage() { 626 | echo "Usage: $0 [-h] [-m ] [-d] filename" 1>&2 627 | exit 1 628 | } 629 | 630 | # In case of optional [] parameters default values are overriden 631 | while getopts "m:dh" o; do 632 | case "$0" in 633 | m) 634 | MESSAGE="$OPTARG" 635 | ;; 636 | d) 637 | DEBUG=TRUE 638 | ;; 639 | h) 640 | usage 641 | ;; 642 | *) 643 | usage 644 | ;; 645 | esac 646 | done 647 | # Shift parameters away. $1 becomes filename 648 | shift $(expr $OPTIND - 1) 649 | 650 | # Additional checks 651 | # Check if filename exists 652 | [ -e "$1" ] || usage 653 | 654 | echo m = "$MESSAGE" 655 | echo d = "$DEBUG" 656 | echo filename = "$1" 657 | exit 0 658 | ``` 659 | 660 | ### Buone pratiche 661 | * Trattandosi di un linguaggio antico, l'indentazione è ancora facoltativa (in Python, recente, è obbligatoria!). Indentazione è comunque di fondamentale importanza! 662 | * Variabili globali sono MAIUSCOLE (ad es. USAGE="$0 usage: ...") 663 | * Il controllo dei parametri avviene in **via negativa**. Si controllano le condizioni di fallimento e, se verificate, si termina lo script ritornando un codice errore (exit 1). Questa pratica evita indentazione eccessiva 664 | * I valori di uscita (exit) utilizzano valori diversi per distinguere successo (exit 0) da fallimento (exit 1).Per differenziare fra diversi tipi di fallimento si possono utilizzare numeri positivi > 1 (ad es. exit 2) 665 | * I filesystem moderni supportano la presenza di spazi. Per questo motivo, tutte le variabili fuori dal controllo del programmatore (ad es. nomi di file) vanno espanse fra doppie virgolette (ad es. echo "$filename") 666 | * Aderire ad una struttura nota: 667 | * Definizione interprete 668 | * Definizione variabili globali 669 | * Definizione funzioni 670 | * Controllo parametri 671 | * Corpo principale 672 | * Terminazione 673 | 674 | ```shell 675 | #!/bin/bash 676 | 677 | # Definizione variabili globali 678 | USAGE="usage: $0 dirname" 679 | 680 | # Definizione funzioni 681 | usage() { 682 | echo "$USAGE" 683 | exit 1 684 | } 685 | 686 | # Controllo parametri 687 | if [ $# -ne 1 ]; then 688 | usage 689 | fi 690 | 691 | if [ ! -d "$1" ] || [ ! -x "$1" ]; then 692 | usage 693 | fi 694 | 695 | # Corpo principale 696 | F=0; D=0 697 | for fname in "$1"/*; do 698 | if [ -f "$fname" ]; then 699 | F=$(( F + 1 )) 700 | fi 701 | 702 | if [ -d "$fname" ]; then 703 | D=$(( D + 1 )) 704 | fi 705 | done 706 | 707 | echo "#files=$F, #directories=$D" 708 | 709 | # Terminazione 710 | exit 0 711 | ``` 712 | 713 | ### Bash vs Python per la scrittura di script 714 | 715 | **Bash** 716 | 717 | + Notissima, installata ovunque 718 | + Integrazione profonda con Unix (piping, ridirezione) 719 | - Manca supporto per OOP, strutture dati, multi-threading 720 | - Tool di debug scarsi 721 | 722 | 723 | **Python** 724 | 725 | + Supporto per OOP, strutture dati, multi-threading 726 | + Tool di debug 727 | - Gestione delle dipendenze 728 | - Codice più prolisso per cose semplici 729 | 730 | L'esempio che segue mostra lo stesso programma scritto nei due linguaggi. Le somiglianze sono piuttosto evidenti. 731 | 732 | ```shell 733 | #!/bin/bash 734 | 735 | if [ $# -lt 1 ]; then 736 | echo "usage: $0 f1 .. fn" 737 | exit 1 738 | fi 739 | 740 | l=0 741 | for fname in $*; do 742 | l=$(wc -l "$fname" | cut -d ' ' -f 1) 743 | echo "$fname": "$l" 744 | done 745 | 746 | exit 0 747 | ``` 748 | 749 | ```python 750 | #!/usr/bin/env python 751 | 752 | import sys 753 | import subprocess 754 | 755 | if len(sys.argv) < 2: 756 | sys.stderr.write("usage: %s f1 .. fn\n" % (sys.argv[0])) 757 | sys.exit(1) 758 | 759 | for fname in sys.argv[1:]: 760 | out = subprocess.check_output(['wc', '-l', fname]) 761 | print("%s: %s" % (fname, out.split(' ')[0])) 762 | 763 | sys.exit(0) 764 | ``` 765 | 766 | -------------------------------------------------------------------------------- /slides/images/architettura-interna.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/architettura-interna.avif -------------------------------------------------------------------------------- /slides/images/bit-di-protezione.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/bit-di-protezione.avif -------------------------------------------------------------------------------- /slides/images/chmod-chown.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/chmod-chown.avif -------------------------------------------------------------------------------- /slides/images/combinare-comandi.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/combinare-comandi.avif -------------------------------------------------------------------------------- /slides/images/de-cinnamon.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-cinnamon.avif -------------------------------------------------------------------------------- /slides/images/de-elementary.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-elementary.avif -------------------------------------------------------------------------------- /slides/images/de-gnome-mac.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-gnome-mac.avif -------------------------------------------------------------------------------- /slides/images/de-gnome.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-gnome.avif -------------------------------------------------------------------------------- /slides/images/de-hyprland.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-hyprland.avif -------------------------------------------------------------------------------- /slides/images/de-plasma.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/de-plasma.avif -------------------------------------------------------------------------------- /slides/images/esempi-1.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/esempi-1.avif -------------------------------------------------------------------------------- /slides/images/esempi-2.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/esempi-2.avif -------------------------------------------------------------------------------- /slides/images/esempio-mount.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/esempio-mount.avif -------------------------------------------------------------------------------- /slides/images/file-e-metadati.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/file-e-metadati.avif -------------------------------------------------------------------------------- /slides/images/flussi-dati-standard.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/flussi-dati-standard.avif -------------------------------------------------------------------------------- /slides/images/flussi-dati.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/flussi-dati.avif -------------------------------------------------------------------------------- /slides/images/funzionalità-principali-os.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/funzionalità-principali-os.avif -------------------------------------------------------------------------------- /slides/images/genealogia-famiglia-unix.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/genealogia-famiglia-unix.avif -------------------------------------------------------------------------------- /slides/images/i-node.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/i-node.avif -------------------------------------------------------------------------------- /slides/images/implementazione-pipes.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/implementazione-pipes.avif -------------------------------------------------------------------------------- /slides/images/implementazione-ridirezione-input.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/implementazione-ridirezione-input.avif -------------------------------------------------------------------------------- /slides/images/implementazione-ridirezione-output.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/implementazione-ridirezione-output.avif -------------------------------------------------------------------------------- /slides/images/interazione-kernel-applicazioni.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/interazione-kernel-applicazioni.avif -------------------------------------------------------------------------------- /slides/images/links-ln.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 13 | 15 | 17 | 20 | 21 | 23 | 26 | 27 | 29 | 32 | 33 | 35 | 38 | 39 | 41 | 44 | 45 | 47 | 50 | 51 | 53 | 56 | 57 | 59 | 62 | 63 | 65 | 67 | 70 | 71 | 73 | 76 | 77 | 79 | 82 | 83 | 85 | 88 | 89 | 91 | 94 | 95 | 97 | 100 | 101 | 103 | 106 | 107 | 109 | 112 | 113 | 115 | 118 | 119 | 121 | 124 | 125 | 126 | 128 | 132 | 133 | 135 | 139 | 140 | 141 | 152 | 165 | 178 | 182 | 187 | 192 | 197 | 202 | 207 | 212 | 217 | 222 | 227 | 232 | 237 | 242 | 247 | 252 | 257 | 262 | 263 | 274 | 287 | 293 | 296 | 307 | 308 | 312 | 317 | 322 | 327 | 332 | 337 | 342 | 347 | 352 | 357 | 362 | 367 | 372 | 377 | 382 | 387 | 392 | 393 | 399 | 402 | 413 | 414 | 425 | 429 | 434 | 439 | 444 | 449 | 454 | 459 | 460 | 464 | 469 | 474 | 479 | 484 | 485 | 486 | -------------------------------------------------------------------------------- /slides/images/links-mv.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | 13 | 15 | 17 | 20 | 21 | 23 | 26 | 27 | 29 | 32 | 33 | 35 | 38 | 39 | 41 | 44 | 45 | 47 | 50 | 51 | 53 | 56 | 57 | 59 | 62 | 63 | 65 | 67 | 70 | 71 | 73 | 76 | 77 | 79 | 82 | 83 | 85 | 88 | 89 | 91 | 94 | 95 | 97 | 100 | 101 | 103 | 106 | 107 | 109 | 112 | 113 | 115 | 118 | 119 | 121 | 124 | 125 | 126 | 128 | 132 | 133 | 135 | 139 | 140 | 141 | 152 | 165 | 178 | 182 | 187 | 192 | 197 | 202 | 207 | 212 | 217 | 222 | 227 | 232 | 237 | 242 | 247 | 252 | 257 | 262 | 263 | 274 | 287 | 293 | 296 | 307 | 308 | 312 | 317 | 322 | 327 | 332 | 337 | 342 | 347 | 352 | 357 | 362 | 367 | 372 | 377 | 382 | 387 | 392 | 393 | 399 | 402 | 413 | 414 | 425 | 429 | 434 | 439 | 444 | 449 | 454 | 459 | 460 | 464 | 469 | 474 | 479 | 484 | 485 | 491 | 492 | -------------------------------------------------------------------------------- /slides/images/linux-kernel-map.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/linux-kernel-map.avif -------------------------------------------------------------------------------- /slides/images/mobile-market-share.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/mobile-market-share.avif -------------------------------------------------------------------------------- /slides/images/monolitici-micro-ibridi.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/monolitici-micro-ibridi.avif -------------------------------------------------------------------------------- /slides/images/multitasking.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/multitasking.avif -------------------------------------------------------------------------------- /slides/images/nomi-assoluti-e-relativi.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/nomi-assoluti-e-relativi.avif -------------------------------------------------------------------------------- /slides/images/quanto-è-complesso-un-kernel.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/quanto-è-complesso-un-kernel.avif -------------------------------------------------------------------------------- /slides/images/struttura-file-system.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/struttura-file-system.avif -------------------------------------------------------------------------------- /slides/images/terminale-testuale.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/terminale-testuale.avif -------------------------------------------------------------------------------- /slides/images/top-btop.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/top-btop.avif -------------------------------------------------------------------------------- /slides/images/top-gtop.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/top-gtop.avif -------------------------------------------------------------------------------- /slides/images/top-htop.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/top-htop.avif -------------------------------------------------------------------------------- /slides/images/top.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/top.avif -------------------------------------------------------------------------------- /slides/images/total-market-share.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbicocchi/learn-bash/7111f60c034ccddda681ae64c8987a67cfd6505c/slides/images/total-market-share.avif --------------------------------------------------------------------------------