Basi di Linux – Standard input, standard output, standard error e redirezione

September 21, 2007

Standard input, standard output, standard error e redirezione

Tutto in linux viene visto come file. Anche i dispositivi di output
come video e stampanti, i dispositivi di input, come mouse e
tastiere; i dischi rigidi, i floppy disk, i cd-rom, i dvd, etc.
Difatti se facciamo un giro per la directory “dev”:

[roberto@diamondhead /dev]$ ls /dev/

vedremo una serie interminabile di file che identificano un dispositivo. Non tutti i file che vedete qui visualizzati sono associati ad un dispositivo fisico, ma solo una minima parte. Se vogliamo renderci conto che effettivamente linux vede anche il video come un file, possiamo fare in questo modo: bisogna
prendere i privilegi di superutente:

[roberto@diamondhead roberto]$ su -

“su” sta per super user e il “-” ve lo spiegherò dopo. Attenzione, adesso tutto ciò che si ha può essere potenzialmente pericoloso. Fatto questo si cerca di identificare qual’è il nostro terminale con il comando “w”:

[root@diamondhead /root]# w
10:59am up 2:27, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
roberto pts/0 geelong 9:46am 0.00s 0.14s 0.01s w

Con il comando “w” posso sapere quali utenti attualmente sono presenti nel sistema e altre informazioni come il terminale attualmente in uso per quell’utente, cioè “pts/0″. Poi si redirige lo standard input del mouse, nello standard output del terminale:

[root@diamondhead /root]# cat /dev/mouse > /dev/pts/0

cliccando i pulsanti del mouse, o muovendolo, vedremo a video una sequenza di caratteri illeggibili (molti di questi non sono visualizzati perchè sono caratteri speciali) e servono per l’interazione tra il dispositivo ed il sistema. Si possono fare altre prove, come quella di vedere i dati che passano attraverso un modem (che in norma viene identificato con /dev/ttyS0 se viene collegato alla seriale 1 oppure /dev/ttyS1 per la seriale 2 – l’equivalente di COM1 e COM2 di windows), oppure trasferire informazioni direttamente al modem
da linea di comando.

Quando si esegue un comando Linux, l’output prodotto viene
inserito nel canale standard output. Se scriviamo:

[root@diamondhead /root]# echo “ciao”

questa è la maniera implicita per dire al sistema di scrivere “ciao” nello standard output, cioè il video; sarebbe esattamente la stessa cosa se in modo esplicito scrivessimo:

[root@diamondhead /root]# echo “ciao” > /dev/pts/0

Se vogliamo redirezionare l’output in un file possiamo scrivere:

[root@diamondhead /root]# echo “ciao” > testo.txt
[root@diamondhead /root]# more testo.txt
ciao

Praticamente l’operazione di redirezione, in questo caso, crea il file di destinazione. Se il file esiste già verrà cancellato e solo successivamente verranno passati i dati al file (se questi esistono).

Un secondo operatore di redirezione “>>” ci consente di accodare i dati in un file:

[root@diamondhead /root]# echo “a te” >> testo.txt
[root@diamondhead /root]# more testo.txt

Quando si esegue un comando può verificarsi un errore. Per esempio, se digitiamo il comando:

[root@diamondhead /root]# cat pippo.c

e il file pippo.c non esiste, come in questo caso, avremo un messaggio d’errore:

cat: pippo.c: File o directory inesistente

questo messaggio è stato generato dallo standard error rediretto allo standard output, cioè il video. Se vogliamo catturare questo messaggio d’errore e redirigerlo su di un file possiamo digitare:

[root@diamondhead /root]# cat pippo.c 2> error.txt
[root@diamondhead /root]# more error.txt
cat: pippo.c: File o directory inesistente

in questo esempio solo lo standard error (identificato con il numero “2″) viene rediretto nel file error.txt, eventuali altri messaggi diretti sullo standard output vengono regolarmente visualizzati a video.

Definizione1: A tutti i canali standard è associato un numero: i numeri 0, 1 e 2 fanno riferimento, rispettivamente, allo standard input, allo standard output e allo standard error. Anche in questo caso utilizzando l’operatore “>>” possiamo accodare i messaggi d’errore in un file:

[root@diamondhead /root]# cat pippo.o 2>> error.txt
[root@diamondhead /root]# more error.txt
cat: pippo.c: File o directory inesistente
cat: pippo.o: File o directory inesistente

Ora digitiamo:

[root@diamondhead /root]# vi lettera

e scriviamo nell’editor:

ciao come stai?

Possiamo redirigere lo standard input in modo da ricevere i dati dal file lettera invece che dalla tastiera:

[root@diamondhead /root]# cat < lettera
ciao come stai?

in questo modo viene copiato il contenuto del file lettera nello standard input di cat. Quindi il comando cat legge lo standard input e visualizza il contenuto del file lettera.

