echo o print / dev / stdin / dev / stdout / dev / stderr


13

Voglio stampare il valore di / dev / stdin, / dev / stdout e / dev / stderr.

Ecco il mio semplice script:

#!/bin/bash
echo your stdin is : $(</dev/stdin)
echo your stdout is : $(</dev/stdout)
echo your stderr is : $(</dev/stderr)

io uso i seguenti tubi:

[root@localhost home]# ls | ./myscript.sh
[root@localhost home]# testerr | ./myscript.sh

$(</dev/stdin)sembra solo funzionare, ho anche trovato su alcune altre domande che le persone usano: l' "${1-/dev/stdin}"ho provato senza successo.

Risposte:


29

stdin, stdoute stderrsono flussi associati ai descrittori di file 0, 1 e 2 rispettivamente di un processo.

Al prompt di una shell interattiva in un emulatore di terminale o terminale, tutti e 3 i descrittori di file farebbero riferimento alla stessa descrizione di file aperto che sarebbe stata ottenuta aprendo un file di dispositivo terminale o pseudo-terminale (qualcosa di simile /dev/pts/0) in lettura + scrittura modalità.

Se da quella shell interattiva, avvii lo script senza utilizzare alcun reindirizzamento, lo script erediterà quei descrittori di file.

Su Linux, /dev/stdin, /dev/stdout, /dev/stderrsono collegamenti simbolici a /proc/self/fd/0, /proc/self/fd/1, /proc/self/fd/2rispettivamente stessi link simbolici speciali al file effettivo che è aperto su quei descrittori di file.

Non sono stdin, stdout, stderr, sono file speciali che identificano i file a cui vanno i file stdin, stdout, stderr (notare che è diverso in altri sistemi rispetto a Linux che hanno quei file speciali).

