L’ultima volta, nella parte 3 di questa serie di scripting bash, abbiamo parlato dei parametri della riga di comando e degli switch. Il nostro argomento di oggi è l’input, l’output e tutto ciò che è collegato a questo.
Hai già familiarità con due metodi per lavorare con l’output degli script a riga di comando.script da linea di comando:
- Visualizza i dati in uscita sullo schermo.
- Ridirigere l’output su un file.
A volte qualcosa deve essere mostrato sullo schermo, e qualcosa deve essere scritto su un file, quindi è necessario capire come vengono gestiti input e output in Linux, il che significa che è necessario imparare come inviare i risultati degli script dove è necessario. Iniziamo parlando dei descrittori di file standard.
Tutto in Linux è costituito da file, inclusi input e output. Il sistema operativo identifica i file usando descrittori.
Ogni processo può avere fino a nove descrittori di file aperti. La shell bash riserva i primi tre descrittori con gli ID 0, 1 e 2. Ecco cosa significano.
-
0
STDIN
– standard input stream. -
1
STDOUT
– standard output stream. -
2
STDERR
– flusso di errore standard. Possono essere paragonati alle fondamenta su cui è costruita l’interazione degli script con il mondo esterno. Consideriamo i dettagli su di loro.
STDIN
STDIN
– questo è lo standard input della shell. Per un terminale, l’input standard è la tastiera. Quando gli script utilizzano il carattere di reindirizzamento dell’input –<
, Linux sostituisce il descrittore del file di input standard con quello specificato nel comando. Il sistema legge il file ed elabora i dati come se fossero inseriti dalla tastiera.Molti comandi bash prendono l’input da
STDIN
a meno che non sia specificato un file sulla linea di comando da cui recuperare i dati. Per esempio, questo è vero per una squadracat
.Quando si inserisce un comando
cat
sulla linea di comando senza specificare i parametri, accetta l’input daSTDIN
. Dopo aver inserito la linea successiva, essacat
la stampa semplicemente sullo schermo.STDOUT
STDOUT
– lo standard output della shell. Per impostazione predefinita, questo è lo schermo. La maggior parte dei comandi bash emette datiSTDOUT
alla console, che li fa apparire nella console. I dati possono essere reindirizzati ad un file allegandoli al suo contenuto utilizzando il comando>>
.Abbiamo quindi un certo file di dati, al quale possiamo aggiungere altri dati usando questo comando:
pwd >> myfile
Quello che esce
pwd
verrà aggiunto al filemyfile
, mentre i dati già presenti non andranno da nessuna parte.raevskym@DESKTOP-JNF3L6H:~/bash_course$ pwd >> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
this is old file content
/home/raevskym/DesktopFin qui tutto bene, ma cosa succede se si prova a fare qualcosa come quella mostrata qui sotto, accedendo ad un file inesistente
xfile
, pensando tutto questo in modo chemyfile
il messaggio di errore entri nel file.ls –l xfile > myfile
Dopo aver eseguito questo comando, vedremo dei messaggi di errore sullo schermo.
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile > myfile
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directoryUn tentativo di accedere ad un file inesistente genera un errore, ma la shell non ha reindirizzato i messaggi di errore al file, visualizzandoli. Ma noi volevamo che i messaggi di errore andassero al file. Cosa fare? La risposta è semplice – usare il terzo descrittore standard.
STDERR
STDERR
è il flusso di errore standard della shell. Per default, questo handle punta alla stessa cosa che punta aSTDOUT
, che è il motivo per cui quando si verifica un errore, vediamo un messaggio sullo schermo.Così, supponiamo di voler reindirizzare i messaggi di errore, ad esempio, ad un file di log, o da qualche altra parte, invece di stamparli sullo schermo.
Ridurre il flusso degli errori
Come già sapete, un descrittore di file per
STDERR
è 2. Possiamo reindirizzare gli errori mettendo questo descrittore prima del comando di reindirizzamento:ls -l xfile 2>myfile
cat ./myfileIl messaggio di errore andrà ora al file
myfile
.raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l xfile 2> myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
ls: cannot access 'xfile': No such flie or directoryRedirezione dei flussi di errore e di output
Quando si scrivono script a riga di comando, può verificarsi una situazione in cui è necessario organizzare sia la redirezione dei messaggi di errore che quella dell’output standard. Per ottenere ciò, è necessario utilizzare i comandi di reindirizzamento per i descrittori corrispondenti, indicando i file dove gli errori e l’output standard dovrebbero andare:
ls –l myfile xfile anotherfile 2> errorcontent 1> correctcontent
Vediamo più da vicino:
raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile 2> errorcontent 1> correctcontent
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./correctcontent
myfile
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./errorcontent
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directoryLa shell reindirizzerà ciò che il comando
ls
normalmente invia aSTDOUT
un filecorrectcontent
dopo la sua costruzione1>
. I messaggi di errore che sarebbero finitiSTDERR
nel file sonoerrorcontent
dovuti al comando di reindirizzamento2>
.Se necessario, e
STDERR
, eSTDOUT
possono essere reindirizzati allo stesso file utilizzando il comando&>
:raevskym@DESKTOP-JNF3L6H:~/bash_course$ ls -l myfile xfile anotherfile &> content
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
ls: cannot access '-l': No sudh file or directory
ls: cannot access 'xfile': No such file or directory
ls: cannot access 'anotherfile': No such file or directory
myfileDopo l’esecuzione del comando, ciò che è destinato a
STDERR
eSTDOUT
è nel filecontent
.Ridirigere l’output negli script
Ci sono due metodi per reindirizzare l’output negli script a riga di comando:
- Ridirigere temporaneamente, o reindirizzare l’output di una linea.
- Ridirigere in modo permanente, o reindirizzare tutto o parte dell’output nello script.
Ridirigere temporaneamente l’output
In uno script, è possibile reindirizzare l’output di una singola linea a
STDERR
. Per fare questo, è sufficiente utilizzare il comando di reindirizzamento, specificando un descrittoreSTDERR
, e davanti al numero del descrittore, è necessario mettere una virgola (&
) simbolo:#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"Se si esegue lo script, entrambe le linee andranno sullo schermo, poiché, come già sapete, per default, gli errori vengono visualizzati nello stesso posto dei dati normali.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is an error
This is normal outputEseguiamo lo script in modo che l’output
STDERR
vada in un file../myscript 2> myfile
Come potete vedere, ora il solito output viene fatto sulla console, e i messaggi di errore vengono inviati al file.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript 2> content
This is normal output
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./content
This is an errorReindirizzamento permanente dell’output
Se uno script ha bisogno di reindirizzare molti dati visualizzati, è
echo
inconveniente aggiungere il comando appropriato ad ogni chiamata. Invece, è possibile impostare l’output per essere reindirizzato ad un descrittore specifico per la durata dello script utilizzando il comandoexec
:#!/bin/bash
exec 1>outfile
echo "This is a test of redirecting all output"
echo "from a shell script to another file."
echo "without having to redirect every line"Eseguiamo lo script:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat outfile
This is a test of redirecting all output
from a shell script to another file.
without having to redirect every lineSe si guarda il file specificato nel comando di reindirizzamento dell’output, risulta che tutto ciò che l’output del comando
echo
è andato in quel file.Il comando
exec
può essere usato non solo all’inizio dello script ma anche in altri posti:#!/bin/bash
exec 2>myerror
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>myfile
echo "This should go to the myfile file"
echo "and this should go to the myerror file" >&2Questo è quello che succede dopo aver eseguito lo script e aver visualizzato i file a cui abbiamo reindirizzato l’output.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This is the start of the script
now redirecting all output to another location
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myerror
and this should go to the myerror file
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
This should go to the myfile filePrima, il comando
exec
imposta il reindirizzamento dell’output daSTDERR
a un filemyerror
. Poi l’output di diversi comandi èecho
inviato aSTDOUT
e visualizzato sullo schermo. Dopo di che, il comandoexec
imposta l’invio di tutto ciò che va aSTDOUT
un filemyfile
, e infine, usiamo il comando di reindirizzamentoSTDERR
nel comandoecho
, che porta a scrivere la linea corrispondente al file.myerror.
Avendo imparato questo, potete reindirizzare l’output dove volete. Ora parliamo del reindirizzamento dell’input.
Ridirigere l’input negli script
Per reindirizzare l’input, potete usare la stessa tecnica che abbiamo usato per reindirizzare l’output. Per esempio, il comando
exec
permette di fare una fonte di dati perSTDIN
un file:exec 0< myfile
Questo comando dice alla shell di reindirizzare il suo input da un file
myfile
, non da uno normaleSTDIN
. Vediamo il reindirizzamento dell’input in azione:#!/bin/bash
exec 0< testfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
doneQuesto è ciò che apparirà sullo schermo dopo l’esecuzione dello script.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: this is the first line
Line #2: this is the second line
Line #3: this is the third lineIn uno degli articoli precedenti, avete imparato come usare un comando
read
per leggere l’input dell’utente dalla tastiera. Se si reindirizza l’input rendendo l’origine dei dati un file, allora il comandoread
, quando cerca di leggere i dati daSTDIN
, li leggerà dal file, e non dalla tastiera.Alcuni amministratori Linux usano questo approccio per leggere e poi elaborare i file di log.
Creare il proprio reindirizzamento dell’output
Quando si reindirizzano input e output negli script, non si è limitati ai tre descrittori di file standard. Come detto, puoi avere fino a nove descrittori aperti. Gli altri sei, numerati da 3 a 8, possono essere usati per reindirizzare l’input o l’output. Ognuno di essi può essere assegnato a un file e usato nel codice dello script.
È possibile assegnare un descrittore per l’output dei dati usando il comando
exec
:#!/bin/bash
exec 3>myfile
echo "This should display on the screen"
echo "and this should be stored in the file" >&3
echo "And this should be back on the screen"Dopo aver eseguito lo script, parte dell’output andrà sullo schermo, e parte andrà al file con il descrittore
3
.raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
This should display on the screen
And this should be back on the screen
raevskym@DESKTOP-JNF3L6H:~/bash_course$ cat ./myfile
and this should be stored in the fileCreare descrittori di file per l’input di dati
È possibile reindirizzare l’input in uno script allo stesso modo dell’output. Memorizza
STDIN
in un altro descrittore prima di reindirizzare l’input.Dopo aver finito di leggere il file, potete ripristinare
STDIN
e usarlo come al solito:#!/bin/bash
exec 6<&0
exec 0< myfile
count=1
while read line
do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
exec 0<&6
read -p "Are you done now? " answer
case $answer in
y) echo "Goodbye";;
n) echo "Sorry, this is the end.";;
esacTestiamo lo script:
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? y
Goodbye
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
Line #1: First line
Line #2: Second line
Line #3: Third line
Are you done now? n
Sorry, this is the end.In questo esempio, il descrittore di file 6 è stato utilizzato per memorizzare un riferimento a
STDIN
. Poi è stato fatto il reindirizzamento dell’input, la fonte di dati per ilSTDIN
file è diventata. Dopo di che, l’input per il comandoread
veniva dalSTDIN
reindirizzato, cioè dal file.Dopo aver letto il file, torniamo
STDIN
al suo stato originale reindirizzandolo a un descrittore6
. Ora, per controllare che tutto funzioni correttamente, lo script pone una domanda all’utente, attende l’input dalla tastiera ed elabora ciò che viene inserito.Chiude i descrittori di file
La shell chiude automaticamente i descrittori di file dopo che lo script termina. Tuttavia, in alcuni casi, è necessario chiudere manualmente i descrittori prima che lo script finisca di lavorare. Per chiudere l’handle, deve essere reindirizzato a
&-
. Si presenta così:#!/bin/bash
exec 3> myfile
echo "This is a test line of data" >&3
exec 3>&-
echo "This won't work" >&3Dopo aver eseguito lo script, riceveremo un messaggio di errore.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
./myscript: line 5: 3: Bad file descriptorIl punto è che abbiamo cercato di accedere ad un descrittore di file inesistente.
Fate attenzione quando chiudete i descrittori di file negli script. Se inviate dati ad un file, poi chiudete il descrittore e poi lo riaprite, la shell sostituirà il file esistente con quello nuovo. Cioè, tutto ciò che è stato scritto su questo file in precedenza sarà perso.
Ricuperare informazioni sugli handle aperti
Per ottenere un elenco di tutti i descrittori aperti in Linux, è possibile utilizzare il comando
lsof
. Molte distribuzioni, come Fedora, hanno l’utilitàlsof
in/usr/sbin
. Questo comando è molto utile in quanto visualizza informazioni su ogni handle aperto sul sistema. Questo include ciò che è aperto dai processi in esecuzione in background e ciò che è aperto dagli utenti che sono loggati.Questo comando ha molti tasti, vediamo i più importanti.
-
-p
Permette di specificare unID
processo. -
-d
Permette di specificare il numero di un descrittore sul quale si vogliono ottenere informazioni.
Per conoscere il
PID
del processo corrente, è possibile utilizzare una speciale variabile d’ambiente$$
nella quale la shell scrive quello correntePID
.La chiave è
-a
utilizzata per eseguire un’operazione booleanaAND
sui risultati restituiti utilizzando le altre due chiavi:raevskym@DESKTOP-JNF3L6H:~/bash_course$ lsof -a -p $$ -d 0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1Il tipo di file associato a
STDIN
STDOUT
eSTDERR —
CHR (modalità carattere). Poiché tutti puntano a un terminale, il nome del file corrisponde al nome del dispositivo assegnato al terminale. Tutti e tre i file standard sono disponibili per la lettura e la scrittura.Guardiamo come chiamare un comando
lsof
da uno script in cui, oltre allo standard, sono aperti altri descrittori:#!/bin/bash
exec 3> myfile1
exec 6> myfile2
exec 7< myfile3
lsof -a -p $$ -d 0,1,2,3,6,7Questo è ciò che accade se si esegue questo script.
raevskym@DESKTOP-JNF3L6H:~/bash_course$./myscript
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
myscript 24 raevskym 0u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 1u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 2u CHR 4,1 25332 /dev/tty1
myscript 24 raevskym 3w REG 0,14 0 13510 ~/bash_course/myfile1
myscript 24 raevskym 6w REG 0,14 0 13229 ~/bash_course/myfile2Lo script ha aperto due descrittori per l’output (
3
e6
) e uno per l’input (7
). Anche i percorsi dei file usati per configurare i descrittori sono mostrati qui.Suppressione dell’output
A volte è necessario assicurarsi che i comandi nello script, che, per esempio, possono essere eseguiti come processo in background, non mostrino nulla sullo schermo. Per fare questo, è possibile reindirizzare l’output a
/dev/null
. Questo è qualcosa come un “buco nero”.Per esempio, ecco come sopprimere i messaggi di errore:
ls -al badfile anotherfile 2> /dev/null
Lo stesso approccio si usa se, per esempio, avete bisogno di pulire un file senza cancellarlo:
cat /dev/null > myfile
Outcome
Oggi hai imparato come funzionano input e output nello scripting a riga di comando. Ora sai come gestire i descrittori di file, crearli, visualizzarli e chiuderli, sai come reindirizzare i flussi di input, output ed errore. Questi sono tutti molto importanti nello sviluppo di script bash.
La prossima volta, parliamo dei segnali di Linux, come gestirli negli script, come eseguire lavori programmati e compiti in background.