If you want to overwrite a file with Bash, this is easy
echo "Hello world" > hosts
This does not seem to work with a file descriptor
$ exec 3<> hosts
$ echo "Hello world" >&3
$ cat hosts
Hello world
$ echo "Hello world" >&3
$ cat hosts
Hello world
Hello world
That’s correct. The mode in which a file is opened is determined when the shell calls
open(2). When youDUP2an FD (in any language), the flags that were set when the file was opened are shared between open FDs. In your case,O_TRUNCcan only be specified when the file is actually opened.The important thing to know is that the mode and various flags are determined only when the file is opened using
<file,>file, or similar. Copying a FD with the&modifier essentially creates an “alias” that points to the original FD, and retains all the same state as the original. Truncating the file requires re-opening it.This is my debugging function if you’d like to play around with file descriptors easily:
I like to not close stdin, because this can sometimes cause problems, so it gets saved first.
As you can see, even if FD 0 is moved to 3 using the
3>&0-operator, the file remains openedO_RDONLY. The choice of>or<is arbitrary with the copy descriptor and only serves to determine the default if the FD on the left of the operator is omitted.If you did want to open a new independent FD, something like this could work:
Now FD 4 is really “re-opened” to the file FD 3 is pointed at, not just duplicated (even if the file has already been
unlink(2)‘d, as above). First FD 4 is opened and seeked to the end usingcat, then an additional line is written to the file. Meanwhile the seek position of FD 3 is still at the beginning, so the entire resulting file can be cated to the terminal.The same principle could be applied to your case.
Or probably even better would be to just open and close the file twice using two separate commands, without
exec.I prefer to avoid using
execjust to open file descriptors unless it’s absolutely necessary. You have to remember to explicitly close the file. If you use command grouping instead, files will automatically close themselves, in effect giving them a “scope”. This is similar in principle to Python’swithstatement. That might have prevented some confusion.See also: