You need the write permission on a directory to create or remove files in it, but not to write to a file in it. Most shell commands, when given an output file, simply open that file for writing, and replace the data that was previously in the file. The >
redirection operator truncates the existing file (i.e. deletes the existing file content, resulting in a file with length zero) and starts writing to it. The >>
redirection operator causes data to be appended to the end of the file.
Writing to files is limited by the possibilities offered by the low-level interface. You can overwrite bytes in a file in place, append to the end of the file, and truncate a file to a chosen length. You cannot insert bytes while shifting subsequent bytes forward (as in foobar
→ fooNEWSTUFFbar
) nor delete bytes while shifting subsequent bytes backwards (as in foobar
→ for
), except by simulating these operations by reading the data to move and writing it at its new location.
The problem with editing files in place is that it's difficult to ensure that the file content remains consistent if something goes wrong (program bug, disk full, power loss, …). This is why robust file processing usually involves writing a temporary file with the new data, then moving that temporary file into place.
Another limitation with editing files in place is that complex operations involving reads and writes are not atomic. This is a problem only if some other task may want to read the file at the same time as your modification. For example, if you change foobar
to fooNEWSTUFFbar
by reading the last 3 bytes (bar
) then writing the new data (foo
→ fooNEWSTUFF
) and finally appending the old tail (fooNEWSTUFF
→ fooNEWSTUFFbar
), a concurrent reader might see fooNEWSTUFF
, or even other partial states of the file with only part of the data written. Again, writing to a temporary file first solves this problem, because moving the temporary file in place is an atomic operation.
If you don't care about these limitations, then you can modify a file in place. Appending data is easy (>>
), most other transformations are more involved. A common pitfall is that
somefilter <somefile >somefile
does NOT apply somefilter
to the file content: the >
operator truncates the output file before somefilter
starts reading from it.
Joey Hess's moreutils contains a utility called sponge
which fixes this problem. Instead of redirecting the output to somefile
, you pipe it into sponge
, which reads all of its input and then overwrites the existing file with the input that it's read. Note that it's still possible to end up with partial data if the filter fails.
somefilter <somefile | sponge somefile
If you don't have sponge
, the portable easy way to fix this is to first read the data into memory:
content=$(cat somefile; echo a)
content=${content%a}
The echo a
bit is to preserve newlines at the end of the file — command substitution always strips off trailing newlines. You can then pass the content to a command:
printf %s "$content" | somefilter >somefile
This replaces the content of the file by the output of the filter. If the command fails for any reason, the original content of the file is lost and the file contains whatever data the command wrote before failing.
Beware that this method doesn't work for binary files, because most shells don't support null bytes.
Another way to modify a file in place is to use the ed
editor, which bears a strong resemblance to sed
, but loads the file into memory and saves it in place, as opposed to sed's line-by-line operation.
Acting on a file without loading it into memory and without creating a temporary file is trickier with only standard shell tools, but it can be done. Shell redirection and most text processing utilities only let you append to a file or overwrite it from the beginning. You can use dd conv=notrunc seek=…
to overwrite data at some offset in a file without affecting the parts that aren't being overwritten; see Is there a way to modify a file in-place? for an example.
rev readonly/foo | sponge readonly/foo
– deltab Apr 13 '15 at 01:47