I'd expect
echo foo | tee /proc/self/fd/{3..6} 3>&1
to fail with errors like /proc/self/fd/4: No such file or directory etc., but to my surprise, it outputs
foo
foo
foo
foo
foo
It's like 3>&1
causes all following descriptors to be redirected to stdout, except it doesn't work if I change 3
to something else, like
$ echo foo | tee /proc/self/fd/{3..6} 4>&1
tee: /proc/self/fd/3: No such file or directory
tee: /proc/self/fd/5: No such file or directory
tee: /proc/self/fd/6: No such file or directory
foo
foo
$ echo foo | tee /proc/self/fd/{4..6} 4>&1
tee: /proc/self/fd/5: No such file or directory
tee: /proc/self/fd/6: No such file or directory
foo
foo
Is there an explanation for this behavior?
With the technology, Apple Watch Series 3 models can send and receive phone calls, text messages, and email without needing to have an iPhone nearby. Thanks to the S3 dual-core processor, Series 3 watches accelerate device performance up to 70%. Wi-Fi connectivity improves, too, thanks to Apple's custom W2 chip.
Launched in September 2017, the Apple Watch Series 3 keeps the classic design of earlier models but packs in quite a few new features. Alongside improvements, including the faster S3 processor and W2 wireless chip, was a cellular variant for the first time. Today, Apple sells it only in a GPS variant.
Breaking news: Series 7 is the most durable Apple Watch ever built. Fundamental design changes were needed to achieve the vision of the larger Always-On Retina display. These same innovations also helped make the most crack‑resistant front crystal yet. Crack ResistantOur strongest front crystal ever.
The Apple Watch Series 7 is Apple's latest smart watch, featuring a larger always-on Retina display, a more rounded design with a larger casing, improved durability, and faster charging, starting at a price of $399.
strace
shows this sequence of system calls:
$ strace -o strace.log tee /proc/self/fd/{3..6} 3>&1 ... $ cat strace.log ... openat(AT_FDCWD, "/proc/self/fd/3", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 4 openat(AT_FDCWD, "/proc/self/fd/4", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 5 openat(AT_FDCWD, "/proc/self/fd/5", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 6 openat(AT_FDCWD, "/proc/self/fd/6", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 7 ...
The first line opens /proc/self/fd/3
and assigns it the next available fd number, 4. /proc/self/fd/3
is a special path. Opening it has an effect similar to duping fd 3: fd 4 points to the same place as fd 3, the tty.
The same thing happens for each successive openat()
call. When the dust settles fds 4, 5, 6, and 7 are all duplicates of fd 3.
Note that the 3>&1
redirection isn't important. What's important is that we're asking tee to open /proc/self/fd/N
where N is already in use. We should get the same result if we get rid of 3>&1
and have tee start at /proc/self/fd/2
instead. Let's see:
$ echo foo | tee /proc/self/fd/{2..6} foo foo foo foo foo foo
Confirmed! Same result.
We can also repeat the same fd number over and over. We get the same result when we hit fd 6. By the time it reaches the last one it has opened enough descriptors to make the jump to 6 possible.
$ echo foo | tee /proc/self/fd/{2,2,2,2,6} foo foo foo foo foo foo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With