leggere qualcosa da stdin significa leggere dal descrittore di file 0 (che punterà da qualche parte all'interno del file a cui fa riferimento /dev/stdin).

Ma in $(</dev/stdin), la shell non legge da stdin, apre un nuovo descrittore di file per la lettura sullo stesso file di quello aperto su stdin (quindi leggendo dall'inizio del file, non dove stdin attualmente punta).

Tranne nel caso speciale di dispositivi terminali aperti in modalità lettura + scrittura, stdout e stderr non sono normalmente aperti per la lettura. Sono pensati per essere flussi a cui scrivi . Quindi la lettura dal descrittore di file 1 generalmente non funzionerà. Su Linux, l'apertura /dev/stdouto la /dev/stderrlettura (come in $(</dev/stdout)) funzionerebbe e ti permetterebbe di leggere dal file dove stdout va (e se stdout era una pipe, che leggerebbe dall'altra estremità della pipe, e se fosse un socket , fallirebbe poiché non è possibile aprire un socket).

Nel nostro caso lo script viene eseguito senza reindirizzamento al prompt di una shell interattiva in un terminale, tutti i file / dev / stdin, / dev / stdout e / dev / stderr saranno quel file / dev / pts / x terminal device.

La lettura da quei file speciali restituisce ciò che viene inviato dal terminale (ciò che si digita sulla tastiera). Scrivere su di essi invierà il testo al terminale (per la visualizzazione).

echo $(</dev/stdin)
echo $(</dev/stderr)

sarà lo stesso. Per espandere $(</dev/stdin), la shell aprirà quel / dev / pts / 0 e leggerà ciò che si digita fino a quando non si preme ^Dsu una riga vuota. Passeranno quindi l'espansione (ciò che hai digitato spogliato delle nuove righe finali e soggetto a split + glob) a echocui poi lo emetterà su stdout (per la visualizzazione).

Tuttavia in:

echo $(</dev/stdout)

in bash( e bashsolo ), è importante rendersi conto che dentro $(...), stdout è stato reindirizzato. Ora è una pipa. Nel caso di bash, un processo di shell figlio sta leggendo il contenuto del file (qui /dev/stdout) e lo sta scrivendo nella pipe, mentre il genitore legge dall'altra estremità per compensare l'espansione.

In questo caso, quando si apre quel processo bash figlio /dev/stdout, si sta effettivamente aprendo l'estremità di lettura della pipe. Nulla verrà mai da quello, è una situazione di stallo.

Se volessi leggere dal file indicato dallo script stdout, dovresti aggirarlo con:

 { echo content of file on stdout: "$(</dev/fd/3)"; } 3<&1

Ciò duplicherebbe fd 1 su fd 3, quindi / dev / fd / 3 punta allo stesso file di / dev / stdout.

Con uno script come:

#! /bin/bash -
printf 'content of file on stdin: %s\n' "$(</dev/stdin)"
{ printf 'content of file on stdout: %s\n' "$(</dev/fd/3)"; } 3<&1
printf 'content of file on stderr: %s\n' "$(</dev/stderr)"

Quando eseguito come:

echo bar > err
echo foo | myscript > out 2>> err

Vedresti in outseguito:

content of file on stdin: foo
content of file on stdout: content of file on stdin: foo
content of file on stderr: bar

Se al contrario di lettura da /dev/stdin, /dev/stdout, /dev/stderr, si voleva leggere da stdin, stdout e stderr (che renderebbe ancora meno senso), faresti:

#! /bin/sh -
printf 'what I read from stdin: %s\n' "$(cat)"
{ printf 'what I read from stdout: %s\n' "$(cat <&3)"; } 3<&1
printf 'what I read from stderr: %s\n' "$(cat <&2)"

Se hai avviato di nuovo quel secondo script come:

echo bar > err
echo foo | myscript > out 2>> err

Vedresti in out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr:

e in err:

bar
cat: -: Bad file descriptor
cat: -: Bad file descriptor

Per stdout e stderr, catfallisce perché i descrittori di file erano aperti solo per la scrittura , non per la lettura, l'espansione di $(cat <&3)ed $(cat <&2)è vuota.

Se lo hai chiamato come:

echo out > out
echo err > err
echo foo | myscript 1<> out 2<> err

(dove si <>apre in modalità lettura + scrittura senza troncamento), vedresti in out:

what I read from stdin: foo
what I read from stdout:
what I read from stderr: err

e in err:

err

Noterai che nulla è stato letto da stdout, perché il precedente printfaveva sovrascritto il contenuto di outwith what I read from stdin: foo\ned aveva lasciato la posizione stdout all'interno di quel file subito dopo. Se avessi innescato outun testo più grande, come:

echo 'This is longer than "what I read from stdin": foo' > out

Quindi entrerai out:

what I read from stdin: foo
read from stdin": foo
what I read from stdout: read from stdin": foo
what I read from stderr: err

Guarda come $(cat <&3)ha letto ciò che era rimasto dopo il primo printf e così facendo ha anche spostato la posizione stdout oltre in modo che il successivo printfemetta ciò che è stato letto dopo.


2

stdoute stderrsono uscite, non leggi da loro puoi solo scrivere a loro. Per esempio:

echo "this is stdout" >/dev/stdout
echo "this is stderr" >/dev/stderr

i programmi scrivono su stdout di default, quindi il primo è equivalente a

echo "this is stdout"

e puoi reindirizzare stderr in altri modi come

echo "this is stderr" 1>&2

1
Su Linux, echo xnon è lo stesso echo x > /dev/stdoutse stdout non va in una pipe o in alcuni dispositivi a caratteri come un dispositivo tty. Ad esempio, se stdout passa a un file normale echo x > /dev/stdout, troncerebbe il file e lo rimpiazzerebbe x\ninvece di scrivere x\nnella posizione stdout corrente.
Stéphane Chazelas,

@ Michael-Daffin> Grazie, quindi se voglio leggere stdout e stderr posso usare solo cat? (dal momento che sono file), ad esempio voglio mostrare all'utente l'ultimo errore che si è verificato echo this is your error $(cat /dev/stderr)?
Omar BISTAMI,

@OmarBISTAMI Non puoi leggere da stdoute stderr, sono output.
Kusalananda

1

myscript.sh:

#!/bin/bash
filan -s

Quindi eseguire:

$ ./myscript.sh
    0 tty /dev/pts/1
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh
    0 pipe 
    1 tty /dev/pts/1
    2 tty /dev/pts/1

$ ls | ./myscript.sh > out.txt
$ cat out.txt
    0 pipe 
    1 file /tmp/out.txt
    2 tty /dev/pts/1

Probabilmente avrete bisogno di installare filancon sudo apt install socatprima.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.