103.4 Lezione 2
Certificazione: |
LPIC-1 (101) |
---|---|
Versione: |
5.0 |
Argomento: |
103 I Comandi GNU e Unix |
Obiettivo: |
103.4 Utilizzare stream, pipe e reindirizzamenti |
Lezione: |
2 di 2 |
Introduzione
Un principio della filosofia Unix afferma che ogni programma dovrebbe avere uno scopo specifico e non dovrebbe cercare di incorporare funzionalità al di fuori del suo ambito. Ma mantenere le cose semplici non significa risultati meno elaborati, perché diversi programmi possono essere concatenati insieme per produrre un output combinato. Il carattere della barra verticale |
, noto anche come simbolo pipe, può essere utilizzato per creare una pipeline che collega l’output di un programma direttamente all’ingresso di un altro programma, mentre la command substitution consente di memorizzare l’output di un programma in una variabile oppure usarlo direttamente come argomento per un altro comando.
Le Pipe
A differenza dei reindirizzamenti, con le pipe i dati fluiscono da sinistra a destra nella riga di comando e l’obiettivo è un altro processo, non un percorso del file system, un descrittore di file o un Here document. Il carattere pipe |
dice alla shell di avviare tutti i comandi distintamente e di collegare l’output del comando precedente all’input del comando successivo, da sinistra a destra. Per esempio, invece di usare i redirect, il contenuto del file /proc/cpuinfo
inviato allo standard output da cat
può essere reindirizzato allo stdin di wc
con il seguente comando:
$ cat /proc/cpuinfo | wc 208 1184 6096
In assenza di un percorso a un file, wc
conta il numero di righe, parole e caratteri che riceve sul suo stdin, come nel caso dell’esempio. Molte pipe possono essere presenti in un comando composto. Nell’esempio seguente vengono utilizzate due pipe:
$ cat /proc/cpuinfo | grep 'model name' | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
Il contenuto del file /proc/cpuinfo
prodotto da cat /proc/cpuinfo
è stato reindirizzato al comando grep 'model name'
, che quindi seleziona solo le righe contenenti il termine model name
. Il sistema che esegue l’esempio ha molte CPU, quindi ci sono righe ripetute con model name
. L’ultima pipe collega grep 'model name'
a uniq
, che si preoccuperà di eliminare qualsiasi riga uguale alla precedente.
Le pipe possono essere combinate con i reindirizzamenti nella stessa riga di comando. L’esempio precedente può essere riscritto in una forma più semplice:
$ grep 'model name' </proc/cpuinfo | uniq model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
Il reindirizzamento dell’input per grep
non è strettamente necessario in quanto grep
accetta un percorso di file come argomento, ma l’esempio dimostra come costruire tali comandi combinati.
Le pipe e i reindirizzamenti sono esclusivi, ovvero una sorgente può essere mappata su una sola destinazione. Tuttavia, è possibile reindirizzare un output su un file e vederlo ancora sullo schermo con il programma tee
. Per farlo, il primo programma invia il suo output allo stdin di tee
e a quest’ultimo viene fornito un nome di file dove memorizzare i dati:
$ grep 'model name' </proc/cpuinfo | uniq | tee cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz $ cat cpu_model.txt model name : Intel(R) Xeon(R) CPU X5355 @ 2.66GHz
L’output dell’ultimo programma della catena, generato da uniq
, viene visualizzato e memorizzato nel file cpu_model.txt
. Per non sovrascrivere il contenuto del file fornito ma per aggiungere dati a esso, l’opzione -a
deve essere fornita a tee
.
Solo l’output standard di un processo viene catturato da una pipe. Supponiamo che sia necessario eseguire un lungo processo di compilazione sullo schermo e allo stesso tempo salvare sia lo standard output che lo standard error in un file per un’ispezione successiva. Supponendo che la tua directory corrente non abbia un Makefile, il seguente comando restituirà un errore:
$ make | tee log.txt make: *** No targets specified and no makefile found. Stop.
Sebbene mostrato sullo schermo, il messaggio di errore generato da make
non è stato catturato da tee
e il file log.txt è stato creato vuoto. È necessario eseguire un reindirizzamento prima che una pipe possa acquisire lo stderr:
$ make 2>&1 | tee log.txt make: *** No targets specified and no makefile found. Stop. $ cat log.txt make: *** No targets specified and no makefile found. Stop.
In questo esempio lo stderr di make
è stato reindirizzato allo stdout, quindi tee
è stato in grado di catturarlo con una pipe, visualizzarlo sullo schermo e salvarlo nel file log.txt
. In casi come questo, può essere utile salvare i messaggi di errore per un controllo successivo.
Sostituzione dei Comandi
Un altro metodo per catturare l’output di un comando è attraverso la command substitution. Inserendo un comando all’interno delle virgolette ``, Bash lo sostituisce con il suo output standard. L’esempio seguente mostra come utilizzare lo stdout di un programma come argomento per un altro programma:
$ mkdir `date +%Y-%m-%d` $ ls 2019-09-05
L’output del programma date
, la data corrente formattata come year-month-day, è stato usato come argomento per creare una directory con mkdir
. Un risultato identico si ottiene usando $()
invece delle virgolette
$ rmdir 2019-09-05 $ mkdir $(date +%Y-%m-%d) $ ls 2019-09-05
Lo stesso metodo può essere utilizzato per memorizzare l’output di un comando come variabile:
$ OS=`uname -o` $ echo $OS GNU/Linux
Il comando uname -o
restituisce il nome generico del sistema operativo corrente, che è stato memorizzato nella variabile di sessione OS
. Assegnare l’output di un comando a una variabile è molto utile negli script, poiché consente di memorizzare e valutare i dati in molti modi distinti.
A seconda dell’output generato dal comando sostituito, la sostituzione del comando integrato potrebbe non essere appropriata. Un metodo più sofisticato per usare l’output di un programma come argomento di un altro programma impiega un intermedio chiamato xargs
. Il programma xargs
usa il contenuto che riceve tramite stdin per eseguire un dato comando con il contenuto come argomento. Il seguente esempio mostra xargs
che esegue il programma identify
con gli argomenti forniti dal programma find
:
$ find /usr/share/icons -name 'debian*' | xargs identify -format "%f: %wx%h\n" debian-swirl.svg: 48x48 debian-swirl.png: 22x22 debian-swirl.png: 32x32 debian-swirl.png: 256x256 debian-swirl.png: 48x48 debian-swirl.png: 16x16 debian-swirl.png: 24x24 debian-swirl.svg: 48x48
Il programma identify
fa parte di ImageMagick, un insieme di strumenti a riga di comando per ispezionare, convertire e modificare la maggior parte dei tipi di file immagine. Nell’esempio, xargs
ha preso tutti i percorsi elencati da find
e li ha inseriti come argomenti di identify
, che poi mostra le informazioni per ogni file formattato come richiesto dall’opzione -format
. I file trovati da find
nell’esempio sono immagini contenenti il logo della distribuzione in un filesystem Debian. -format
è un parametro per identify
, non per xargs
.
L’opzione -n 1
richiede che xargs
esegua il comando dato con un solo argomento alla volta. Nel caso dell’esempio, invece di passare tutti i percorsi trovati da find
come un elenco di argomenti a identify
, usando xargs -n 1
si eseguirà il comando identify
per ogni percorso separatamente. Usando -n 2
si eseguirà identify
con due percorsi come argomenti, -n 3
con tre percorsi come argomenti e così via. Allo stesso modo, quando xargs
elabora contenuti multilinea — come nel caso dell’input fornito da find
— l’opzione -L
può essere usata per limitare il numero di righe che saranno usate come argomenti per l’esecuzione del comando.
Note
|
Usare |
Se i percorsi hanno caratteri di spazio, è importante eseguire find
con l’opzione -print0
. Questa opzione indica a find
di usare un carattere nullo tra ogni voce in modo che l’elenco possa essere correttamente analizzato da xargs
(l’output è stato soppresso):
$ find . -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 du | sort -n
L’opzione -0
dice a xargs
che il carattere nullo dovrebbe essere usato come separatore. In questo modo i percorsi dei file forniti da find
vengono analizzati correttamente anche se contengono caratteri vuoti o altri caratteri speciali. L’esempio precedente mostra come utilizzare il comando du
per scoprire l’utilizzo del disco di ogni file trovato e quindi ordinare i risultati per dimensione. L’output è stato soppresso per concisione. Nota che per ogni criterio di ricerca è necessario inserire l’opzione -print0
per find
.
Per impostazione predefinita, xargs
inserisce gli argomenti del comando eseguito per ultimo. Per cambiare quel comportamento, dovrebbe essere usata l’opzione -I
:
$ find . -mindepth 2 -name '*avi' -print0 -o -name '*mp4' -print0 -o -name '*mkv' -print0 | xargs -0 -I PATH mv PATH ./
Nell’ultimo esempio, ogni file trovato da find
viene spostato nella directory corrente. Poiché il percorso di origine deve essere dato a mv
prima del percorso di destinazione, viene fornito un termine di sostituzione all’opzione -I
di xargs
che viene poi opportunamente posizionato accanto a mv
. Utilizzando il carattere null come separatore, non è necessario racchiudere il termine di sostituzione tra virgolette.
Esercizi Guidati
-
È conveniente salvare la data di esecuzione delle azioni eseguite dagli script automatici. Il comando
date +% Y-% m-% d
mostra la data corrente nel formato year-month-day. Come può l’output di un tale comando essere memorizzato in una variabile di shell chiamataTODAY
usando la sostituzione del comando? -
Usando il comando
echo
, come può il contenuto della variabileTODAY
essere inviato allo standard input del comandosed s /-/./ g
? -
In che modo l’output del comando
date +%Y-%m-%d
può essere usato come Here string da fornire ased s /-/./ g
? -
Il comando
convert image.jpeg -resize 25% small/image.jpeg
crea una versione più piccola diimage.jpeg
e colloca l’immagine risultante in un file con lo stesso nome all’interno della sottodirectorysmall
. Usandoxargs
, come è possibile eseguire lo stesso comando per ogni immagine elencata nel filefilelist.txt
?
Esercizi Esplorativi
-
Una semplice routine di backup crea periodicamente un’immagine della partizione
/dev/sda1
condd < /dev/sda1> sda1.img
. Per eseguire futuri controlli di integrità dei dati, la routine genera anche un hash SHA1 del file consha1sum < sda1.img > sda1.sha1
. Aggiungendo pipe e il comandotee
, come sarebbero combinati questi due comandi in uno? -
Il comando
tar
viene utilizzato per archiviare molti file in un unico file, preservando la struttura delle directory. L’opzione-T
permette di specificare un file contenente i percorsi da archiviare. Per esempio,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crea un file tar compressoetc.tar.xz
dalla lista fornita dal comandofind
(l’opzione-T -
indica lo standard input come elenco dei percorsi). Al fine di evitare possibili errori di analisi dovuti a percorsi contenenti spazi, quali opzioni di comando dovrebbero essere presenti perfind
etar
? -
Invece di aprire una nuova sessione di shell remota, il comando
ssh
può semplicemente eseguire un comando indicato come argomento:ssh user@storage "remote command"
. Dato chessh
permette anche di reindirizzare l’output standard di un programma locale allo standard input del programma remoto, come potrebbe il comandocat
reindirizzare un file locale chiamatoetc.tar.gz
a/srv/backup/etc.tar.gz
inuser@storage
tramitessh
?
Sommario
Questa lezione copre le tradizionali tecniche di comunicazione tra processi impiegate da Linux. La command pipelining crea un canale di comunicazione unidirezionale tra due processi e la command substitution consente di memorizzare l’output di un processo in una variabile di shell. La lezione segue i seguenti passaggi:
-
Come la pipe può essere utilizzata per trasmettere l’output di un processo all’input di un altro processo.
-
Lo scopo dei comandi
tee
exargs
. -
Come catturare l’output di un processo con la command substitution, memorizzandolo in una variabile o usandolo direttamente come parametro per un altro comando.
I comandi e le procedure trattate erano:
-
Comand pipelining con
|
. -
Sostituzione dei comandi con backtick e
$()
. -
Comandi
tee
,xargs
efind
.
Risposte agli Esercizi Guidati
-
È conveniente salvare la data di esecuzione delle azioni eseguite dagli script automatici. Il comando
date +% Y-% m-% d
mostra la data corrente nel formato year-month-day. Come può l’output di un tale comando essere memorizzato in una variabile di shell chiamataTODAY
usando la sostituzione del comando?$ TODAY=`date +%Y-%m-%d`
o
$ TODAY=$(date +%Y-%m-%d)
-
Usando il comando
echo
, come può il contenuto della variabileTODAY
essere inviato allo standard input del comandosed s /-/./ g
?$ echo $TODAY | sed s/-/./g
-
In che modo l’output del comando
date +%Y-%m-%d
può essere usato come Here string da fornire ased s /-/./ g
?$ sed s/-/./g <<< `date +%Y-%m-%d`
o
$ sed s/-/./g <<< $(date +%Y-%m-%d)
-
Il comando
convert image.jpeg -resize 25% small/image.jpeg
crea una versione più piccola diimage.jpeg
e colloca l’immagine risultante in un file con lo stesso nome all’interno della sottodirectorysmall
. Usandoxargs
, come è possibile eseguire lo stesso comando per ogni immagine elencata nel filefilelist.txt
?$ xargs -I IMG convert IMG -resize 25% small/IMG < filelist.txt
o
$ cat filelist.txt | xargs -I IMG convert IMG -resize 25% small/IMG
Risposte agli Esercizi Esplorativi
-
Una semplice routine di backup crea periodicamente un’immagine della partizione
/dev/sda1
condd < /dev/sda1> sda1.img
. Per eseguire futuri controlli di integrità dei dati, la routine genera anche un hash SHA1 del file consha1sum < sda1.img > sda1.sha1
. Aggiungendo pipe e il comandotee
, come sarebbero combinati questi due comandi in uno?# dd < /dev/sda1 | tee sda1.img | sha1sum > sda1.sha1
-
Il comando
tar
viene utilizzato per archiviare molti file in un unico file, preservando la struttura delle directory. L’opzione-T
permette di specificare un file contenente i percorsi da archiviare. Per esempio,find /etc -type f | tar -cJ -f /srv/backup/etc.tar.xz -T -
crea un file tar compressoetc.tar.xz
dalla lista fornita dal comandofind
(l’opzione-T -
indica lo standard input come elenco dei percorsi). Al fine di evitare possibili errori di analisi dovuti a percorsi contenenti spazi, quali opzioni di comando dovrebbero essere presenti perfind
etar
?Opzioni
-print0
e--null
:$ find /etc -type f -print0 | tar -cJ -f /srv/backup/etc.tar.xz --null -T -
-
Invece di aprire una nuova sessione di shell remota, il comando
ssh
può semplicemente eseguire un comando indicato come argomento:ssh user@storage "remote command"
. Dato chessh
permette anche di reindirizzare l’output standard di un programma locale allo standard input del programma remoto, come potrebbe il comandocat
reindirizzare un file locale chiamatoetc.tar.gz
a/srv/backup/etc.tar.gz
inuser@storage
tramitessh
?$ cat etc.tar.gz | ssh user@storage "cat > /srv/backup/etc.tar.gz"
or
$ ssh user@storage "cat > /srv/backup/etc.tar.gz" < etc.tar.gz