This construct does work (it uses tr
as a sample operation):
{ 'rm' -f File.txt && tr '[:lower:]' '[:upper:]' > File.txt; } < File.txt
The input redirection < File.txt
assigns the file as stdin to the statements in braces first, before executing anything within the block.
Removing the file inside the block does not remove the contents, because the file has an open file descriptor. Note the rm
is quoted to avoid the case where it might be aliased, e.g. with the -i
option.
The output redirection creates a new output file (inode) with the same name as the original. When the tr
terminates, the new file is closed.
Finally, the input redirection completes, it now has no connections via directories or file descriptors, and its resources are freed up.
Edit based on Stéphane Chazelas' excellent comments:
(a) The rm
should have the -f
option, to avoid all prompts and errors.
(b) The tr
should use the Locale-independent character classes.
(c) The new File.txt is independent from the old one, with different inode number, birth time and possible ownership, permissions, acl or other extended attributes. It also replaces a symlink with a plain file and would detach a hard link, leaving the original data under a different path name.