La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per...

27
La shell BASH http://wowarea.com/italiano/linux/shell.htm 11.1 I comandi La shell e' un interprete di comandi. I comandi digitati dall'utente vengono letti dalla shell, interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli umani, cioe' un programma che accetta i comandi digitati dall'utente, li trasforma in istruzioni eseguibili dal computer e li invia al kernel per l'esecuzione. Supponiamo di voler intrattenere una conversazione con una persona straniera, ad esempio un norvegese: conoscendo la lingua, non avremmo problemi, in caso contrario avremmo bisogno di un interprete, cioe' di una persona che, conoscendo entrambe le lingue possa tradurre da una lingua all'altra e viceversa. La shell e' qualcosa di simile: converte i nostri comandi in istruzioni eseguibili dalla macchina. I comandi vengono digitati dall'utente sulla riga comandi della shell. La riga comandi e' una riga digitabile che comincia dal prompt. Il prompt e' un carattere o un insieme di caratteri che possono essere personalizzati dall'utente. Il prompt dell'utente root (il superuser) inizia con il carattere '#' (cancelletto), mentre il prompt di un utente qualsiasi inizia con il carattere '$' (dollaro). Ecco un esempio di prompt: mau@localhost mau]$_ il carattere di sottolineatura '_' (underscore) rappresenta il cursore dal quale e' possibile iniziare a scrivere. Ma un prompt come questo appena visto puo' essere personalizzato modificando la variabile PS1 presente nel file di configurazione .bash_profile. Ad esempio si potrebbe definire un prompt di questo tipo: sto aspettando un tuo comando:_ I comandi sono dei programmi che sono contenuti all'interno di alcune directory speciali come le directory /bin e /sbin. Quando l'utente digita il nome del comando/programma e preme il tasto invio, la shell cerca tale file in alcune directory predefinite (come /bin e /sbin appunto) e se lo trova lo esegue. In caso contrario produce un errore del tipo bash: nome-comando : command not found cioe' comando non trovato. Un comando e' composto dal nome del comando stesso, da una o piu' opzioni facoltative e da uno o piu' argomenti facoltativi. Un argomento e' solitamente il nome di un file sul quale il comando deve agire. Alcuni comandi prevedono degli argomenti obbligatori, altri no: nome-comando -opzione argomento il nome del comando deve essere separato dalle opzioni da uno spazio, cosi' come le opzioni e gli eventuali argomenti. Le opzioni sono specificate usando un trattino '-' e la lettera o le lettere che specificano le opzioni. L'ordine e': 1) nome-comando, spazio, 2) -opzioni, spazio, 3) argomenti. Tra il trattino ed i caratteri di opzione non devono essere presenti spazi. Ad esempio il comando ls consente l'uso di alcune opzioni come l (lista estesa) a (tutti i file) i (visualizza inode) e via dicendo. Percio' per visualizzare l'elenco dei file di una directory si puo' usare il comando 'ls -lai'. Se si vogliono visualizzare le informazioni di un file ben preciso, occorre specificare il nome del file come argomento. Ad esempio con il comando 'ls -l pippo' vengono visualizzate le informazioni sul file pippo. Ecco alcuni esempi dello stesso comando, alcuni errati ed alcuni corretti:

Transcript of La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per...

Page 1: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

La shell BASH

http://wowarea.com/italiano/linux/shell.htm

11.1 I comandi

La shell e' un interprete di comandi. I comandi digitati dall'utente vengono letti dalla shell, interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli umani, cioe' un programma che accetta i comandi digitati dall'utente, li trasforma in istruzioni eseguibili dal computer e li invia al kernel per l'esecuzione. Supponiamo di voler intrattenere una conversazione con una persona straniera, ad esempio un norvegese: conoscendo la lingua, non avremmo problemi, in caso contrario avremmo bisogno di un interprete, cioe' di una persona che, conoscendo entrambe le lingue possa tradurre da una lingua all'altra e viceversa. La shell e' qualcosa di simile: converte i nostri comandi in istruzioni eseguibili dalla macchina. I comandi vengono digitati dall'utente sulla riga comandi della shell. La riga comandi e' una riga digitabile che comincia dal prompt. Il prompt e' un carattere o un insieme di caratteri che possono essere personalizzati dall'utente. Il prompt dell'utente root (il superuser) inizia con il carattere '#' (cancelletto), mentre il prompt di un utente qualsiasi inizia con il carattere '$' (dollaro). Ecco un esempio di prompt:

mau@localhost mau]$_

il carattere di sottolineatura '_' (underscore) rappresenta il cursore dal quale e' possibile iniziare a scrivere. Ma un prompt come questo appena visto puo' essere personalizzato modificando la variabile PS1 presente nel file di configurazione .bash_profile. Ad esempio si potrebbe definire un prompt di questo tipo:

sto aspettando un tuo comando:_

I comandi sono dei programmi che sono contenuti all'interno di alcune directory speciali come le directory /bin e /sbin. Quando l'utente digita il nome del comando/programma e preme il tasto invio, la shell cerca tale file in alcune directory predefinite (come /bin e /sbin appunto) e se lo trova lo esegue. In caso contrario produce un errore del tipo

bash: nome-comando : command not found

cioe' comando non trovato. Un comando e' composto dal nome del comando stesso, da una o piu' opzioni facoltative e da uno o piu' argomenti facoltativi. Un argomento e' solitamente il nome di un file sul quale il comando deve agire. Alcuni comandi prevedono degli argomenti obbligatori, altri no:

nome-comando -opzione argomento

il nome del comando deve essere separato dalle opzioni da uno spazio, cosi' come le opzioni e gli eventuali argomenti. Le opzioni sono specificate usando un trattino '-' e la lettera o le lettere che specificano le opzioni. L'ordine e': 1) nome-comando, spazio, 2) -opzioni, spazio, 3) argomenti. Tra il trattino ed i caratteri di opzione non devono essere presenti spazi. Ad esempio il comando ls consente l'uso di alcune opzioni come l (lista estesa) a (tutti i file) i (visualizza inode) e via dicendo. Percio' per visualizzare l'elenco dei file di una directory si puo' usare il comando 'ls -lai'. Se si vogliono visualizzare le informazioni di un file ben preciso, occorre specificare il nome del file come argomento. Ad esempio con il comando 'ls -l pippo' vengono visualizzate le informazioni sul file pippo. Ecco alcuni esempi dello stesso comando, alcuni errati ed alcuni corretti:

Page 2: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

ls -lai (corretto) ls - lai (errato: tra il trattino '-' e le opzioni 'l', 'a' ed 'i' non devono esserci spazi) ls -l ai (errato: tra le opzioni 'l', 'a' ed 'i' non ci devono essere spazi) ls pippo -lai (errato: l'argomento pippo deve essere scritto dopo le opzioni -lai) ls -l pippo (corretto: l'argomento pippo e' specificato dopo l'opzione 'l') ls -ail (corretto: le opzioni non devono essere digitate rispettando una sequenza prestabilita) ls -lia (corretto: le opzioni non devono essere digitate rispettando una sequenza prestabilita) ls -ial (corretto: le opzioni non devono essere digitate rispettando una sequenza prestabilita)

un esempio di comando che puo' accettare piu' argomenti e' il comando cp (CoPy, cioe' copia) che consente di effettuare la copia di un file in un altro file con un nome diverso. Questo comando puo' essere utile se si possiede un documento importante e si desidera salvarlo in una directory di salvataggio. Qualora il file originario venisse cancellato accidentalmente, si avrebbe sempre a disposizione la sua copia nella directory di salvataggio. Percio' si puo' usare il comando cp specificando il file da copiare ed il nome della nuova copia dello stesso file. Ad esempio:

cp documento /home/mau/salvataggi/documento.bak

come si puo' vedere il primo argomento del comando cp e' 'documento' mentre il secondo argomento e' '/home/mau/salvataggi/documento.bak'. In questo comando non sono presenti opzioni poiche' non e' presente un carattere '-' che contraddistingue le opzioni. Le opzioni infatti, per definizione sono facoltative. Nel primo argomento non e' stato specificato il path completo, cioe' il percorso per raggiungere il file documento. In questo caso la shell sa di dover reperire tale file dalla directory corrente. Per default infatti se non si specifica un path, la shell cerca il file nella directory corrente (chiamata anche directory di lavoro). Nel secondo argomento invece e' stato specificato il path completo, perche' altrimenti la shell avrebbe copiato il file documento nella directory corrente con il nome documento.bak. Cio' sarebbe stato possibile, ma il nostro obiettivo e' quello di salvarlo in una directory diversa e preposta al salvataggio dei nostri documenti. Ad ogni modo il comando 'cp documento documento.bak' e' un comando perfettamente legale in quanto i due argomenti hanno nomi diversi. Non si puo' dire altrettanto del comando: 'cp documento documento', in quanto il nome del file iniziale e quello della copia sono identici. In questo caso infatti riceveremmo un messaggio di errore della shell che ci informa che il file documento esiste gia'. Non e' possibile infatti avere due file diversi con lo stesso nome all'interno della stessa directory. Se per assurdo cio' fosse possibile, nel caso si volesse visualizzare il file documento, quale dei due file dovrebbe aprire la shell? Al contrario due file con lo stesso nome possono esistere nel sistema purche' residenti in due directory distinte. Nell'esempio sopra avremmo potuto percio' scrivere:

cp documento /home/mau/salvataggi/documento

ora i due argomenti hanno lo stesso nome (documento) ma questo comando e' perfettamente legale e la shell lo interpretera' senza problemi. Questo perche' i due file pur avendo lo stesso nome risiedono in directory diverse. Non esiste percio' ambiguita' in questo caso, purche' per referenziare uno dei due file venga specificato il path completo. Nell'esempio la directory che contiene il file originale e' quella corrente cioe' /home/mau/ e per referenziare il file originale occorre scrivere /home/mau/documento; la copia di tale file invece risiede nella directory di salvataggio /home/mau/salvataggi pertanto per referenziarla occorre scrivere /home/mau/salvataggi/documento. Per lavorare con i file e' necessario conoscere alcuni comandi basilari come cp, mv, rm ed altri: cio' sara' oggetto di un successivo capitolo. E' possibile digitare piu' comandi sulla stessa riga comando separandoli con il carattere ';'. E' anche possibile scrivere dei comandi molto lunghi su piu' righe usando il carattere '\' a fine riga. In questo caso, premendo invio si potra' scrivere su una nuova riga per continuare la digitazione del comando, ma niente verra' inviato alla shell. Ogni riga che termina con '\' ed il tasto invio, crea una nuova riga ma non permette di eseguire il comando. Per eseguire il comando occorrera' premere invio avendo l'accortezza di non terminare il comando stesso con il carattere '\'. Ad esempio:

Page 3: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

questo e' un comando molto lungo \ (premo invio) pertanto ho deciso di scriverlo \ (premo invio) su piu' righe (premo invio)

In pratica il carattere \ dice alla shell: 'in questa riga il tasto invio non serve per inviarti il comando ma per andare a capo'. Il tasto \ e' in questo caso un metacarattere, cioe' un carattere che maschera un altro carattere. Il tasto invio infatti produce un carattere di 'new line' che la shell interpreta come fine del comando. Mascherando tale carattere con il metacarattere \ potremo andare a capo ma la shell non lo considerera' come carattere di fine comando. All'ultima riga, alla fine del comando, premendo invio senza \ faremo eseguire il comando alla shell. Infine un ultimo quesito: come interrompere un comando che sta impiegando troppo tempo? Premendo la combinazione di tasti CTRL-C. Tale combinazione di tasti infatti interrompe l'esecuzione di qualsiasi comando.

11.2 Un po' di storia

I sistemi operativi che esistevano prima di Linux e di Unix (ricordiamo che Linux e' un clone di Unix) possedevano gia' degli interpreti di comandi al loro interno, ma tali interpreti avevano un difetto: non erano modificabili. Quando Ken Thompson e Dennis Ritchie cominciarono a scrivere Unix, decisero di creare una interfaccia utente modificabile a seconda delle esigenze che si potevano presentare di volta in volta. Cio' che venne fuori fu un tipo di shell programmabile, contenente istruzioni simili a quelle del linguaggio C. Questo non e' un caso visto che la prima versione di Unix venne scritta in linguaggio assembler, ma fu successivamente riscritta in linguaggio C (ricordiamo che Dennis Ritchie e' insieme a Brian Kerninghan l'inventore del linguaggio C). L'interprete dei comandi creato sotto Unix venne chiamato shell. Una delle prime shell venne creata agli inizi degli anni 70 presso i Bell Laboratories AT&T ad opera di Steven R. Bourne e venne chiamata appunto Bourne shell (sh). Verso la fine degli anni 70 presso l'universita' di Berkley venne creata la C shell (csh) allo scopo di estendere la shell Bourne e renderla piu' simile al linguaggio di programmazione C. Successivamente vennero sviluppate altre shell, come la Korn shell (ksh) e la TC shell (tcsh). A causa dei problemi di copyright, l'organizzazione GNU decise di sviluppare una shell completamente libera da restrizioni e nacque cosi' la shell BASH, cioe' Bourne Again SHell. Esistono molteplici versioni di shell, la shell ash, la shell csh, la shell ksh, la shell pdksh, la shell tcsh, la shell zsh ed altre ancora, ma la shell BASH e' senza dubbio la piu' famosa ed usata in tutti i sistemi Linux. La shell BASH e' conforme allo standard POSIX. Posix sta per Portable Operating System Interface for uniX, cioe' interfaccia standard per il sistema operativo Unix e si tratta di uno standard creato verso la fine degli anni 80 allo scopo di uniformare le varie versioni di Unix. Successivamente, ad opera del gruppo IEEE (Institute of Electrical and Electronics Engineers, cioe' istituto degli ingegneri elettrotecnici ed elettronici) lo standard POSIX venne adottato anche da altri sistemi operativi non Unix come VMS, MVS, NT etc.

11.3 La shell BASH

Per iniziare ad esplorare la shell BASH e' sufficiente disporre di un emulatore di terminale. Se stiamo lavorando in ambiente grafico (X Window) occorre aprire una finestra di emulazione terminale come ad esempio Xterm, altrimenti non occorre fare nulla, in quanto ci troviamo gia' in modalita' terminale. Nel primo caso, supponendo di usare un desktop grafico come KDE, bastera' cliccare sul bottone di avvio di KDE, selezionare 'terminali' e scegliere il tipo di terminale che si preferisce. A questo punto ci si trova all'interno di una shell, che potrebbe essere BASH o CSH o TCSH o altro. Normalmente pero' la shell di default, cioe' quella preimpostata inizialmente e' la shell BASH. Ad ogni modo e' sempre possibile invocare la shell BASH digitando il comando 'sh' oppure il comando 'bash'. Questo comando non fa altro che invocare la shell BASH. Piu' esattamente invoca una subshell, ossia una shell BASH all'interno della shell corrente. Per uscire dalla subshell e' possibile digitare il comando 'exit' o premere i tasti CTRL-D (Ctrl e D contemporaneamente). Premendo CTRL-D da una subshell si ritorna alla shell corrente e premendo nuovamente CTRL-D si esce dalla shell iniziale. Per scoprire quali

Page 4: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

shell siano installate nel nostro sistema, e' possibile visualizzare il file /etc/shells utilizzando il comando:

cat /etc/shells

verra' visualizzato un elenco delle shell disponibili. Per invocare una qualsiasi delle shell elencate e' sufficiente digitarne il nome e premere invio. Cosi' facendo verra' creata una ISTANZA della shell digitata. E' possibile creare piu' istanze, l'una all'interno dell'altra (a mo' di bambola matrioska), digitando il nome della shell. Per capire meglio cio', apriamo un terminale tipo Xterm e digitiamo il comando exit (o i tasti CTRL-D): cosa accade? Si chiude la finestra Xterm e si ritorna nel desktop, poiche' il comando exit permette di uscire dalla shell. Ora facciamo una seconda prova: apriamo la finestra Xterm e digitiamo il comando: bash. Apparentemente non accade nulla in quanto il sistema non fa altro che riproporre il prompt visualizzando una seconda riga comandi vuota. In realta' pero' abbiamo appena creato una subshell bash, abbiamo cioe' invocato una shell bash all'interno della shell bash iniziale. Per dimostrarlo digitiamo il comando exit. Cosa accade? La finestra Xterm non si chiude, ed il sistema ripropone ancora il prompt. Questo avviene perche' il comando exit distrugge la subshell che abbiamo creato e ritorna a quella iniziale. A questo punto, digitando nuovamente il comando exit si chiudera' la finestra, in quanto usciremo anche dalla shell iniziale. Digitando il comando bash piu' volte possiamo creare innumerevoli subshell una all'interno dell'altra. Per uscire dalla finestra Xterm percio', dovremo digitare il comando exit tante volte quante subshell abbiamo creato. La primissima shell che Linux ci propone e' la shell di login, ossia la shell con la quale l'utente effettua il login. Cosa e' possibile fare con una shell? Prima di tutto inviare comandi al sistema, ma e' anche possibile creare dei programmi per effettuare operazioni particolari o per eseguire una serie di comandi in sequenza. Vediamo cosa accade digitando il comando:

ls -l *

apparentemente il sistema mostra un elenco di file e cio' appare ai nostri occhi come una operazione semplicissima. In realta', ogni volta che viene eseguito un semplice comando, il sistema effettua tutta una serie di operazioni invisibili ai nostri occhi delle quali non sospetteremmo minimamente l'esistenza. Nell'esempio precedente, una volta digitato il comando e premuto il tasto invio, il sistema o meglio la shell:

1. Esamina la riga di comando appena digitata 2. Espande il carattere '*' individuando tutti i file presenti nella directory corrente 3. Cerca il comando ls all'interno delle directory specificate nella variabile PATH 4. Crea una subshell dove verra' eseguito il comando ls 5. Collega l'input standard e l'output standard al comando ls 6. Esegue il comando ls all'interno della subshell appena creata 7. Chiude la subshell, torna alla shell iniziale e mostra i risultati del comando

non male come lavoro eh? ;o) Andando avanti nella conoscenza di Linux, cominceremo ad usare la shell sempre piu' spesso, in quanto molte volte troveremo piu' semplice digitare alcuni comandi piuttosto che cliccare a destra e a sinistra con il mouse. Prima di esaminare i comandi principali di Linux e di imparare a programmare la shell, e' utile apprendere a districarsi all'interno della riga comando, applicando molte scorciatoie che il sistema offre per velocizzare la digitazione dei comandi. Ad esempio e' possibile rieseguire l'ultimo comando digitando !! e premendo invio, e' possibile visualizzare i comandi appena digitati o rieseguire un comando ben preciso premendo un solo tasto,e' possibile concatenare i comandi tra loro, e via dicendo. Tutto cio' e' oggetto del prossimo paragrafo.

11.4 Le scorciatoie, la storia ed il comando history

Vediamo come 'navigare' all'interno della riga comando. Prima di tutto occorre conoscere lo scopo del tasto invio. Il tasto invio e' un tasto utilizzato per comunicare al sistema: 'ok, ho

Page 5: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

appena finito di digitare il comando, puoi eseguirlo'. Una volta premuto invio il sistema viene allertato per eseguire il comando digitato. Ogni volta che si preme un tasto, indipendentemente dal sistema operativo usato, viene attivata una cosidetta richiesta di interrupt di tipo hardware che, molto piu' semplicemente significa che un dispositivo hardware (la tastiera in questo caso) 'sveglia' il sistema dal torpore e gli comunica che deve intraprendere un'azione. In realta' il sistema non dorme, ma esegue altre operazioni in sottofondo. Il sistema e' sempre in movimento ed effettua molte operazioni continuamente. Pero', ogni volta che viene premuto un tasto invio, il sistema viene bruscamente interrotto da qualsiasi azione stava eseguendo per rispondere alla richiesta di attenzione inviata dalla tastiera. Se ci si trova all'interno di un programma di scrittura e stiamo scrivendo una lettera, ad ogni tasto premuto il sistema risponde visualizzando il carattere corrispondente, a meno che non si tratti di un tasto speciale come ad esempio il tasto invio, il tasto canc, il tasto freccia in alto e via dicendo. All'interno di un programma di scrittura di testi il tasto invio permette di andare a capo, mentre all'interno della riga comando serve per avvertire il sistema che ci occorre la sua attenzione per eseguire il comando appena digitato. Ma prima di inviare il comando al sistema con il tasto invio, e' possibile editarlo (il termine editare e' uno italianismo del termine inglese edit che sigifica modificare). Per modificare quanto si sta scrivendo sulla riga comando esistono molte possibilita'. Ad esempio e possibile andare avanti ed indietro nella riga comandi premendo i tasti freccia a sinistra e freccia a destra. Oppure e' possibile cancellare l'ultimo carattere appena digitato premendo il tasto canc. Ma e' anche possibile cancellare l'ultima parola appena digitata premendo i tasti CTRL-W. Oppure e' possibile cancellare l'intera riga appena scritta premendo i tasti CTRL-U. I tasti freccia in alto e freccia in basso invece, sono utili per scorrere la storia dei comandi. La shell infatti memorizza i comandi che vengono digitati all'interno di un file di sistema, che e' possibile scorrere a video tramite i tasti freccia in alto e freccia in basso. Cio' permette di eseguire gli stessi comandi piu' volte senza doverli riscrivere, oppure di visualizzare un comando precedentemente digitato per modificarlo e rieseguirlo. I comandi eseguiti vengono memorizzati all'interno di un file di sistema che si trova all'interno della directory home dell'utente e che si chiama .bash_history. Ad esempio l'utente mau avra' un suo file di storia dei comandi all'interno della directory /home/mau. Per visualizzare i comandi presenti in questo file e' possibile usare il comando history. Oppure e' possibile visualizzarlo con il comando cat. Il comando cat visualizza il contenuto di un file (in realta' lo scopo del comando cat e' ben altro, ma per ora e' irrilevante saperlo). Ad esempio 'cat pippo' permette di visualizzare a video il contenuto del file pippo. Quindi l'utente mau puo' visualizzare la sua storia dei comandi con il comando cat /home/mau/.bash_history. Un sistema Unix o Linux e' un sistema multiutente, percio' nello stesso istante possono coesistere piu' utenti che lavorano con lo stesso sistema. Bene, ogni utente ha a disposizione una directory home personale, una shell personale e ovviamente, un file di storia personale. Esistono pertanto tante copie del file .bash_history quanti sono gli utenti che posseggono un account su un sistema. Nel caso di un sistema Linux su un PC utilizzato da un unico utente sara' presente ovviamente una sola copia del file .bash_history. Digitando il comando

history

verra' visualizzato a video un elenco di tutti i comandi digitati. A sinistra di ciascun comando e' presente un numero progressivo che rappresenta un identificativo utilizzabile per richiamare quel dato comando. Infatti digitando il carattere ! e premendo invio viene eseguito l'ultimo comando appena eseguito, ma digitando il carattere ! seguito da un numero, e' possibile eseguire il comando referenziato da quel numero. Ad esempio, se digitando history si ottiene:

1 ls 2 cd miadir 3 cat pippo 4 ls -lai *

digitando il comando:

!3

Page 6: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

viene eseguito il comando 'cat pippo' (questo comando visualizza il file pippo). Un'altra scorciatoia molto utile e' il tasto TAB. E' possibile espandere nomi di file o di comandi digitando una serie di caratteri (pattern) e premendo TAB. Ad esempio, supponiamo di voler eseguire il comando mount, ma non ricordiamo esattamente il nome del comando. Sappiamo solo che inizia per 'm' o per 'mo'. Bene, digitando m e premendo TAB, il sistema visualizzera' tutti i comandi che iniziano per m. Digitando mo e premendo TAB, il sistema visualizzera' tutti i comandi che iniziano per mo. Premendo il tasto TAB senza digitare alcun carattere il sistema chiedera' la conferma per visualizzare tutti i comandi che conosce. La ricerca dei comandi viene effettuata scorrendo la variabile di sistema PATH, che contiene tutte le directory dove il sistema deve ricercare i comandi da eseguire. Se si digita un comando e successivamente alcuni caratteri e poi si premte TAB, il sistema non cerca il nome di un comando ma il nome di un file. Ad esempio, digitando:

ls pr

e premendo TAB senza premere invio, la shell visualizzera' tutti i file che iniziano per pr come prova, provetta, prosa, prassi e via dicendo, ed inoltre consentira' di proseguire il comando ls continuando a digitare i caratteri rimanenti. Infatti a questo punto e' possibile digitare ls prov e premere TAB. La shell visualizzera' ora solo i file prova e provetta. E' un sistema utile quando sono presenti centinaia di voci e non rammentiamo bene il nome esatto del comando o del file che ci interessa.

11.5 Sommario delle scorciatoie della shell

!! riesegue l'ultimo comando appena eseguito !n riesegue l'ennesimo comando presente nella storia, dove 'n' e' il numero del

comando da rieseguire !stringa riesegue l'ultimo comando che inizia con i caratteri specificati in 'stringa' !stringa:p visualizza l'ultimo comando che inizia con i caratteri specificati in 'stringa' !?comando? ricerca il comando specificato tra punti interrogativi fc 4 permette di modificare in comando numero 4 con l'editor predefinito (solitamente

vi) fc -e 'nome' 4 permette di modificare in comando numero 4 con l'editor 'nome'

specificato ^comando1^comando2 riesegue l'ultimo comando eseguito che contiene la parola

'comando1' sostituendola con 'comando2'. history visualizza l'elenco di tutti i comandi eseguiti CTRL-U cancella tutta la riga dalla posizione del cursore all'inizio della riga CTRL-K cancella tutta la riga dalla posizione del cursore alla fine della riga CTRL-W cancella una parola dalla posizione del cursore all'inizio della riga ALT-D cancella una parola dalla posizione del cursore alla fine della riga freccia sinistra sposta il cursore di un carattere a sinistra freccia destra sposta il cursore di un carattere a destra freccia su scorre la storia dei comandi a ritroso freccia giu' scorre la storia dei comandi in avanti tasto home sposta il cursore all'inizio della riga CTRL-A sposta il cursore all'inizio della riga tasto fine sposta il cursore alla fine della riga CTRL-E sposta il cursore alla fine della riga ALT-F sposta il cursore alla fine della parola successiva (F sta per forward, successivo) CTRL-B sposta il cursore all'inizio della parola precedente (B sta per backward,

precedente) CTRL-T inverte gli ultimi due caratteri a sinistra del cursore, cioe' ab diventa ba (T sta

per transpose) ALT-T inverte le ultime due parole a sinistra del cursore, cioe' cp pippo pluto diventa cp

pluto pippo

Page 7: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

