TransWikia.com

Output tee to stdout while writing to a FD using here-strings

Unix & Linux Asked by kon on December 29, 2021

I am trying to output tee while writing to a custom file descriptor. Example:

exec 4>"/tmp/testfile.txt"; # open FD 4
tee -a >&4 <<< "Output this to stdout" # Write to open file
exec 4>&- # close FD

If I perform instead tee -a /proc/self/fd/4 <<< "Output this to stdout" I can see the output in my terminal / standard stdout.

I am using BASH 5.0.16(1)-release.

I tried doing a connection between FD 1 (stdout) and FD 4 but then my string wouldn´t be written to the file.

Thanks a lot!

EDIT:
Please refer to @Stéphane Chazelas answer. My updates below may be true but way to complicated.

Update:
I am getting closer I guess.
When trying tee <<< "Output this to stdout" >(tee -a >&4) I can actually see the output in my terminal and it writes to the file but for some reason it overrides the content and doesn´t seem to append (-a).

Update 2:
I feel like I´ve found my mistake now.

First: Using Bash Process Substitution I was able to output to stdout and write to my FD by using tee two times. I did this in this way:

exec 4>"/tmp/testfile.txt"; # open FD 4
tee >(tee -a >&4) <<< "Output this to stdout" # Write to open file
exec 4>&- # close FD

As mentioned before the file content is overwritten which is the second part of the answer.

Second: I´ve totally forgot about seek positions! If I open a file using exec 4>"..." the seek position (or pointer) is at the beginning of the file. In this case appending is like overwritting the file.
To fix this the file descriptor needs to read the complete file before any action to set the current position to the end of the file. One solution is just using cat. Of course I also need to open the file for reading then (<>).

Finally:
This works for me. It opens a FD for a file, read it´s content and append to it.

exec 4<>"/tmp/testfile.txt"; # open FD 4 read (<) and write (>)
cat <&4 >/dev/null; tee >(tee -a >&4) <<< "Output this to stdout" # read fd (>/dev/null to silent the output) for correct position and append to. Also output to stdout
exec 4>&- # close FD (this closes it completly, so no need for exec 4<&-)

KEEP IN MIND:
This can actually be carefull if you use multiple processes which modify the file. If I read correctly it´s not a "true" append.
Example: You did a cat and the position is 3. Someone else appends to the file two lines in the meantime. If you now append using your current known position the other two lines are lost.


I guess I´ve learned a bit about file reading in Unix systems again. Overall I might need to overthink my idea of opening a FD. If you know the file will be empty you are fine. But it can be risky with files which have already content and esp. when another process may write to it when your pointer is not up to date.
I may explained some technical aspects wrong but it should be correct in some way.

2 Answers

If you want to write the same input to both fd 1 and 4, you'd use zsh and do something like:

cat <<< some-input >&1 >&4

(or just <<< some-input >&1 >&4 as cat happens to be the default $NULLCMD)

In zsh, and if the multios option is enabled (as it is by default), redirecting a fd (here 1 as > is short for 1>) several times for output causes that fd to be redirected to an internal teeing process that multiplexes the output to all the targets.

While <<< (also a zsh extension) is now supported by quite a few other shells (including bash), that multios feature is not.

On systems other than Linux and Cygwin, you could also do:

tee /dev/fd/4 <<< some-input

As on most systems (except those 2 mentioned above), opening /dev/fd/4 does the same as duplicating fd 4 (same as {fd}>&4).

On Linux and Cygwin however, that's not what happens. Instead, opening /dev/fd/4 (same as /proc/self/fd/4 there) works as if you were opening the file currently open on that fd anew from scratch.

So, if fd 4 points to a socket for instance, that won't work as you can't open() a socket, and if it's a regular file, opening it with > will cause the file to be truncated.

With tee -a /dev/fd/4 <<< some-input, you can avoid the truncation, but beware fd 4 will be left asis, so if you write data again to fd 4, it will likely overwrite what was output by tee. You'd need the fd 4 to have also been opened in append mode (which guarantees all writes are done at the end) to avoid it.

Doing:

tee >(cat >&4) <<< some-input

Also addresses the problem, but means shoving the data through an extra cat process.

Answered by Stéphane Chazelas on December 29, 2021

> opens and truncates, >> opens for appending.

You therefore want:

exec 4>>"/tmp/testfile.txt";

tee -a would open files for appending, but you're not specifying any files to open so it has no effect . You might as well be using echo.

Answered by that other guy on December 29, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP