Bash read/write file descriptors -- seek to start of file

telotortium picture telotortium · Oct 1, 2010 · Viewed 13.8k times · Source

I tried to use the read/write file descriptor in bash so that I could delete the file that the file descriptor referred to afterward, as such:

F=$(mktemp)
exec 3<> "$F"
rm -f "$F"

echo "Hello world" >&3
cat <&3

but the cat command gives no output. I can achieve what I want if I use separate file descriptors for reading and writing:

F=$(mktemp)
exec 3> "$F"
exec 4< "$F"
rm -f "$F"

echo "Hello world" >&3
cat <&4

which prints Hello world.

I suspected that bash doesn't automatically seek to the start of the file descriptor when you switch from writing to reading it, and the following combination of bash and python code confirms this:

fdrw.sh

exec 3<> tmp
rm tmp

echo "Hello world" >&3
exec python fdrw.py

fdrw.py

import os  

f = os.fdopen(3)
print f.tell()
print f.read()

which gives:

$ bash fdrw.sh
12

$ # This is the prompt reappearing

Is there a way to achieve what I want just using bash?

Answer

David Ongaro picture David Ongaro · May 18, 2014

I found a way to do it in bash, but it's relying on an obscure feature of exec < /dev/stdin which actually can rewind the file descriptor of stdin according to http://linux-ip.net/misc/madlug/shell-tips/tip-1.txt:

F=$(mktemp)
exec 3<> "$F"
rm -f "$F"

echo "Hello world" >&3
{ exec < /dev/stdin; cat; } <&3

The write descriptor isn't affected by that so you can still append output to descriptor 3 before the cat.

Sadly I only got this working under Linux not under MacOS (BSD), even with the newest bash version. So it doesn't seem very portable.