ALT-U trasforma in maiuscolo la parola su cui si trova il cursore (U sta per uppercase, cioe' maiuscolo)

ALT-L trasforma in minuscolo la parola su cui si trova il cursore (L sta per lowercase, cioe' minuscolo)

tasto TAB espande il nome di un file o di un comando

11.6 Gli alias

Digitare frequentemente dei comandi complessi contenenti numerose opzioni puo' essere alla lunga fastidioso, percio' puo' esser conveniente utilizzare dei sinonimi piu' facili da ricordare e da digitare. Prendiamo ad esempio il comando:

mount -t vfat /dev/hdc1 /mnt/mydir

questo comando 'monta' la prima partizione presente sul terzo disco alla directory /mnt/mydir, specificando la tipologia del filesystem (vfat). Supponiamo che tale partizione contenga i file di Windows: sarebbe interessante poter sostituire tale comando con il comando 'montaWindows'. Bene, e' effettivamente possibile fare cio' creando l'alias montaWindows per tale comando. Per creare l'alias 'montaWindows' occorre usare il comando alias:

alias montaWindows='mount -t vfat /dev/hdc1 /mnt/mydir'

questo comando non fa altro che creare il sinomimo montaWindows. Una volta eseguito questo comando, sara' possibile usare il sinonimo creato: lanciando montaWindows verra' eseguito il comando 'mount -t vfat /dev/hdc1 /mnt/mydir'. Gli alias possono essere utili anche per creare dei comandi in lingua italiana. Ad esempio, il comando cp si potrebbe sostituire con 'copia', il comando rm potrebbe chiamarsi 'cancella' oppure potremmo sostituire il comando pwd con 'dovemitrovo'. Un alias puo' essere utile anche per limitare operazioni pericolose. Ad esempio, il comando rm cancella i file irreversibilmente senza chiedere conferma, a meno che non venga usata l'opzione -i. Pertanto, poiche' il comando rm cancella senza conferma, mentre il comando 'rm -i' cancella chiedendo conferma, e' possibile creare un sinonimo di rm in questo modo:

alias rm='rm -i'

da questo momento in poi, digitando il comando rm il sistema chiedera' conferma prima di cancellare qualsiasi file. ;o) Per eliminare un alias e' possibile usare il comando <b<unalias< b="">: </b<unalias<>

unalias rm

dove rm e' il nome dell'alias.

11.7 Opzioni per modificare il comportamento della shell

Per evitare di uscire dalla shell digitando inavvertitamente i tasti CTRL-D, e' possibile usare l'opzione ignoreeof. Quest'opzione viene attivata con il comando set. Il comando set accetta due opzioni: -o e o. -o imposta ignoreeof mentre o lo elimina. Il altre parole, per impostare l'opzione ignoreeof occorre digitare il comando:

set -o ignoreeof

mentre per ripristinare il comportamento normale della shell occorre digitare:

set o ignoreeof

Page 8: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

Esistono altre opzioni per modificare il comportamento della shell, tra le quali noclobber e noglob. La prima serve per evitare che un file venga cancellato inavvertitamente da una operazione di redirezione. Ad esempio il comando 'cat pippo > pluto' potrebbe sovrascrivere il contenuto di pluto con pippo. Impostando l'opzione noclobber con il comando set, non sara' piu' possibile sovrascrivere il file pluto in quanto la shell presentera' un messaggio di errore. Vediamo:

ls > pluto (copio l'elenco dei file presenti nella directory all'interno del file pluto) set -o noclobber (ora imposto noclobber) ls > pluto (provo ad eseguire nuovamente questo comando ma non riesco perche' la shell invia un messaggio di errore) pluto: file exist (la shell infatti mi informa che il file esiste gia' e l'operazione non e' consentita in quanto lo cancellerebbe).

L'opzione noglob infine, disattiva i caratteri speciali della shell. Una volta impostata tale opzione i caratteri *, ?, [, ] e ~ non verranno piu' espansi se presenti all'interno di un file. Cio' puo' essere utile per fare riferimento a file che contengono tali caratteri. Ad esempio, scrivendo 'pippo*' normalmente la shell espanderebbe pippo con tutti i nomi dei file che iniziano con 'pippo' e terminano con una qualsiasi sequenza di caratteri: impostando l'opzione noglob, cio' non accadrebbe piu' e sarebbe possibile far riferimento al file pippo* senza problemi. Inutile dire che e' comunque meglio evitare di creare nomi di file contenenti caratteri speciali! ;o)

11.8 Configurazione della shell

La shell BASH ha delle impostazioni standard generali per tutti gli utenti, ma ogni utente puo' avere una versione personalizzata della propria shell modificando opportunamente alcuni file di configurazione. All'interno di questi file sono presenti delle variabili che contengono dei valori predefiniti. Una variabile e' un'area di memoria alla quale viene assegnato uno specifico valore. Tale area viene creata al momento del login e viene distrutta al momento del logout. Una variabile puo' essere paragonata ad una scatola nera che puo' contenere dei valori. Ad esempio la variabile USER contiene il nome dell'utente che ha effettuato il login. Quando un utente effettua il login nel sistema, Linux crea una shell di login ed inizializza alcune variabili con dei valori predefiniti. L'utente puo' creare delle variabili personali e puo' modificare alcune di quelle predefinite dal sistema. Per convenzione le variabili della shell sono definite con nomi costituiti da lettere maiuscole. Ad esempio, la variabile HOME definisce il percorso della directory home dell'utente. Le variabili vengono inizializzate dal sistema al momento del login da parte di un utente e vengono distrutte al momento del logout, cioe' quando l'utente si scollega dal sistema. Per visualizzare il contenuto di una variabile si puo' eseguire il comando echo, seguito dal carattere $ e dal nome della variabile. Ad esempio, un ipotetico utente mau, visualizzando la variabile HOME utilizzando il comando:

echo $HOME

produrra' a video il percorso /home/mau. I file di configurazione della shell BASH sono 5:

/etc/profile /etc/bashrc /home/mau/.bash_profile /home/mau/.bashrc /home/mau/.bash_logout

le prime 2 sono generali per tutti gli utenti e le altre 3 sono specifiche per ogni utente e se vengono modificate ridefiniscono quelle generali. Percio' se un utente 'mau' modifica ad esempio il file .bash_profile presente nella sua directory home (/home/mau), tali modifiche saranno valide per lui ma non per tutti gli altri utenti che continueranno ad usare il file profile generale presente nella directory /etc. Stesso discorso vale per il file bashrc. Il file profile presente nella directory /etc puo' essere paragonato al file autoexec.bat del dos. Questo file

Page 9: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

infatti contiene l'inizializzazione di alcune variabili di sistema e l'esecuzione di alcuni programmi di avvio. I file bashrc invece contiene gli alias ed alcune funzioni definibili dall'utente. Un alias e' un nome mnemonico che si puo' dare ad un comando. Ad esempio il comando che visualizza tutti i file di una directory:

ls -lai

puo' avere come alias il nome 'elencafile' oppure 'lsa' oppure 'dir' (per gli amanti del DOS) o un qualsiasi altro nome scelto dall'utente. Un'altra differenza che contraddistingue il file profile dal file bashrc sta nel fatto che mentre il primo viene eseguito al momento del login da parte dell'utente, il secondo viene eseguito ogni volta che viene creata una shell o una subshell. Quando l'utente effettua il login, viene creata una shell chiamata shell di login, percio' viene eseguito prima il file profile e poi il file bashrc. Quando un utente crea una subshell od una shell che non e' di login (ad esempio apre una finestra Xterm) viene eseguito il file bashrc. In un sistema Linux, oltre ai file di inizializzazione della shell attivati al momento del login, e' presente anche un file attivato al momento del logout. Una subshell viene creata automaticamente dal sistema ogni volta che si esegue un comando oppure intenzionalmente dall'utente lanciando il comando che crea la shell (che nel caso di shell BASH e' 'bash'). Ogni volta che un utente effettua il logout e si scollega dal sistema, viene eseguito il file .bash_logout. In questo file sono presenti tutte le operazioni di chiusura che l'utente vuole eseguire. Questo file non viene creato automaticamente come gli altri gia' visti, percio' occorre crearlo manualmente con un editor di testi qualsiasi (ad esempio vi). In questo file puo' essere contenuto ad esempio un messaggio di arrivederci con un comando tipo echo "Arrivederci". Riassumendo, non appena un utente effettua il login nel sistema:

viene automaticamente creata una shell di login viene eseguito il file profile presente nella directory /etc che definisce le variabili

generali viene eseguito il file .bash_profile presente nella directory home dell'utente viene eseguito il file bashrc presente nella directory /etc dove sono presenti gli alias

dei comandi viene eseguito il file .bashrc presente nella directory home dell'utente

non appena l'utente effettua il login dal sistema, viene eseguito il file .bash_logout.

11.9 I caratteri speciali di completamento dei nomi

Come gia' visto precedentemente per nominare un file e' possibile usare fino a 256 caratteri, pero' esiste una limitazione: non e' possibile usare determinati caratteri. Tali caratteri infatti sono speciali, in quanto se presenti, vengono interpretati dalla shell seguendo delle regole ben precise. Ecco un elenco di alcuni di tali caratteri:

. / " $ & ' ? ~ ! < > * = .. ; [ ]

ognuno di questi caratteri ha un significato ben preciso per la shell, ad esempio il carattere '.' significa la directory corrente, il carattere '..' e' la directory immediatamente superiore, il carattere '~' rappresenta la directory home, il carattere ';' e' usato per separare piu' comandi sulla stessa riga e via dicendo. I caratteri speciali che verranno analizzati qui sono i caratteri: '*', '?' , '[' e ']'. Tali caratteri vengono definiti anche caratteri jolly o anche wildcards. Si tratta di caratteri che vengono usati dalla shell per espandere il nome di un file. Il carattere '*' significa qualsiasi carattere o qualsiasi sequenza di caratteri. Digitando una serie di caratteri e terminando la serie con il carattere *, la shell individuera' tutti i file all'interno della directory corrente i cui nomi iniziano con la serie di caratteri digitati e terminano con una sequenza qualsiasi di caratteri. Digitando solo il carattere *, la shell individuera' semplicemente tutti i file all'interno della directory corrente. Ad esempio, supponiamo che l'utente mau possegga all'interno della directory documenti i file lettera1, lettera2, doc, letteratura.txt, lettere e doc2. Il comando:

Page 10: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

ls lett*

visualizzera' solo i file lettera1, lettera2, lettere e letteratura.txt. i file doc1 e doc2 non verranno visualizzati. Cio' perche' la shell considerera' tutti i file presenti all'interno della directory che iniziano con i caratteri 'lett' e terminano con una sequenza qualsiasi di caratteri. I file doc1 e doc2 non soddisfano tali requisiti. Invece, digitando il comando:

ls lettera*

la shell visualizzera' i file lettera1, lettera2 e letteratura.txt. Questa volta, oltre ai file doc1 e doc2 verra' escluso dall'elenco anche il file lettere che percio' non verra' visualizzato. Ma supponiamo ora che di tutti i file presenti nella directory volessimo visualizzare solamente i file lettera1 e lettera2: come dovremmo usare il carattere '*'? Bene, in questo caso scrivendo lettera* visualizzeremmo i file lettera1, lettera2 ma anche letteratura.txt. Come fare allora? Semplice: potremmo usare il carattere '?'. Infatti il carattere ? significa qualsiasi carattere. Nel nostro esempio pertanto potremmo scrivere il comando: 'ls lettera?'. La shell interpretera' la stringa 'lettera?' cercando all'interno della directory corrente tutti i file il cui nome inizia per 'lettera' e termina con 1 carattere qualsiasi. La differenza tra il carattere * ed il carattere ? sta nel fatto che mentre con il primo si intende qualsiasi sequenza di caratteri, con il secondo si intende qualsiasi carattere. Per specificare ulteriormente i caratteri da espandere, si puo' specificare uno o piu' caratteri all'interno di una coppia di parentesi quadre. I caratteri definiti tra parentesi quadre stabiliscono un insieme di caratteri validi che devono essere presenti all'interno del nome dei file da considerare. Ad esempio, supponendo di avere all'interno della directory corrente i file doc1, doc2, docA1, docB1 e doca2, specificando la stringa doc[1A] prenderemmo in considerazione solamente i file doc1 e docA1 in quanto entrambi hanno un nome che inizia con i caratteri doc e termina con il carattere 1 o con il carattere A. Come possiamo osservare non viene preso in considerazione neanche il file doca2 perche' Linux e' un sistema case sensitive, cioe' fa differenza tra caratteri maiuscoli e caratteri minuscoli. Per Linux i caratteri 'A' e 'a' sono due caratteri completamente diversi. Se avessimo voluto prendere in considerazione anche il file doca2, avremmo dovuto scrivere doc[1Aa]. L'ordine con il quale vengono digitati i caratteri jolly all'interno delle parentesi quadre e' ininfluente, percio' avremmo potuto scrivere anche doc[Aa1] o doc[A1a]. Ma i caratteri jolly possono servire non solamente a specificare dei nomi di file che cominciano con una sequenza di caratteri prestabilita e terminano con una sequenza di caratteri indefinita: infatti e' possibile anche effettuare l'operazione inversa, cioe' specificare dei nomi di file che iniziano con una sequenza di caratteri indefinita e che pero' terminano con dei caratteri ben precisi. Ad esempio, supponendo di avere all'interno della directory corrente i file doc.txt, doc.doc, lettera.doc e lettera.txt, scrivendo *.doc specificheremmo solamente i file doc.doc e lettera.doc. All'interno delle parentesi quadre e' possibile anche specificare una sequenza di valori usando il carattere '-'. Ad esempio la stringa doc[1-9] significa qualsiasi file il cui nome inizia per doc e termina con una cifra da 1 a 9. Supponendo di avere all'interno della nostra directory i file doc1, doc2, doc3, doc4 e doc5, scrivendo la stringa doc[1-3] la shell prendera' in considerazione unicamente i file doc1, doc2 e doc3. E' possibile combinare tutti questi caratteri speciali in qualsiasi modo, percio' una stringa del tipo d?c[1-3].* e' perfettamente valida. Supponendo infatti di avere all'interno della nostra directory i file doc1.txt, dic2.txt, dec3.doc, documenti, lettere, fax e moduli, con la stringa d?c[1-2].* prenderemmo in considerazione unicamente i file doc1.txt, dic2.txt e dec3.doc.

11.10 Standard input, standard output e standard error

Quando Unix, e di conseguenza anche Linux, quando vennero sviluppati, si volle mantenere indipendenti la struttura logica dei file dalla conformazione del supporto fisico, di conseguenza venne sviluppato il concetto secondo il quale qualsiasi dispositivo fisico puo' essere visto logicamente come un file, ossia come una sequenza continua di byte. Questo concetto logico si estende anche alle operazioni di input ed output, infatti in queste operazioni i dati sono organizzati come sequenze continue di byte. I dati introdotti in input con la tastiera vengono inseriti all'interno di un canale costituito da una sequenza continua di byte. Allo stesso modo i

Page 11: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

dati prodotti a video vengoni inseriti all'interno di un altro canale. Tali canali vengono definiti standard input e standard output. Pertanto la tastiera e' il canale standard input ed il video e' il canale standard output. Qualsiasi comando quindi, accetta in input dei valori che legge dallo standard input (la tastiera) e produce dei risultati scrivendoli nello standard output (il video). Ma un comando puo' produrre anche dei messaggi di errore se per esempio vengono introdotti dei valori in input non validi. I messaggi di errore dei comandi vengono scritti in un terzo canale, lo standard error. Lo standard error generalmente coincide con lo standard output, nel senso che sia il risultato di un comando che eventuali errori vengono visualizzati a video. Digitando il comando cat pippo, otterremo a video il contenuto del file pippo, in quanto cat accetta dallo standard input (la tastiera) l'argomento pippo e lo invia allo standard output (il video). Cosa accade pero' se si digita cat senza argomenti e si preme il tasto invio? Verra' eseguito il comando cat che leggera' lo standard input. In pratica ìl comando cat accetta da tastiera degli argomenti. A questo modo e' possibile scrivere un documento come una lettera ad esempio. Per terminare la scrittura della lettera occorre premere la combinazione di tasti CTRL-D. Tale combinazione di tasti corrisponde infatti al carattere di fine file (EOF, cioe' End Of File). Infatti lo standard input e' un file come un altro. Usando CTRL-D scriviamo il carattere di fine file ed il comando cat smettera' di leggere cio' che digitiamo. Questi 3 canali sono dei file ben distinti e separati, e questo e' un concetto importante da tenere a mente, poiche' Linux (cosi' come Unix) ci consente di redirigere un canale standard da o verso un file. E' possibile ad esempio inviare l'output prodotto da un comando non a video (il canale standard output) ma all'interno di un file. Oppure e' possibile far si che un comando accetti in input dei valori prendendoli da un file piuttosto che dalla tastiera. Queste operazioni di redirezione vengono effettuate utilizzando gli operatori di redirezione '<', '>' e '>>'. Il primo e' l'operatore di redirezione dell'input standard, mentre gli ultimi due sono gli operatori di redirezione dell'output standard. Supponiamo che digitando il comando ls il sistema produca a video la seguente lista di file:

pippo pluto topolino minnie

supponiamo ora di voler conservare questo elenco di file all'interno di un documento chiamato disney.txt. Bene, per eseguire tale operazione dovremmo redirigere l'output standard (il video) all'interno del file disney.txt tramite l'operatore '>' in questo modo:

ls > disney.txt

eseguendo tale comando la shell interpretera' l'operatore '>' redirigendo il risultato del comando ls all'interno del file disney.txt. In altre parole tale comando fornisce le seguenti istruzioni per la shell: 'esegui il comando ls ma non inviare il risultato a video perche' lo devi inviare all'interno del file disney.txt; se tale file non esiste crealo'. Cosi' la shell creera' il file disney.txt ed inviera' l'output del comando ls all'interno di tale file. Qualora il file disney.txt fosse gia' presente, la shell lo creera' nuovamente, distruggendo eventuali dati gia' presenti. Nel nostro esempio il comando ls > disney.txt creera' il file disney.txt scrivendo all' interno i seguenti valori:

pippo pluto topolino minnie disney.txt

per visualizzare il contenuto del file disney.txt potremmo utilizzare il comando cat:

cat disney.txt pippo

Page 12: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

pluto topolino minnie disney.txt

Come possiamo notare all'interno del file disney.txt e' presente anche il nome disney.txt. Infatti con l'operatore > la shell prima crea il file disney.txt e successivamente esegue il comando ls (che elenchera' anche il file appena creato). Supponiamo ora di cancellare i file pluto topolino e minnie dalla nostra directory e di creare un file chiamato paperino.

ls (elenca i file della directory) pippo pluto topolino minnie disney.txt ls > disney.txt (reindirizza il risultato del comando ls nel file disney.txt) cat disney.txt (visualizza il contenuto del file disney.txt) pippo pluto topolino minnie disney.txt rm pluto topolino minnie (cancella i file pluto topolino e minnie) ls (visualizza il contenuto della directory dove ora sono presenti solo i file pippo e disney.txt) pippo disney.txt cp pippo paperino (copia pippo in paperino, cioe' crea il file paperino che e' una copia di pippo) ls (visualizza il contenuto della directory che ora contiene i file pippo, paperino e disney.txt) pippo paperino disney.txt ls > disney.txt (reindirizza il risultato del comando ls nel file disney.txt distruggendo cio' che conteneva precedentemente) cat disney.txt (infatti visualizzando il file disney.txt vedremo che pluto, topolino e minnie non sono piu' presenti) pippo paperino disney.txt

Come abbiamo visto, eseguendo nuovamente il comando ls > disney.txt il contenuto precedente del file disney.txt viene azzerato e vengono scritti all'interno solo i nomi pippo e paperino. Come fare per aggiornare il file disney.txt senza distruggere il contenuto precedente? Potremmo usare l'operatore di redirezione dello standard output '>>'. Infatti tale operatore non distrugge il file, in quanto permette di accodare alla fine del file il risultato prodotto dal comando. Tornando al nostro esempio eseguendo il comando ls per la seconda volta ma utilizzando l'operatore >> al posto dell'operatore > il contenuto del file non verrebbe cancellato in quanto il risultato del comando ls verrebbe accodato ad esso. Percio' eseguendo il comando ls >> disney.txt si otterrebbe un file disney.txt contenente i nomi: pippo, pluto, topolino, minnie, pippo e paperino. Come si puo' osservare pippo e' ripetuto due volte in quanto il contenuto del file non e' stato cancellato. Ovviamente per utilizzare il comando ls >> disney.txt occorre che il file disney.txt sia esistente, in caso contrario otterremmo un errorre dalla shell. Infatti mentre l'operatore '>' crea un nuovo file, l'operatore '>>' accoda all'interno di un file gia' esistente. L'operazione di redirezione puo' avvenire anche all'inverso, in quanto e' possibile redirigere il canale di input standard. Generalmente i comandi accettano dei valori leggendoli dal canale di input standard cioe' dalla tastiera. Per esempio il comando:

Page 13: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

ls /home/mau/documenti

visualizza il contenuto della directory /home/mau/documenti. Tale directory e' un argomento che abbiamo fornito noi in input digitandolo da tastiera. E' possibile pero' fare in modo che il comando ls prenda in input l'argomento '/home/mau/documenti' non da tastiera ma ad esempio da un file. Cio' puo' essere fatto redirigendo l'input standard, cioe' copiando il contenuto di un file all'interno del canale di input standard (ricordiamo che in Linux la struttura fisica dei dispositivi e' separata dalla struttura logica dei file, pertanto un comando sa che deve accettare eventuali argomenti dal canale di input standard ignorando completamente se tale canale sia la tastiera, il disco o qualsiasi altro supporto). Ad esempio il comando:

ls < pippo

redirige il contenuto del file pippo nell'input standard. Come gia' accennato, ogni volta che viene eseguito un comando la shell si occupa di collegare input standard ed output standard al comando stesso: interpretando il comando, in questo caso la shell collega l'output standard e l'input standard ma prima di far cio' redirige il contenuto del file pippo all'interno dell'input standard. Percio' se all'interno del file e' presente la stringa '/home/mau/documenti', tale stringa verra' copiata dalla shell (cioe' rediretta) nel canale di input standard. Quando il comando ls leggera' l'input standard, trovera' quindi all'interno la stringa '/home/mau/documenti' appena copiata dalla shell. E' possibile redirigere l'input e l'output standard contemporaneamente, infatti il comando:

cat < pippo > pluto

legge dal file pippo e scrive il risultato nel file pluto.

11.11 Le pipe

Spesso puo' essere utile inviare l'output di un comando non a video ma ad un altro comando prima di essere visualizzato. Supponiamo ad esempio di voler visualizzare l'elenco dei file contenuti all'interno di una directory. Supponiamo anche che il risultato prodotto sia un elenco interminabile di file. E possibile in questo caso utilizzare il comando more che accetta in input dei dati e li visualizza a video sotto forma di pagine formattate, consentendo la visualizzazione di una pagina per volta. Premendo un tasto qualsiasi il comando more visualizza la pagina successiva. Come e' possibile collegare l'output prodotto dal comando ls al comando more? E' possibile utilizzando l'operatore di pipe '|'. E' il carattere che si trova nel tasto posto sotto al tasto esc. Si ottiene premendolo contemporaneamente al tasto shift (il tasto che permette di scrivere un carattere maiuscolo). Usando l'operatore di pipe '|' comunichiamo alla shell che e' necessario collegare l'output standard del primo comando all'input standard del secondo comando. Ad esempio il comando:

ls | more

invia il risultato del comando ls al comando more. Il comando more a sua volta formatta tale risultato e lo invia a video. E' possibile usare il comando less al posto del comando more, in quanto tale comando permette di scorrere le pagine avanti ed indietro (less e' in sostanza una versione migliorata del comando more). Un'altra cosa che Linux permette di fare e' consentire ad un comando di accettare degli argomenti sia dall'input standard e sia da un file. Redirigere l'input standard con l'operatore < significa in pratica copiare dei dati da un file all'interno dell'input standard. Puo' essere utile pero' far si che un comando accetti degli argomenti dall'input standard e contemporaneamente da un file. E' possibile farlo utilizzando l'operatore '-'. L'operatore '-' rappresenta infatti l'input standard. Vediamo un esempio concreto. Supponiamo di voler visualizzare il contenuto di un file a video usando il comando cat. Supponiamo anche di voler visualizzare prima del contenuto del file anche il nome della directory corrente che lo contiene. Il nome della directory corrente si ottiene con il comando pwd (Print Working Directory, cioe' stampa la directory di lavoro). Potremmo scrivere percio':

Page 14: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

pwd | cat - miofile

cosa significano questi comandi? Il comando pwd crea in output il nome della directory, supponiamo /home/mau ma invece di visualizzarlo a video lo invia tramite l'operatore di pipe '|' al comando cat. Cio' accade perche' la shell interpretando la riga comandi, nota l'operatore di pipe '|' e quindi collega l'output standard del comando pwd all'input standard del comando cat. Il comando cat quindi leggera' dall'input standard la riga prodotta da pwd (/home/mau) ma anche il nome del file miofile (miofile e' stato digitato con la tastiera pertanto si trova all'interno dell'input standard). In altre parole il comando cat visualizzera' prima il primo argomento letto dall'input standard, cioe' il nome della directory prodotto dal comando pwd e successivamente il secondo argomento letto dall'input standard, cioe' il file miofile. Il risultato finale sara' il nome della directory ed il contenuto del file prodotti a video. Cosi' come e' possibile forzare un comando ad accettare un argomento dall'input standard ed un altro da un file e' anche possibile l'operazione inversa. E' possibile cioe' inviare il risultato di un comando sia a video (output standard) che in un file. Cio' e' possibile utilizzando il comando tee. Il comando cat pippo visualizza a video il contenuto del file pippo. Per visualizzarne il contenuto e contemporaneamente scriverlo all'interno di un altro file, si puo usare il comando tee in questo modo:

cat pippo | tee pluto

il comando cat legge il contenuto del file pippo e lo invia allo standard output. La shell pero' notando l'operatore di pipe '|' collega l'output standard del comando cat all'input standard del comando tee. Il comando tee a sua volta legge il contenuto del file pippo dal suo input standard e lo invia sia a video che all'interno del file pluto. Vediamo a questo punto come redirigere l'ultimo canale standard visto: il canale error standard. Quando un comando viene eseguito, normalmente viene prodotto un risultato a video. Se pero' sbagliamo qualcosa nel digitare il comando, viene prodotto a video un messaggio di errore. Supponiamo ad esempio di voler visualizzare il contenuto del file pippo ma digitando erroneamente scriviamo:

cat peppo

poiche' il file peppo non esiste, il comando cat visualizzera' a video il messaggio di errore cat: peppo not found. Infatti normalmente il canale di standard error e' il video. Come fare per scrivere il messaggio di errore non a video ma all'interno di un file? Ogni canale standard e' identificabile da un numero: 0 per il canale standard input, 1 per il canale standard output e 2 per il canale standard error. Digitare il comando cat peppo > errori non produrrebbe il risultato voluto in quanto l'operatore > redirige lo standard output all'interno del file errori ma non lo standard error. Poiche' il file peppo non esiste il comando cat non scrive nulla nello standard output ma scrive un messaggio di errore nello standard error. Per specificare quale canale redirigere nel file errori si puo' usare il numero che lo identifica. Nel nostro esempio desideriamo redirigere lo standard error (2) e non lo standard output (1) all'interno del file errori. Pertanto possiamo scrivere:

cat peppo 2> errori

Il numero 2 a sinistra dell'operatore > specifica che il canale che intendiamo redirigere non e' lo standard output ma bensi' lo standard error. Il comando cat pippo > pluto scrive il contenuto di pippo all'interno di pluto. Questo perche' per default l'operatore > redirige lo standard output. In pratica e' la stessa cosa che scrivere cat pippo 1> pluto, in quanto in tal caso 1 se non specificato e' sottinteso. Diverso e' il comando 'cat pippo 1> dati 2> errori'. Qui si vuole infatti specificare che occorre inviare il contenuto di pippo all'interno del file dati ed eventuali errori all'interno del file errori. Tanto per complicare un po' di piu' le idee, illustriamo come sia possibile redirigere un canale standard all'interno di un altro canale standard. Fino ad ora abbiamo infatti rediretto un canale standard verso un file o viceversa, un file verso un canale standard. Per far riferimento ad un canale standard occorre usare l'operatore &. Infatto &0 fa riferimento al canale standard input, &1 fa riferimento al canale standard output e &2 fa

Page 15: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

riferimento al canale standard error. Quindi la stringa '2>&1' significa: redirezione del canale standard error (2) nel canale standard output (&1). Vediamo ad esempio il comando:

cat non_esiste 1> dati 2>&1

tale comando visualizza il contenuto del file non_esiste inviandolo non a video ma all'interno del file dati ed inoltre invia eventuali errori all'interno dello stesso file. In questo caso poiche' il file non_esiste come dice il nome non esiste ;o) verra' prodotto un messaggio di errore. Analizziamo il comando: cat legge dallo standard input il nome del file da visualizzare ma poiche' non lo trova produce un errore; inoltre il comando cat sa che il contenuto di tale file dovrebbe essere inviato all'interno del file dati e non a video ('1>' infatti redirige lo standard output nel file dati); infine cat sa che lo standard error deve essere rediretto nello standard output ('2>&1' infatti redirige lo standard error nello standard output). Il risultato e' che il messaggio di errore andrebbe scritto nello standard error ma, poiche' tale canale e' stato rediretto, viene scritto nello standard output. Poiche a sua volta lo standard output e' stato rediretto nel file dati, il messaggio di errore verra' scritto nel file dati. Seguendo lo stesso percorso logico, la stringa '1>&2' redirige lo standard output verso lo standard error. Riassumendo, '2>&1' invia lo standard error verso lo standard output e 1>&2 produce il risultato contrario.

11.12 Variabili d'ambiente

Una variabile e' una parte della memoria RAM identificata da un nome che puo' contenere dei valori. E' una scatola nera, un contenitore all'interno del quale possiamo collocare una data, un nome, una cifra e cosi' via. Quando viene definita una nuova variabile, il sistema associa al nome di tale variabile un indirizzo di memoria RAM a partire dal quale viene memorizzato un valore stabilito dall'utente. Per definire e valorizzare una variabile e' sufficiente assegnare un valore ad un nome, ad esempio, il comando:

PIPPO=253

definisce una variabile di nome PIPPO ed assegna a questa variabile il valore '253'. In altre parole il sistema alloca uno spazio di memoria RAM a partire da un indirizzo scelto da lui (ad esempio 12234) una zona all'interno della quale memorizza il valore 253. Cio' significa che la cella di memoria 12234 contiene 2, la cella contigua 12235 contiene 5 e la cella contigua 12236 contiene 3. Queste 3 celle contigue rappresentano una entita' di nome PIPPO. Oltre a contenere valori numerici, una variabile puo' contenere anche stringhe, cioe' insiemi di caratteri. Ad esempio il comando PIPPO=scatola, crea una variabile di nome PIPPO all'interno della quale viene memorizzato la stringa 'scatola'. Molte variabili contengono dei valori utili al sistema. Ad esempio la variabile USERNAME contiene il nome dell'utente che si e' collegato al sistema mentre la variabile HOME contiene il path della directory home dell'utente (ad esempio /home/mau). Una stringa puo' contenere anche il carattere spazio, ma in questo caso occorre racchiuderla tra doppi apici. Ad esempio il comando MIAVAR="ciao a tutti" crea una variabile di nome MIOVAR e ne memorizza all'interno la stringa "ciao a tutti". Il carattere '=' rappresenta l'operatore di assegnamento, cioe' l'operatore che assegna il valore alla variabile. Tra il nome della variabile, l'operatore '=' ed il valore vero e proprio non devono essere presenti spazi. Quindi il comando MIAVAR = 234 non e' corretto, perche' sono presenti degli spazi. Invece il comando MIAVAR=234 e' un comando corretto. Una variabile ha vita solo all'interno della shell nella quale viene creata, pertanto cambiando shell, il sistema non riconosce piu' tale variabile. Come sappiamo e' possibile creare delle subshell, cioe' delle shell all'interno di altre shell. Bene, se definiamo la variabile PIPPO all'interno di una data shell e successivamente apriamo una seconda shell, la variabile PIPPO rimarra' sconosciuta alla seconda shell. Possiamo verificarlo con questa sequenza di comandi:

PIPPO=maurizio (definisco una variabile di nome PIPPO che contiene la stringa 'maurizio') set (il comando set visualizza tutte le variabili definite: mostrera' anche la variabile PIPPO) bash (creo una subshell bash)

Page 16: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

set (visualizzo le variabili definite e, con sorpresa, non trovo piu' la variabile PIPPO) exit (o CTRL-D, con questo comando esco dalla subshell e torno alla shell principale) set (ripeto il comando set ed ora la variabile PIPPO e' nuovamente visibile)

Affinche' una variabile sia visibile all'interno di qualsiasi shell, occorre 'esportarla' mediante il comando export. Ad esempio:

PIPPO="ciao a tutti" export PIPPO (ora la variabile PIPPO e' visibile da qualsiasi shell)

Per visualizzare le variabili definite si puo' usare il comando set, mentre per visualizzare le variabili di ambiente occorre usare il comando env. Le variabili di ambiente sono tutte quelle variabili predefinite all'interno della shell che troviamo normalmente all'interno di un sistema Linux. Per visualizzare l'elenco di tali variabili usiamo il comando env:

env

Per visualizzare il contenuto di una variabile invece, occorre usare il comando echo insieme all'operatore '$'. L'operatore $ serve per far riferimento al contenuto di una variabile. Ad esempio il comando:

echo $PIPPO

visualizza a video il contenuto della variabile PIPPO.

11.13 Comandi eseguiti in background

I programmi che sono in esecuzione sotto Linux vengono definiti processi. Quando si esegue un comando come ad esempio ls, viene messo in esecuzione un processo. Ogni processo sotto Linux viene identificato da un numero univoco: il PID. PID sta per Process IDentifier cioe' identificatore del processo. A dire il vero i comandi eseguiti dagli utenti vengono definiti job. Si tratta sempre di processi, ma i processi utente vengono definiti job per distinguerli dai processi di sistema. Un comando, come qualsiasi processo puo' essere eseguito in background (lett. in sottofondo) nel senso che mentre il comando e' in esecuzione l'utente puo' eseguire altre attivita'. E' conveniente eseguire un comando in background quando la sua esecuzione richiede del tempo. Ad esempio la ricerca di un file all'interno di un grosso disco scorrendo tutte le directory richiede un po' di tempo: e' possibile eseguire tale ricerca in background e nel frattempo eseguire altre attivita'. Per eseguire un comando in background e' sufficiente apporre il carattere di e commerciale '&' alla fine del comando. Ad esempio per stampare un file molto grande si puo' lanciare il comando lpr in questo modo:

lpr dati & [1] 534

il carattere & permette di eseguire il comando lpr in background, permettendoci di eseguire altre attivita' evitando di dover aspettare che il comando termini la sua esecuzione. La riga presente sotto a quella che contiene il comando rappresenta il numero del job (1) ed il PID del processo (4). Cio' significa che il comando lpr appena lanciato e' identificato dal sistema come processo numero 534 e job numero 1. E' possibile eseguire in background piu' di un comando contemporaneamente. Per visualizzare i comandi in esecuzione in background (cioe' i job) si utilizza il comando jobs.

jobs [1] running lpr dati [2] - running cat *.txt > testo

Page 17: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

Il comando jobs visualizza i comandi in esecuzione in background e per ogni comando viene indicato il numero del job, lo stato del comando (running o stopped) ed il comando stesso. Un comando in stato running e' un comando in esecuzione mentre un comando in stato stopped e' un comando fermo. Il carattere indica il job corrente, cioe' quello attualmente in esecuzione, mentre il carattere - indica il prossimo job che verra' eseguito. Non bisogna confondere un job stopped da uno non ancora in esecuzione: il primo e' un job fermo in attesa di essere riattivato o ucciso, il secondo e' un job attivo ma non ancora in esecuzione. Questa distinzione e' importante perche' l'utente puo' spospendere un job e riattivarlo in qualsiasi momento. E' possibile eseguire piu' comandi in background digitandoli sulla stessa riga e terminandoli con il carattere &:

lpr dati & cat *.txt > testo & [1] 534 [2] 567

Linux avverte della conclusione di job solo quando si termina il comando corrente (ad esempio una sessione di editing in vi). Per essere avvertiti della conclusione di un job si puo' utilizzare il comando notify. Usando il comando notify, non appena un job termina Linux interrompe il comando corrente (indipendentemente dall'operazione che si sta svolgendo) avvertendo l'utente della conclusione del job. Il comando notify accetta come parametro il numero del job che desideriamo tenere sotto controllo. Nell'esempio precedente, digitando il comando notify %1 Linux avvisera' l'utente - interrompendo qualsiasi azione stia facendo - non appena il comando 'lpr dati' (il job numero 1) sara' terminato. E' possibile portare un comando da modalita' background a modalita' foreground (la normale modalita' di esecuzione dei comandi) utilizzando il comando fg. E' anche possibile l'operazione opposta, cioe' da foreground a background mediante il comando bg. E' possibile annullare l'esecuzione di un comando mediante il comando kill (lett. uccidi, uccidere). Nell'esempio precedente il comando kill %2 annullera' l'esecuzione del job numero 2 ossia del comando 'cat *.txt > testo'. E' possibile annullare un comando indicando il numero del job od il numero del processo cioe' il PID. E' possibile anche sospendere temporaneamente un comando senza ucciderlo definitivamente premendo la combinazione di tasti CTRL-Z. L'utente puo' riattivare il comando un secondo tempo in modalita' background o foreground mediante i comandi bg e fg. Tutto cio' puo' essere utile nei casi in cui l'esecuzione di un comando appena digitato richieda piu' tempo del previsto. In questo caso e' possibile mettere in background il comando, ma occorre prima di tutto sospenderne l'esecuzione premendo i tasti CTR-Z. Non e' possibile infatti mettere il comando in background direttamente senza averne prima sospeso l'esecuzione. Una volta sospesa l'esecuzione del comando, lo si puo' mettere in backgroun usando il comando bg:

lpr divinacommedia.txt (stampo tutta la divina commedia ;o) CTRL-Z (sospendo l'esecuzione del comando non appena mi accorgo che il comando richiede molto tempo) jobs [1] 345 stopped lpr divinacommedia.txt bg %1 (metto in background il comando di stampa)

11.14 Programmazione della shell

Nei sistemi Linux e' possibile programmare la shell, cioe' creare dei programmi per eseguire automaticamente delle operazioni che altrimenti si dovrebbero eseguire manualmente. Ad esempio e' possibile creare un programma per effettuare periodicamente il salvataggio dei dati su un supporto esterno (nastro, floppy, CD-Rom etc), oppure si potrebbe scrivere un programma che controlla lo spazio occupato sul disco e produce delle statistiche. Non ci sono limiti alle possibili applicazioni che possono essere gestite automaticamente da un programma: l'unico limite e' la fantasia del programmatore. Bene, tali programmi vengono definiti script della shell (per chi conosce il SO MSDOS: possono essere paragonati ai file .bat). Per scrivere uno script della shell si puo' utilizzare un qualsiasi editor di testo come VI, EMACS, JED e via dicendo. Una volta scritto lo script, per eseguirlo occorre renderlo eseguibile impostando i

Page 18: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

permessi di esecuzione (utilizzando il comando chmod) oppure usare il comando sh od il comando '.'. Entrambi questi comandi eseguono lo script che viene loro passato come argomento. Ad esempio, supponendo di aver scritto uno script chiamato pippo, impostandone i permessi di esecuzione con il comando chmod, tale file diventera' simile ad un comune comando Linux: digitandolo e premendo INVIO verra' eseguito:

chmod u x pippo pippo (e premere INVIO) oppure, senza impostare i permessi di esecuzione: sh pippo o anche: . pippo

Uno script della shell puo' contenere dei comuni comandi Linux, delle righe di commento ed una prima riga di commento un po' speciale. Quando un file di script viene eseguito la shell legge tale file riga per riga ed esegue tutte le istruzioni che incontra: si tratta percio' di un programma intrpretato. Tutte le righe che iniziano con il carattere cancelletto '#' vengono trattate come commento e sono percio' ignorate dalla shell. Le righe di commento sono particolarmente utili nella scrittura di script complessi per descrivere lo scopo del codice che si sta scrivendo. La primissima riga di commento pero' ha una funzione ben precisa: indicare il tipo di shell che potra' eseguire lo script ed eventualmente la locazione del disco o piu' esattamente il path per richiamare tale shell. Nel caso questa prima riga di commento venga omessa verra' chiamata la shell di default, cioe' la shell BASH. Infatti se il primo carattere e' uno spazio si suppone che tale script e' eseguibile dalla shell bash, se tale carattere e' un cancelletto si suppone che la shell da usare sia la TCSH. Inoltre e' possibile scrivere il carattere ! dopo il carattere # per specificare una shell diversa, ma in questo caso occorre anche indicare il path per reperire tale shell: #!/bin/sh. Quest'ultima notazione e' indispensabile se si utilizza una shell e si crea uno script per un'altra shell. Ad esempio se si utilizza la shell TCSH e si scrive uno script per la shell BASH e' indispensabile iniziare tale script con la stringa: #!/bin/sh, dove /bin/sh e' il path di default della shell BASH (ovviamente se in un particolare sistema la shell BASH si trova in una locazione diversa il relativo path da specificare dovra' essere diverso). Il comando echo visualizza in output una stringa. Vediamo un classico esempio di programma che utilizza il comando echo:

mioscript e' il nome dello script e contiene al suo interno le seguenti istruzioni: #!/bin/sh # questo e' un semplice script echo "salve gente!" mioscript (digitando mioscript e premendo INVIO lo eseguo) salve gente!

Uno script generalmente accetta dei dati in input e genera dei dati in output: il comando read permette di leggere l'input ed il comando echo permette di scrivere in output. Ad esempio:

lo script mioscript contiene: #!/bin/sh echo "digita qualcosa" read qualcosa echo "ok hai digitato: $qualcosa"

Page 19: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

mioscript (digitando mioscript e premendo INVIO lo eseguo) digita qualcosa adlkmasdl ok hai digitato: adlkmasdl

nello script appena visto l'istruzione read legge lo standard input e lo mette nella variabile $qualcosa, successivamente il comando echo visualizza il contenuto della variabile. Poiche' la stringa passata al comando echo e' inserita tra una coppia di doppi apici, eventuali caratteri speciali all'interno della variabile qualcosa non vengono valutati. Nel caso occorresse valutare eventuali caratteri speciali occorrera' eliminare i doppi apici. Ad esempio si potrebbe creare un comando che prendesse in considerazione anche eventuali caratteri speciali digitati dall'utente per effettuare alcune elaborazioni. Osserviamo il seguente script:

lo script elenco contiene: #!/bin/sh echo "quali file vuoi visualizzare?" read nomi ls $nomi elenco *.txt pippo.txt, pluto.txt , paperino.txt

l'utente digita il nome dello script (elenco) seguito dall'argomento *.txt. L'asterisco e' un carattere speciale che in questo caso viene preso in input dallo script e valutato in quanto la variabile $nomi non e' posta tra doppi apici. Percio' il comando ls accetta in input il contenuto della variabile passata allo script elenco e cioe' '*.txt'. Ad ogni modo uno script puo' leggere dei dati anche da se stesso:

lo script inviamail contiene: #!/bin/sh mail marco <<fine caro marco ricordati dell'appuntamento! ciao fine

la stringa <<fine significa: prendi in input tutto cio' che trovi da questo punto fino alla parola 'fine'. Se non si specifica un punto di terminazione occorrera' inserire il carattere di fine file CTRL-D. Ovviamente dopo i caratteri << e' possibile scrivere qualsiasi cosa come ad esempio <<stop, <<end e via dicendo.

Come qualsiasi comando Linux, anche gli script accettano degli argomenti in input. Per far riferimento ad un argomento e' possibile usare il simbolo $ seguito dal numero dell'argomento: $0 e' il nome dello script, $1 e' il primo argomento, $2 e' il secondo, $3 il terzo e cosi' via. $# rappresenta il numero degli argomenti passati in input e $* rappresenta tutti gli argomenti. Un esempio chiarira' il concetto:

mio script arg1 arg2 arg3 br> #!/bin/sh il numero degli argomenti passati e': $# il primo e': $1 il secondo e': $2

Page 20: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

il terzo e': $3 gli argomenti passati sono: $*

Veniamo ora alla prima istruzione utilizzabile all'interno di uno script: l'istruzione let. Let confronta due valori o permette di eseguire delle operazioni aritmetiche. Attenzione: let confronta due valori e non due espressioni. Esempi:

let 3*5 let 2 4 oppure mioscript contiene: ancora=1 while let "ancora <= 3" do echo $ancora ciao let "ancora = ancora 1" done eseguo mioscript (premendo INVIO): 1ciao 2ciao 3ciao

come si puo' vedere se gli argomenti del comando let contengono spazi, occorre quotarli usando i doppi apici. Il comando while appena visto e' una struttura di controllo che vedremo successivamente insieme alle altre strutture (if, for, for-in e case) in quanto occorre prima introdurre la struttura di controllo test. Il comando test permette di confrontare due valori. E' possibile confrontare due valori usando il comando test o inserendo l'espressione di confronto all'interno di una coppia di parentesi quadre. Per verificare l'uguaglianza tra due valori occorre usare il simbolo '=' oppure l'opzione -eq del comando test. Viceversa, per verificare la disuguaglianza tra due valori occore usare il simbolo "!=":

num=2 test $num -eq 3 echo $? ($? contiene il codice di uscita dell'ultimo comando eseguito) un altro esempio per verificare due valori che non contiene la parola test: [$num = 3]

la verifica dei valori usando il comando test o le parentesi quadre e' usata all'interno di strutture di controllo come while, if, for, for-in e case- La sintassi della struttura di controllo if e' la seguente:

if comando-linux then comandi... else comandi... fi

Ogni comando linux quando termina restituisce un codice di errore che puo' essere 0 se il comando non ha successo o 1 se il comando e' andato a buon fine. Tale codice di errore viene verificato dalla struttura di confronto if: se e' 1 vengono eseguiti i comandi successivi alla parola then, in caso contrario vengono eseguiti i comandi successivi alla parola else. Vediamo il funzionamento della struttura di controllo if con lo script 'argomenti' che accetta degli

Page 21: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

argomenti in input e verifica che il numero di questi sia corretto: se viene passato piu' di un argomento visualizza un messaggio di errore:

if [$# -ne 1] then echo "numero di argomenti errato" exit 1 else echo "numero di argomenti corretto fi eseguendo lo script si avra': argomenti (premendo INVIO): numero di argomenti errato argomenti a (INVIO) numero di argomenti corretto argomenti a b c d (INVIO) numero di argomenti errato

e' possibile 'nidificare' le if, ossia effettuare piu' controlli usando la sintassi 'elif' (elif sta per 'else if'). In altre parole se non si verifica la condizione testata vengono eseguiti i comandi successivi alla parola else ma a questo punto e' possibile eseguire un altro controllo utilizzando un altro comando if dopo la parola elif:

if [conddizione1] then comandi elif if [condizione2] then comandi elif if [condizione3] then comandi else comandi fi

come abbiamo visto il costrutto if permette di verificare l'esito di un comando Linux: e' possibile pero' verificare l'esito di due o piu' comandi Linux usando gli operandori logici && e ||. L'operatore logico && corrisponde all' and logico mentre l'operatore || corrisponde all' or logico. Cio' significa che se si vuole verificare che due o piu' comandi siano andati a buon fine contemporaneamente occorre usare l'operatore &&, mentre se si vuole verificare che almeno uno dei comandi sia andato a buon fine occorre usare l'operatore ||: comando1 && comando2 oppure comando1 || comando2. Nel primo caso viene eseguito tutto cio' che si trova dopo la parola then solo se entrambi i comandi hanno esito positivo mentre nel secondo caso e' sufficiente che almeno uno dei due comandi abbia esito positivo. Quando occorre verificare piu' condizioni, e' conveniente usare la struttura di controllo case. Vediamo il costrutto case con un esempio di script che visualizza un menu dove e' possibile scegliere tra le varie opzioni per visualizzare i file:

echo "scegli una opzione tra le seguenti: echo "d: dimensioni" echo "t: tutte le informazioni" echo "i: i-node" echo "c: solo i file con estensione .c"c echo "digita la tua scelta:"

Page 22: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

read scelta case $scelta in d) ls -s ;; t) ls -l ;; i) ls -i ;; c) ls *.c ;; *) echo "input non valido" esac

cosi' come per il costrutto if anche il costrutto case deve terminare con una parola particolare: esac. Come si puo' osservare fi e' if letto in senso contrario ed esac e' case letto in senso contrario. ;o) Vediamo infine i costrutti while, for e for-in. Il costrutto while permette di eseguire un ciclo di comandi mentre e' verificata una condizione. Quando la condizione non e' piu' verificata il ciclo si interrompe e lo script prosegue dall' istruzione successiva al blocco del while. La sintassi dell'istruzione while e' la seguente:

while comando-linux do comando1 comando2 comando3 comando4 ... done

Il costrutto for permette di costruire cicli di comandi. Verranno eseguiti tanti cicli quanti sono gli argomenti passati allo script. Ogni argomento viene spostato all'interno di una variabile che verra' verificata dal ciclo di for. Vediamo un esempio con lo script 'copiafile' che permette di salvare i file specificati all'interno di una data directory:

for miofile do cp $elencofile /home/mau/backup/$miofile echo "$miofile salvato!" done eseguendo lo script copiafile con i file pippo, pluto e topolino: copiafile pippo pluto topolino verranno salvati i tre file nella directory /home/mau/backup: pippo salvato! pluto salvato! topolino salvato!

il costrutto for-in e' molto simile al costrutto for con la sola differenza che l'elenco dei valori non viene preso dall'elenco degli argomenti passati allo script ma dall'elenco fornito direttamente all'interno del costrutto for-in:

Page 23: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

for variabile in pippo pluto topolino do echo $variabile done

Infine il comando exit permette di uscire immediatamente dallo script fornendo un codice di errore che puo' essere 0 o qualsiasi altro numero. Non appena viene incontrato il comando exit, lo script termina ed il controllo torna alla shell.

11.15 Le espressioni regolari

Una espressione regolare e' una stringa che contiene caratteri speciali. I caratteri speciali delle espressioni regolari pero' sono diversi dai caratteri speciali della shell: i primi consentono di effettuare ricerche all'interno di un testo mentre gli altri operano sui nomi di file. Le espressioni regolari possono essere utilizzate da molti filtri come ad esempio grep, sed e awk. I caratteri speciali sono l'accento circonflesso, il dollaro, l'asterisco, il punto e le parentesi quadre. Il segno ^ (accento circonflesso) ed il segno $ (dollaro) indicano rispettivamente l'inizio della riga e la fine della riga. Per ricercare tutte le righe all'interno di un testo che iniziano con la parola 'pippo' occorre definire una espressione regolare in questo modo: ^pippo. Al contrario per ricercare all'interno di un testo tutte le righe che terminano con al stringa 'pluto' occorre definire una espressione regolare in quest'altro modo: pluto$. Vediamo un esempio:

cat testo questo e' un testo banale che e' utile solo per mostrare l'uso delle espressioni regolari all'interno di un testo grep '^che' testo testo: che e' utile solo per mostrare grep 'lari$' testo testo: l'uso delle espressioni regolari

il primo comando grep ricerca le righe che iniziano per 'che' mentre il secondo ricerca le righe che terminano con 'lari. Per ricercare le righe che contengono unicamente una determinata stringa si possono combinare i caratteri ^ e $ insieme in questo modo: ^stringa$, dove stringa e' la parola da ricercare. Se infine si vogliono ricercare tutte le righe vuote si puo' costruire una espressione regolare che contenga unicamente i caratteri ^ e $. Esempio:

cat testo questo e' un testo banale che e' utile solo per mostrare l'uso delle espressioni regolari all'interno di un testo grep '^testo$' testo: questo e' un testo banale grep '^$' testo testo:

Le espressioni regolari possono essere usate anche combinando piu' comandi attraverso le pipe:

ls -l | grep '^d'

Page 24: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