In alcuni casi può essere necessario passare i dati da un comando all’altro. In altre parole, si deve inviare lo standard output di un comando a un altro comando e non a un file di destinazione. Per esempio vogliamo visualizzare la lista del processi attivi con il comando ps:

[root@diamondhead /root]# ps -ef

vedremo una lunga lista di processi. Questi in parole povere sono i processi che attualmente stanno girando silenziosamente nel nostro sistema. Se vogliamo vedere se per esempio sendmail è in esecuzione (sendmail è il server di posta) dovremo sfogliare ogni riga della lista alla ricerca di questa voce. Per evitare ciò potremo filtrare il comando ps con un grep (che ci consente di cercare una
stringa):

[root@diamondhead /root]# ps -ef | grep “sendmail” root 891 1 0 08:32 ? 00:00:00 sendmail: accepting connections

in questo modo abbiamo utilizzato come input del comando grep l’output generato dal comando ps. Altro esempio:

[root@diamondhead /root]# ls | lpr

invia la lista dei file della directory corrente alla stampante.

Una delle principali comodità della shell bash è il completamento automatico dei comandi e dei nomi di file.
Per esempio, proviamo a digitare per due volte il tasto Tab da terminale:

[root@geelong local]#
Display all 3049 possibilities? (y or n)

il sistema ci chiederà se vogliamo visualizzare tutti i 3049 comandi disponibili. Ma attenzione, non sono gli unici, quelli visualizzati sono quelli che effettivamente abbiamo nel nostro path, ma ce ne sono altri.

Il comando history

Definizione2: Nella shell BASH il comando history mantiene una registrazione degli ultimi comandi eseguiti, che in questo caso vengono considerati eventi. I comandi vengono numerati a partire da 1 e viene memorizzato un determinato numero di comandi (normalmente 500 o 1000).

Digitando history si hanno gli ultimi comandi impartiti preceduti da un numero.
Per scorrere i comandi in sequenza si utilizzano i tasti cursore su e giù.
Come per il completamento automatico, già visto, dei comandi e dei nomi di file con la doppia pressione del tasto TAB, con la pressione della combinazione di ESC e TAB è possibile completare gli eventi memorizzati nell’history.

Esempio:

[roberto@geelong roberto]$ m [ESC][TAB][ESC][TAB]

mio_archivio-2002050611* miodoc
mio_archivio-20020506120* mkdir
mio_archivio-200205061204 more

Un’altro modo per far riferimento ad un evento dell’history e quello di far precedere il numero di riferimento dell’evento con il punto esclamativo in questo modo:

[roberto@geelong roberto]$ !1000
su -
Password:

oppure quello di digitare il punto esclamativo seguito da uno spazio e da una o più lettere di riferimento all’evento:

[roberto@geelong roberto]$ ! e[ESC][TAB][ESC][TAB]
echo eject exit

Per vedere com’è configurato il comando history sul nostro sistema basa vedere a quale valore corrisponde la variabile HISTSIZE (che contiene un numero riferito alla quantità di comandi memorizzabili nell’history) e HISTFILE, cioè a quale NOME del file history fa riferimento. Se digitiamo:

[roberto@geelong roberto]$ echo $HISTFILE
/home/roberto/.bash_history

vedremo il path ed il file di riferimento. Con:

[roberto@geelong roberto]$ echo $HISTSIZE
1000

Il comando alias

Un alias consente di definire un nuovo nome per un comando.
Esempio:

alias pippo=’ls -la’

definisce l’alias pippo per il comando ls -la. Per vedere tutti gli alias
utilizzati dal proprio utente digitiamo:

[roberto@geelong roberto]$ alias
alias l.=’ls -d .* –color=tty’
alias ll=’ls -l –color=tty’
alias ls=’ls –color=tty’
alias pippo=’ls -la’
alias vi=’vim’
alias which=’alias | /usr/bin/which –tty-only –read-alias –show-dot –show
tilde’

E’ possibile utilizzare anche le espressioni regolari per definire un’alias. Per definire un’alias per elencare i file di codice sorgente e di codice oggetto, ovvero i file che terminano con .c o .o, si scrive:

[roberto@geelong roberto]$ alias lsc=’ls *.[co]‘

per cancellare un alias:

[roberto@geelong roberto]$ unalias pippo
[roberto@geelong roberto]$ alias

I flag noclobber, ignoreeof e noglob

Adesso parliamo di alcuni flag molto utili.
Definizione3: Il flag noclobber, se abilitato, attiva il controllo che impedisce che l’output rediretto possa inavvertitamente cancellare un file.
Per esempio, creiamo un file che contiene il testo “salve”:

[roberto@diamondhead roberto]$ echo “salve” > testo1.txt
[roberto@diamondhead roberto]$ more testo1.txt
salve

ora creiamo un’altro file con il testo “mondo!”:

[roberto@diamondhead roberto]$ echo “mondo” > testo2.txt
[roberto@diamondhead roberto]$ more testo2.txt
mondo

adesso il file testo1.txt contiene la stringa “salve”, ed il file testo2.txt contiene la stringa “mondo”. Come possiamo fare (con una operazione di redirezione) per fare in modo che il file testo1.txt prenda il contenuto del file testo2.txt, cioè la stringha “mondo”?:

[roberto@diamondhead roberto]$ cat testo2.txt > testo1.txt
[roberto@diamondhead roberto]$ more testo1.txt
mondo

in questo modo però abbiamo perso la stringa “salve”. Se il comando fosse stato lanciato accidentalmente, avremo perso tutto il contenuto di testo1.txt. Se vogliamo che questo NON accade possiamo abilitare il flag noclobber. Rimettiamo a posto il file testo1.txt:

[roberto@diamondhead roberto]$ echo “salve” > testo1.txt
[roberto@diamondhead roberto]$ more testo1.txt
salve

abilitiamo noclobber:

[roberto@geelong roberto]$ set -o noclobber

adesso proviamo a rifare l’operazione di prima:

[roberto@diamondhead roberto]$ cat testo2.txt > testo1.txt
bash: testo1.txt: cannot overwrite existing file

Il file non verrà sovrascritto. Quindi l’opzione -o abilita questo controllo. Per disabilitarlo si utilizza l’opzione +o:

[roberto@diamondhead roberto]$ set +o noclobber
[roberto@diamondhead roberto]$ cat testo2.txt > testo1.txt

Per ignorare il flag noclobber e fare in modo di redirigere forzatamente il contenuto di un file in un’altro possiamo utilizzare il punto esclamativo. Allora ricostruiamo il file:

[roberto@diamondhead roberto]$ echo “salve” > testo1.txt
[roberto@diamondhead roberto]$ more testo1.txt
salve
[roberto@diamondhead roberto]$ more testo2.txt
mondo

abilitiamo noclobber in questo modo:

[roberto@diamondhead roberto]$ set -o noclobber
[roberto@diamondhead roberto]$ cat testo2.txt > testo1.txt
bash: testo1.txt: cannot overwrite existing file

quindi adesso non possiamo fare la redirezione. Forziamo ora la redirezione:

[roberto@diamondhead roberto]$ cat testo2.txt >! testo1.txt (….non funziona)

Un’altro flag utile è ignoreeof, che consente di attivare una funzionalità che evita l’uscita della shell con la combinazione di caratteri CTRL-D:

[roberto@geelong roberto]$ set -o ignoreeof [INVIO]
[roberto@geelong roberto]$ [CTRL] + D
[roberto@geelong roberto]$ Use “exit” to leave the shell.

L’editor VI

L’editor VI è l’editor di default sia per sistemi Linux che Unix in genere. Se volete fare l’amministratore di sistema con molta probabilità vi capiterà prima o poi di collegarvi con un sistema remoto per le operazioni di amministrazione. Quasi sicuramente, se si tratta di sistemi Unix, l’unico editor disponibile è il VI. Da questo si capisce la necessità di impadronirsi almeno dei comandi
base.

Ora proviamo a creare un file di testo con il VI:

[roberto@diamondhead roberto]$ vi elenco.txt

ora ci troviamo in modalità comandi. Tutti i tasti, o le sequenze di tasti, che in questo momento verranno digitati, l’editor li tratterà come comandi. Per iniziare a scrivere qualcosa basta premere il tasto i di insert:

prova1 prova2 prova3
pluto pippo paperino
eccetera

ora premiamo il testo ESC e ci ritroviamo in modalità comandi. Possiamo fare anche delle ricerche o delle sostituzioni, per esempio possiamo sostituire tutte le lettere p del nostro file con la lettera X, nel modo seguente:

come prima cosa posizioniamoci all’inizio della riga del file, premiamo ESC per assicurarci di essere in modalità comandi e poi digitiamo:

:.,$s/p/X/g

aspettate di premere INVIO. Con questa riga impartiamo all’editor VI che si tratta di un comando composto (:), che vogliamo partire dalla linea corrente (.) e proseguire fino all’ultima linea del buffer ($), la virgola (,) serve per separare l’inizio e la fine dell’area da sostituire. La s sta per substitute, quindi è la parola chiave che mi permette di fare questo tipo di operazione, segue la lettera da cercare (p) e la lettera da sostituire (X) separate da uno slash. Per finire vogliamo dire all’editor di applicare le modifiche in maniera
globale (g). Una volta modificato il nostro file possiamo salvare il tutto
premendo ESC :wq!. La “w” sta per write, la “q” sta per quit, ed il punto esclamativo mi sembra che serva per non effettuare nessun altro controllo sul file, tipo se è già in uso e così via.

Leave a Reply