Bash
reindirizzamento
Ricerca…
Sintassi
- comando </ path / to / file # Reindirizza l'input standard al file
- comando> / path / to / file # Redirige l'output standard su flie
- comando file_descriptor> / path / to / file # Rediretta l'output di file_descriptor in un file
- comando> & file_descriptor # Reindirizza l'output a file_descriptor
- comando file_descriptor> & another_file_descriptor # Reindirizza file_descriptor a another_file_descriptor
- comando <& file_descriptor # Reindirizza file_descriptor allo standard input
- comando &> / percorso / al / file # Redirige l'output standard e l'errore standard nel file
Parametri
Parametro | Dettagli |
---|---|
descrittore di file interno | Un numero intero |
direzione | Uno di > , < o <> |
descrittore o percorso del file esterno | & seguita da un numero intero di descrittore di file o un percorso. |
Osservazioni
I programmi di console UNIX hanno un file di input e due file di output (i flussi di input e output, così come i dispositivi, sono trattati come file dal sistema operativo.) Questi sono in genere la tastiera e lo schermo, rispettivamente, ma alcuni o tutti possono essere reindirizzati venire da - o andare a - un file o un altro programma.
STDIN
è un input standard ed è il modo in cui il programma riceve input interattivi. STDIN
viene solitamente assegnato il descrittore di file 0.
STDOUT
è l'output standard. Tutto ciò che viene emesso su STDOUT
è considerato il "risultato" del programma. STDOUT
viene solitamente assegnato il descrittore di file 1.
STDERR
è dove vengono visualizzati i messaggi di errore. In genere, quando si esegue un programma dalla console, STDERR
viene visualizzato sullo schermo ed è indistinguibile da STDOUT
. STDERR
viene in genere assegnato il descrittore di file 2.
L'ordine di reindirizzamento è importante
command > file 2>&1
Reindirizza entrambi ( STDOUT
e STDERR
) al file.
command 2>&1 > file
Reindirizza solo STDOUT
, poiché il descrittore di file 2 viene reindirizzato al file indicato dal descrittore di file 1 (che non è ancora il file di file
quando viene valutata l'istruzione).
Ogni comando in una pipeline ha il proprio STDERR
(e STDOUT
) perché ognuno è un nuovo processo. Ciò può creare risultati sorprendenti se si prevede che un reindirizzamento influenzi l'intera pipeline. Ad esempio questo comando (incluso per la leggibilità):
$ python -c 'import sys;print >> sys.stderr, "Python error!"' \
| cut -f1 2>> error.log
stamperà "Errore Python!" alla console piuttosto che al file di registro. Invece, allegare l'errore al comando che si desidera acquisire:
$ python -c 'import sys;print >> sys.stderr, "Python error!"' 2>> error.log \
| cut -f1
Reindirizzamento dell'output standard
>
reindirizzare lo standard output (aka STDOUT
) del comando corrente in un file o un altro descrittore.
Questi esempi scrivono l'output del comando ls
nel file file.txt
ls >file.txt
> file.txt ls
Il file di destinazione viene creato se non esiste, altrimenti questo file viene troncato.
Il descrittore di reindirizzamento predefinito è lo standard output o 1
quando nessuno è specificato. Questo comando è equivalente agli esempi precedenti con l'output standard esplicitamente indicato:
ls 1>file.txt
Nota: il reindirizzamento viene inizializzato dalla shell eseguita e non dal comando eseguito, pertanto viene eseguito prima dell'esecuzione del comando.
Reindirizzamento STDIN
<
legge dal suo argomento giusto e scrive nel suo argomento di sinistra.
Per scrivere un file in STDIN
dovremmo leggere /tmp/a_file
e scrivere in STDIN
cioè 0</tmp/a_file
Nota: il descrittore di file interno è impostato su 0
( STDIN
) per <
$ echo "b" > /tmp/list.txt
$ echo "a" >> /tmp/list.txt
$ echo "c" >> /tmp/list.txt
$ sort < /tmp/list.txt
a
b
c
Reindirizzamento di STDOUT e STDERR
I descrittori di file come 0
e 1
sono puntatori. Modifichiamo ciò che i descrittori di file puntano con il reindirizzamento. >/dev/null
indica 1
punti a /dev/null
.
Innanzitutto puntiamo 1
( STDOUT
) a /dev/null
quindi il punto 2
( STDERR
) a qualunque punto 1
.
# STDERR is redirect to STDOUT: redirected to /dev/null,
# effectually redirecting both STDERR and STDOUT to /dev/null
echo 'hello' > /dev/null 2>&1
Questo può essere ulteriormente ridotto al seguente:
echo 'hello' &> /dev/null
Tuttavia, questa forma può essere indesiderabile nella produzione se la compatibilità della shell è un problema in quanto è in conflitto con POSIX, introduce un'ambiguità di parsing e le shell senza questa caratteristica la interpretano male:
# Actual code
echo 'hello' &> /dev/null
echo 'hello' &> /dev/null 'goodbye'
# Desired behavior
echo 'hello' > /dev/null 2>&1
echo 'hello' 'goodbye' > /dev/null 2>&1
# Actual behavior
echo 'hello' &
echo 'hello' & goodbye > /dev/null
NOTA: &>
è noto per funzionare come desiderato sia in Bash che in Zsh.
Reindirizzamento di STDERR
2
è STDERR
.
$ echo_to_stderr 2>/dev/null # echos nothing
definizioni:
echo_to_stderr
è un comando che scrive "stderr"
in STDERR
echo_to_stderr () {
echo stderr >&2
}
$ echo_to_stderr
stderr
Aggiungi vs Truncate
Troncare >
- Crea il file specificato se non esiste.
- Tronca (rimuovi il contenuto del file)
- Scrivi su file
$ echo "first line" > /tmp/lines
$ echo "second line" > /tmp/lines
$ cat /tmp/lines
second line
Aggiungi >>
- Crea il file specificato se non esiste.
- Aggiungi file (scrivendo alla fine del file).
# Overwrite existing file
$ echo "first line" > /tmp/lines
# Append a second line
$ echo "second line" >> /tmp/lines
$ cat /tmp/lines
first line
second line
Spiegazione di STDIN, STDOUT e STDERR
I comandi hanno un ingresso (STDIN) e due tipi di uscite, uscita standard (STDOUT) e errore standard (STDERR).
Per esempio:
STDIN
root@server~# read
Type some text here
L'input standard viene utilizzato per fornire input a un programma. (Qui stiamo usando la read
builtin per leggere una riga da STDIN.)
STDOUT
root@server~# ls file
file
L'output standard viene generalmente utilizzato per l'output "normale" da un comando. Ad esempio, ls
elenca i file, quindi i file vengono inviati a STDOUT.
STDERR
root@server~# ls anotherfile
ls: cannot access 'anotherfile': No such file or directory
L'errore standard è (come suggerisce il nome) utilizzato per i messaggi di errore. Poiché questo messaggio non è un elenco di file, viene inviato a STDERR.
STDIN, STDOUT e STDERR sono i tre flussi standard. Sono identificati nella shell da un numero piuttosto che da un nome:
0 = Standard in
1 = Standard out
2 = errore standard
Per impostazione predefinita, STDIN è collegato alla tastiera e sia STDOUT che STDERR appaiono nel terminale. Tuttavia, possiamo reindirizzare STDOUT o STDERR a qualsiasi cosa di cui abbiamo bisogno. Ad esempio, diciamo che è necessario solo lo standard out e tutti i messaggi di errore stampati su errore standard dovrebbero essere soppressi. Questo è quando usiamo i descrittori 1
e 2
.
Reindirizzamento di STDERR a / dev / null
Prendendo l'esempio precedente,
root@server~# ls anotherfile 2>/dev/null
root@server~#
In questo caso, se c'è uno STDERR, verrà reindirizzato a / dev / null (un file speciale che ignora qualsiasi cosa inserito in esso), quindi non si otterrà alcun output di errore sulla shell.
Reindirizzamento di più comandi allo stesso file
{
echo "contents of home directory"
ls ~
} > output.txt
Utilizzando pipe denominate
A volte potresti voler produrre qualcosa da un programma e inserirlo in un altro programma, ma non puoi usare un tubo standard.
ls -l | grep ".log"
Potresti semplicemente scrivere su un file temporaneo:
touch tempFile.txt
ls -l > tempFile.txt
grep ".log" < tempFile.txt
Questo funziona bene per la maggior parte delle applicazioni, tuttavia nessuno saprà cosa fa tempFile
e qualcuno potrebbe rimuoverlo se contiene l'output di ls -l
in quella directory. È qui che entra in gioco una pipe denominata:
mkfifo myPipe
ls -l > myPipe
grep ".log" < myPipe
myPipe
è tecnicamente un file (tutto è in Linux), quindi facciamo ls -l
in una directory vuota che abbiamo appena creato una pipe in:
mkdir pipeFolder
cd pipeFolder
mkfifo myPipe
ls -l
L'output è:
prw-r--r-- 1 root root 0 Jul 25 11:20 myPipe
Notare il primo carattere nelle autorizzazioni, è elencato come una pipe, non un file.
Ora facciamo qualcosa di interessante.
Apri un terminale e prendi nota della directory (o creane uno in modo che la pulizia sia semplice) e crea una pipe.
mkfifo myPipe
Ora mettiamo qualcosa nel tubo.
echo "Hello from the other side" > myPipe
Noterai che questo si blocca, l'altro lato del tubo è ancora chiuso. Apriamo l'altro lato della pipa e lasciamo passare quella roba.
Apri un altro terminale e vai alla directory in cui si trova la pipe (o se lo conosci, aggiungilo alla pipe):
cat < myPipe
Noterai che dopo l' hello from the other side
, il programma nel primo terminale termina, così come nel secondo terminale.
Ora esegui i comandi al contrario. Inizia con cat < myPipe
e poi riecheggia qualcosa. Funziona ancora, perché un programma attenderà che qualcosa venga inserito nella pipe prima di terminare, perché sa che deve ottenere qualcosa.
Le pipe nominate possono essere utili per spostare le informazioni tra i terminali o tra i programmi.
I tubi sono piccoli Una volta completato, lo scrittore blocca fino a quando alcuni lettori leggono il contenuto, quindi è necessario eseguire il lettore e il writer in terminali diversi o eseguire uno o l'altro in background:
ls -l /tmp > myPipe &
cat < myPipe
Altri esempi con pipe denominate:
Esempio 1: tutti i comandi sullo stesso terminale / stessa shell
$ { ls -l && cat file3; } >mypipe & $ cat <mypipe # Output: Prints ls -l data and then prints file3 contents on screen
Esempio 2: tutti i comandi sullo stesso terminale / stessa shell
$ ls -l >mypipe & $ cat file3 >mypipe & $ cat <mypipe #Output: This prints on screen the contents of mypipe.
file3
vengono visualizzati i primi contenuti difile3
e quindi vengono visualizzati i datils -l
(configurazione LIFO).Esempio 3: tutti i comandi sullo stesso terminale / stessa shell
$ { pipedata=$(<mypipe) && echo "$pipedata"; } & $ ls >mypipe # Output: Prints the output of ls directly on screen
$pipedata
che la variabile$pipedata
non è disponibile per l'uso nel terminale principale / shell principale poiché l'uso di&
invoca una subshell e$pipedata
era disponibile solo in questa sottoshell.Esempio 4: tutti i comandi sullo stesso terminale / stessa shell
$ export pipedata $ pipedata=$(<mypipe) & $ ls -l *.sh >mypipe $ echo "$pipedata" #Output : Prints correctly the contents of mypipe
Questo stampa correttamente il valore della variabile
$pipedata
nella shell principale a causa della dichiarazione di esportazione della variabile. Il terminale principale / shell principale non è sospeso a causa dell'invocazione di una shell di sfondo (&
).
Stampa i messaggi di errore su stderr
I messaggi di errore sono generalmente inclusi in uno script per scopi di debug o per fornire una ricca esperienza utente. Semplicemente scrivendo un messaggio di errore come questo:
cmd || echo 'cmd failed'
può funzionare per casi semplici ma non è il solito modo. In questo esempio, il messaggio di errore inquinerà l'output effettivo dello script mescolando sia gli errori che l'output di successo in stdout
.
In breve, il messaggio di errore dovrebbe andare su stderr
non su stdout
. È piuttosto semplice:
cmd || echo 'cmd failed' >/dev/stderr
Un altro esempio:
if cmd; then
echo 'success'
else
echo 'cmd failed' >/dev/stderr
fi
Nell'esempio sopra, il messaggio di successo verrà stampato su stdout
mentre il messaggio di errore verrà stampato su stderr
.
Un modo migliore per stampare un messaggio di errore è definire una funzione:
err(){
echo "E: $*" >>/dev/stderr
}
Ora, quando devi stampare un errore:
err "My error message"
Reindirizzamento agli indirizzi di rete
Bash considera alcuni percorsi come speciali e può fare alcune comunicazioni di rete scrivendo in /dev/{udp|tcp}/host/port
. Bash non può configurare un server in ascolto, ma può iniziare una connessione e, per TCP, può leggere almeno i risultati.
Ad esempio, per inviare una semplice richiesta web si potrebbe fare:
exec 3</dev/tcp/www.google.com/80
printf 'GET / HTTP/1.0\r\n\r\n' >&3
cat <&3
e i risultati della pagina Web predefinita di www.google.com
verranno stampati su stdout
.
allo stesso modo
printf 'HI\n' >/dev/udp/192.168.1.1/6666
invierebbe un messaggio UDP contenente HI\n
a un listener su 192.168.1.1:6666