• Home
  • Releases
  • Submit Vuln
  • Press
  • About
  • PGP
  • Contact
    • Contact
    • Submit Vuln
    • VDP
  • Tutorials
    • All Posts
    • Photoshop on Linux
    • macOS on Linux
  • Supporters
  • Projects
  • Training
Sick Codes - Security Research, Hardware & Software Hacking, Consulting, Linux, IoT, Cloud, Embedded, Arch, Tweaks & Tips!
  • Home
  • Releases
  • Submit Vuln
  • Press
  • About
  • PGP
  • Contact
    • Contact
    • Submit Vuln
    • VDP
  • Tutorials
    • All Posts
    • Photoshop on Linux
    • macOS on Linux
  • Supporters
  • Projects
  • Training
No Result
View All Result
Sick Codes - Security Research, Hardware & Software Hacking, Consulting, Linux, IoT, Cloud, Embedded, Arch, Tweaks & Tips!
  • Home
  • Releases
  • Submit Vuln
  • Press
  • About
  • PGP
  • Contact
    • Contact
    • Submit Vuln
    • VDP
  • Tutorials
    • All Posts
    • Photoshop on Linux
    • macOS on Linux
  • Supporters
  • Projects
  • Training
No Result
View All Result
Sick Codes - Security Research, Hardware & Software Hacking, Consulting, Linux, IoT, Cloud, Embedded, Arch, Tweaks & Tips!
No Result
View All Result
Home Tutorials

ULTIMATE stdin, stdout Guide: How To Duplicate stdin, stdout in BASH/Redirect Output to 2 files, Send Output to Multiple Variables and more!

by Sick Codes
June 1, 2020 - Updated on June 24, 2020
in Tutorials
0

Quick heads up: everything in Linux is a file. You can write, read, delete and wait for a file.

Everything you type in the terminal is symlinked to /dev/pts/1.

stdin goes to /dev/fd/0 and then /dev/pts/1
stdout goes to /dev/fd/1 and then /dev/pts/1
stderr goes to /dev/fd/2 and then /dev/pts/1

Funnily enough, all 3 of them eventually go to /dev/pts/1.

Why? I thought they were different? Not so much.

This is the reason you can see them on terminal!

Here’s the simplest example of duplicating stdout

tee /dev/fd/1 <<< foo
# foo
# foo

Tee automatically created another file descriptor for us.

Now watch this!
This is the same as the above

echo hi | tee /dev/fd/1
# hi
# hi

Instead of sending it back to stdout, send it to stderror!

echo hi | tee /dev/fd/2
# hi
# hi

Now watch when we send to /dev/fd/3 or /dev/fd/0

echo hi | tee /dev/fd/3
# hi

Wtf? Why didn't this print two "hi"s?

Well, because fd/3 doesn't go anywhere useful.

The terminal isn't programmed to read from /dev/fd/3, unless you instruct it.

When you pipe the command to fd 0, the command never ends because it hasn't reached the end of the input.

Watch when you pipe to /dev/0: inputting, and inputting, and inputting!

echo hi | tee /dev/fd/0
# hi
# hi
# hi
# hi
# hi
# hi
# hi
...

The reason these scenarios happen is because that's just the way the terminal is designed.

0 for input
1 for output
2 for error

All 3 of them go to /dev/pts/1 eventually.

If you try piping hi to /dev/fd/2 and then redirecing stderror to /dev/null it won't show you the second command like before.

# normal way to hide error
mount foo bar
# mount point does not exist.
mount foo bar 2>/dev/null

It's just a file bro.

echo hi | tee /dev/fd/2
# hi
# hi
echo hi | tee /dev/fd/2 2>/dev/null
# hi
echo hi | tee /dev/fd/2 2>/dev/null >/dev/null
#


That’s because all 3 of the file descriptors are actually very arbitrary. You can control them like this:

# all the same
echo 1 > file
echo 1 1> file
echo 1 1>&1 file
echo 1 1>&1 1>&1 1>&1 file

You can control them very easily too

1> is the same as >/dev/pts/1

All of these are the same thing

echo hi
# hi
echo hi > /dev/fd/1
# hi
echo hi 1> /dev/fd/1
# hi
echo hi 1> /dev/pts/1
# hi

This command should make sense now:

# echo 1, but send fd/1 as fd/0 for the next command
echo 1 | echo "$( < /dev/fd/0 )" 
# 1

Lets rewrite that

# echo 1, but send the output to fd/0, in a subshell, which reads fd/1 as commands
$(echo 1 >&0)
# 1
$(echo 1)
# 1: command not found
$(echo 1 1>&1)
# 1: command not found
$(echo 1 1>&0)
# 1

Same thing! 1>&0 is really just >/dev/fd/0

