The redirection operator to open a file in read+write mode without truncation is
<> in all Bourne-like shells (that maps to
open(file, O_RDWR|O_CREAT) (though
zsh also throws in a
exec 3<> "$file"
$file on file descriptor 3 in read+write mode (without truncating it and creating it if it didn't exist).
zsh have seeking operators.
dd can seek, but not backwards. And note no shell except
zsh can have NUL bytes in their variables.
exec 3<> $file
sysread -i 3 -c 2 var # a read() of 2 bytes
sysseek -u 3 0 # seek back to beginning
# or sysseek -u 3 -w current -2 # to seek back 2 bytes
syswrite -o 3 something-else
exec 3<&- # close
exec 3<> "$file"
var=$(dd bs=2 count=1 <&3 2>/dev/null; echo .)
exec 3<#((0)) # seek to beginning
# or exec 3<#((CUR-2)) # to seek back 2 bytes
print -ru3 something-else
Portably, you could still open the file several times, for each offset you want, like here to read and write 2 bytes at offset 2 (provided they're not bytes with value 0 if not using
var=$(dd bs=2 count=1 skip=1 < "$file"; echo .)
printf %s something-else | dd bs=2 seek=1 1<> "$file"
printf %s something-else | dd bs=2 seek=1 of="$file" conv=notrunc
To read and write to the same file,
ksh93 has two other interesting redirection operators:
tr 01 10 < file >; file
Would store the output of
tr in a temporary file and if
tr is successful, rename that to
file (beware the file is created anew, so with possibly different permissions and ownership).
tr -d 0 < file 1<>; file
Same as the standard/Bourne
tr -d 0 < file 1<> file except that if
file is truncated where
tr finished writing. You can use that for filter commands that produce less output than they read input, or more precisely commands that would not read data that they've previously written.
zsh has the
=(...) form of process substitution which you can use as:
mv =(tr 01 10 < file) file
(with similar effect and caveats as
cp =(tr 01 10 < file) file
which would preserve attributes of
file but means an extra copy.
Now if you need to read and write at the same offset using the same file descriptor and neither zsh nor ksh93 are available, you could always revert to
perl -e '
open F, "<>", "file" or die "open: $!";
read F, $var, 1;
seek F, 0, 0;
print F "something-else"'
Now, after re-reading the updated version of your question, it looks like your file is behaving more like a socket or bidirectional pipe, and not like a regular, seekable file.
In which case, it could be just a matter of:
socat - file:your-file
(cat >&3 3>&- & cat <&3 3<&-) 3<> your-file
to feed data from and to that file as read from/to stdin/stdout.
Note that each
cat reads/writes to its own copy of the file descriptor 3 open by the shell, but they share the same open file description so it should be equivalent.