i comandi precedenti elencano i file contenuti all'interno della directory corrente e di questi file estraggono solo quelli che iniziano per 'd', cioe' tutti i file che sono directory. Infatti il comando ls -l elenca tutti i file in modalita' estesa (-l sta per long). Ogni riga prodotta in output dal comando ls -l contiene un trattino '-' per i file comuni e una 'd' per le directory: tale output viene passato tramite pipe al comando grep che ricerca solo le righe che iniziano per 'd'. Il risultato finale e' l'elenco delle directory contenute all'interno della directory corrente. Un altro carattere speciale e' il punto: tale simbolo significa qualsiasi carattere. Ad esempio la stringa 'a.a' individua tutte le stringhe che iniziano per a, terminano per a e contengono un qualsiasi altro carattere. Le stringhe che soddisfano questa espressione regolare possono essere: ala, ara, a a, ava, aka e via dicendo. E' possibile usare piu' punti, ad esempio l'espressione regolare: bar.. trova tutte le stringhe che iniziano per bar e contengono 2 altri caratteri qualsiasi. Le stringhe che soddisfano tale espressione possono essere: barca, barza, barone, baronetto e via dicendo. Il carattere '*' posto all'interno di una espressione regolare trova sequenze di 0 o piu' caratteri uguali al carattere che lo precede. Ad esempio bo* permette di trovare stringhe come boomerang, bossolo, boa e cosi' via. L'ultima serie di caratteri speciali usabili nelle espressioni regolari sono le parentesi quadre. Le parentesi quadre permettono di specificare un particolare carattere o una particolare sequenza di caratteri. Vediamo un esempio:

gruppo[145]

l'espressione regolare appena vista individua tutte le stringhe che iniziano per gruppo e terminano con 1, 4 o 5. Le stringhe gruppo1, gruppo2 e gruppo4 soddisfano i criteri ma la stringa gruppo9 o la stringa gruppo6 non soddisfano i criteri e pertanto non verranno individuate. La stringa gruppo2 soddisfa i criteri ma la stringa Gruppo2 non li soddisfa. Infatti 'gruppo2' e 'Gruppo2' sono due stringhe diverse. E' possibile pero' individuare entrambe le stringhe in questo modo:

[gG]gruppo[2]

E' possibile effettuare delle ricerche procedendo in senso inverso, cioe' individuando non tanto le stringhe da trovare ma bensi' le stringhe che non devono essere trovate. Per effettuare cio' e' possibile usare il simbolo '^' per escludere alcuni caratteri dalla ricerca. L'accento circonflesso si deve trovare tra la prima parentesi quadra ed il primo carattere all'interno delle parentesi quadre:

gruppo[^3456789]

l'espressione regolare esposta individua le stringhe gruppo1 e gruppo2 ma non le stringhe gruppo3, gruppo4 o gruppo9, in quanto terminano con un carattere che non deve essere presente: tutte le stringhe che terminano con 3,4,5,6,7,8 o 9 non verranno individuate. Se viene posto un trattino all'interno delle parentesi quadre viene definita una sequenza di caratteri. Ad esempio l'espressione regolare [A-Z] individua tutti i caratteri dell'alfabeto maiuscoli mentre [a-z] individua quelli minuscoli. Allo stesso modo l'espressione regolare [0-9] individua tutti i numeri da 0 a 9. Per includere anche il trattino nella ricerca, occorre usare il carattere '\' come carattere di quotazione: [0-9\-]. Tale espressione individua le stringhe che terminano con un numero o con un trattino. E' possibile combinare tutti questi caratteri speciali insieme per costruire delle stringhe di ricerca particolarmente efficaci. Ad esempio la stringa [0-9][0-9]* significa qualsiasi stringa che inizia con un numero e che termina con uno o piu' numeri: in sostanza vengono individuate solo stringhe numeriche. Allo stesso modo la stringa [A-Z][A-Z]* individua qualsiasi stringa composta da caratteri maiuscoli. Esempio:

I PC IBM sono stati i primi personal computer della storia e sono nati nel 1981

all'interno della riga esposta, l'espressione regolare [A-Z][A-Z]* individua le stringhe PC ed IBM mentre l'espressione regolare [0-9][0-9]* individua la stringa 1981. Le espressioni regolari possono essere particolarmente efficaci:

Page 25: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

ls -l | grep '^......r.x'

i comandi esposti permettono di individuare tutti i file per i quali tutti gli altri utenti (proprietario e gruppo esclusi) posseggano i permessi di lettura ed esecuzione. Ecco un riepilogo dei caratteri speciali utilizzabili all'interno di espressioni regolari:

^ inizio riga $ fine riga . qualsiasi carattere * qualsiasi sequenza dei caratteri [] classi di caratteri

Infine esistono alcuni comandi che accettano anche altri caratteri come ad esempio il comando egrep. Il comando egrep infatti accetta anche i simboli: |, (), e ?. Il simbolo | rappresenta l' OR logico, cioe' permette di indicare delle stringhe alternative. Ad esempio 'frutta|verdura' individua sia la parola frutta che la parola verdura. Le parentesi permettono di concatenare piu' stringhe: (latte(intero|scremato). Il carattere ricerca una o piu' ripetizioni del carattere precedente, ad esempio o individua una o piu' o come ad esempio zoo, boomerang e via dicendo. Il carattere ? infine, individua 0 o 1 istanze del carattere precedente.

11.16 I filtri

I filtri sono comandi che leggono dei dati, li elaborano ed inviano in output il risultato. I dati possono provenire da file, dispositivi o possono essere l'output di altri comandi. L'output di un filtro e' generalmente l'output standard (il video) ma puo' essere rediretto verso un file, un dispositivo od un altro comando che puo' essere a sua volta un filtro. Un esempio di filtro e' il comando cat (da conCATenate). Il comando cat accetta in input dei dati e li visualizza a video. Se non viene specificato l'input, il comando cat accetta i dati dall'input standard (la tastiera). Percio' il comando cat > lettera si mette in attesa di dati dalla tastiera e al termine del flusso li scrive all'interno del file lettera:

cat > lettera caro amico ti scrivo cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' CTRL-D

Per terminare l'input da tastiera occorre premere la combinazione dei tasti CTRL e D che invia il carattere di fine file. I filtri head (testa) e tail (coda) servono per visualizzare le prime righe di un testo (head) o le ultime righe (tail). Ad esempio il comando head -2 visualizza le prime due righe di un testo:

cat lettera caro amico ti scrivo cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' head -2 lettera caro amico ti scrivo cosi' mi distraggo un po'

mentre il comando tail -2 lettera visualizza le ultime due:

cat lettera caro amico ti scrivo

Page 26: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' tail -2 lettera e siccome sei molto lontano piu' forte ti scrivero'

Altri esempi di filtri sono i comandi wc (Word Count) spell e sort. Il comando wc conta il numero di righe, parole e caratteri (inclusi i caratteri di fine riga) presenti all'interno di un file. Il comando spell legge un file e produce in output tutte le parole errate che trova (utilizzando il dizionario inglese). Il comando sort legge un file e lo riproduce in output in modo ordinato. Un altro esempio di filtri sono i comandi grep ed fgrep. Tali comandi ricercano una stringa all'interno di un file. Ad esempio, se all'interno di una directory con molti file esiste un file al cui interno e' presente la stringa 'pippo' ma non ricordiamo piu' il nome di tale file, e' possibile individurarlo utilizzando il comando grep. Il comando grep accetta due argomenti, il primo e' la stringa da ricercare, il secondo e' un elenco di file. Per specificare l'elenco di file e' anche possibile usare i caratteri speciali come '*'. Esempio:

cat lettera caro amico ti scrivo cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' cat documento egregio dottore a seguito della sua richiesta di informazioni pervenutaci in data 12-12-2001 relative al nostro prodotto grep forte lettera documento lettera:piu' forte ti scrivero'

se la stringa da ricercare contiene degli spazi occorre scriverla tra apici altrimenti il comando grep interpretera' lo spazio come un delimitatore considerando gli altri argomenti successivi al primo come nomi di file all'interno dei quali effettuare la ricerca. Esempio:

cat lettera caro amico ti scrivo cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' grep 'caro amico' lettera documento lettera: caro amico ti scrivo

qualora non fossero stati digitati gli apici il comando grep avrebbe ricercato la stringa 'caro' all'interno dei file: amico, lettera e documento. Occorre ricordarsi che Linux e' case sensitive, cioe' fa differenza tra caratteri maiuscoli e caratteri minuscoli:

cat lettera Caro amico ti scrivo cosi' mi distraggo un po' e siccome sei molto lontano piu' forte ti scrivero' grep 'caro amico' lettera documento

il comando grep in questo caso ricerca la stringa 'caro amico' all'interno dei file lettera e documento ma non produce alcun risultato in quanto 'Caro amico' e 'caro amico' sono due stringhe diverse. Per effettuare una ricerca senza badare ai caratteri maiuscoli o minuscoli

Page 27: La shell BASH - Dipartimento di Informaticareting/BASH.pdf · interpretati e inviati al kernel per essere eseguiti. La shell e' percio' una interfaccia per gli ... sto aspettando

occorre usare l'opzione -i: grep -i 'caro amico' lettera documento. Il comando grep puo' anche funzionare al contrario, e' possibile cioe' ricercare tutte le righe che non contengono una data stringa. A tale scopo occorre usare l'opzione -v. Il filtro fgrep e' piu' veloce di grep e puo' cercare contemporaneamente piu' stringhe nei file. Tale comando a differenza di grep non accetta pero' le espressioni regolari. Altri filtri utili sono i comandi diff e sed. Il comando diff permette di confrontare due file per visualizzare le righe differenti. Il comando diff produce in output le righe che risultano differenti, evidenziando quelle del primo file con il carattere '<' e quelle del secondo file con il carattere '>'. Il comando diff inoltre indica le operazioni che occorre effettuare per rendere i due file uguali. Sono possibili 3 operazioni: a, d o c. L'operazione a significa aggiungere, l'operazione d significa cancellare e l'operazione c significa sostituire. Un esempio chiarira' il funzionamento di diff:

cat frutta pere mele banane cat fruttaverdura pere mele carote pomodori cavoli diff frutta fruttaverdura 3c35 < banane > carote > pomodori > cavoli

cioe': sostituire la terza riga del primo file con le righe dalla terza alla quinta del secondo file. La riga del primo file da sostiture e' < banane le righe del secondo file sono > carote, > pomodori e > cavoli. Per ignorare le differenze tra caratteri maiuscoli e caratteri minuscoli e' possibile usare l'opzione -i. Ovviamente e' possibile reindirizzare l'output del comando diff all'interno di un file: diff frutta fruttaeverdura > differenze. Esistono molti altri filtri come join, cut, paste, egrep, comm, cmp, tee, sort, pr, sed...tra questi un filtro molto usato e' il comando sed. Sed sta per Stream EDitor, cioe' editor di flusso: si tratta in sostanza di un editor di riga. Il comando sed accetta in input un comando di editing ed un elenco di file e genera in output una copia modificata di tali file. Esempio:

cat frutta pere mele banane sed '3 d' frutta pere mele

nell'esempio appena visto il comando sed cancella la terza riga dal file frutta. Il comando sed puo' effettuare varie operazioni di editing (inserimento, cancellazione, sostituzione etc.): per conoscere l'elenco completo delle operazioni di editing effettuabili consultare la relativa pagina di man (man sed).

Inizio della guida Il filesystem Indice tipologie di comandi

Copyright (c) 2002-2003 Maurizio Silvestri