$(echo 1 1>&/dev/fd/1)
# 1: command not found
$(echo 1 1>&/dev/fd/0)
# 1
$(echo 1 >&/dev/fd/1)
# 1: command not found
$(echo 1 >&/dev/fd/0)
# 1

Lastly, another way to double the input of two different commands commands

Double stdin to one stdout

printf foo; printf bar;
( printf foo; printf bar; )  >/dev/fd/1
( printf foo; printf bar; )  1>&1
( printf foo; printf bar; )  1>&/dev/fd/1
( printf foo; printf bar; )  1>&/dev/pts/1
( printf foo; printf bar; )  | xargs printf
# foobar
# reverse it: send to subshell, print bar, hear from print foo
( printf foo & printf bar )  | xargs printf
# barfoo

Finally, we approach two commands to one output:

# before
( comm1; comm2; ) 1>&1
# or
( comm1; comm2; ) > file

Send one command to two outputs

You actually require two processes to do this.
/dev/pts/1 cannot say two things at the exact same time, it must be sequential.

Note: $REPLY is the default variable for "read" command if you don't set one.

read <<< 1 
echo $REPLY
1
read < <(echo 1)
echo $REPLY
1
read var1 var2 < <(echo 1 2)
echo $var1
1
echo $var2
2
# consider this
read < <(echo foo)
echo $REPLY > dest1
echo $REPLY > dest2

# same as this
read <<< $(echo foo)
echo $REPLY > dest1
echo $REPLY > dest2

# and this
read <<< foo ; ( echo $REPLY > dest1 ); ( echo $REPLY > dest2 );

# and logical
echo foo | tee >(read; echo "internal ${REPLY}") >(read; echo "internal2 ${REPLY}")

# to multiple files
echo foo | tee >(read; echo $REPLY >test1) >(echo $REPLY >test2)
tee >(read; echo $REPLY >test1) >(echo $REPLY >test2) <<< foo

# the best
tee -a test1 -a test2 <<< foo

Pipe command to two places

echo foo | tee >(read; echo "internal ${REPLY}") >(read; echo "internal2 ${REPLY}")
# foo
# internal foo
# internal2 foo

Pipe to two variables (assign stdout to 2 variables)

# you can't read the subshell variables
echo foo | tee >(read var1) >(read var2)
echo $var1
# 

# you cant read the subshell variables...

# unless... we create more /dev/fd's!
exec 300<>/tmp/300
exec 301<>/tmp/301
echo foo | tee -a /dev/fd/300 -a /dev/fd/301

# Now the message sits in the pipe, waiting to be read.
read < /dev/fd/300 && echo $REPLY
foo
read -u 301 && echo $REPLY
foo

Read stdin and stdout across subshells and terminals

# T1
exec 301<>/tmp/301
while true; do
    read -N 20 < /dev/urandom
    echo ${REPLY//[^[:ascii:]]/} > /dev/fd/301
    sleep 5
done

# T2
tail --follow=descriptor 301
# abc
# def
# etc

Triple bash stdoutput

tee -a /dev/fd/1 -a /dev/fd/1 <<< foo
# foo
# foo
# foo

BONUS! Echo command/stdout into other terminal window! Tell one pts to talk to another pts.

Open two terminals.

ls -lha /dev/pts
[user@hostname ~]$ ls -lha /dev/pts
total 0
drwxr-xr-x  2 root root      0 Jun  1 14:49 .
drwxr-xr-x 22 root root   3.7K Jun  1 14:49 ..
crw--w----  1 user tty  136, 0 Jun  1 22:59 0
crw--w----  1 user tty  136, 1 Jun  1 19:18 1
crw--w----  1 user tty  136, 2 Jun  1 19:22 2
c---------  1 root root   5, 2 Jun  1 14:49 ptmx
echo 1 > /dev/pts/1
echo 2 > /dev/pts/2

And you'll see the command in the other terminal (TTY) where PTS is pseudo terminal.

Next Post

How To Run Teamviewer 15 PERFECTLY on Arch/Manjaro/Debian/Ubutnu

move ssd from old to new

How to Move Linux OS from old NVMe to new NVMe drive.

Arch Linux Rasbperry Pi

How to Install Arch Linux ARM on Raspberry Pi 4 (and 3b+) (and also install XFCE!)

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

No Result
View All Result
  • Home
  • Releases
  • Submit Vuln
  • Press
  • About
  • PGP
  • Contact
    • Contact
    • Submit Vuln
    • VDP
  • Tutorials
    • All Posts
    • Photoshop on Linux
    • macOS on Linux
  • Supporters
  • Projects
  • Training

© 2017-2021 Sick.Codes

@sickcodes

@sickcodes

@sickcodes

Discord Server

sickcodes.slack.com

t.me/sickcodeschat

./contact_form