I want to:
You can read, write and truncate with "a+" (Ruby):
File.open("test.txt", "a+") do |f|
f.print "abc\ndefgh"
f.rewind
p f.read
f.truncate(5)
end
puts File.size("test.txt") #=> 5
I don't know of any elegant way to do exactly this in Ruby. My solution would probably be to create a temporary file, write contents to it, and then rename it to the filename I really wanted. This would overwrite the previous file if it exists, or create the file if it did not. Something like this:
orig_filename = './whatever_file.log'
temp_filename = './.tempfile'
temp_file = File.new(temp_filename, 'w')
// Write contents to file
temp_file.close
File.rename(temp_filename, orig_filename)
The rename will raise SystemCallError
if it fails for whatever reason.
According to OpenGroup:
O_TRUNC
If the file exists and is a regular file, and the file is successfully opened O_RDWR or O_WRONLY, its length is truncated to 0 and the mode and owner are unchanged. It will have no effect on FIFO special files or terminal device files. Its effect on other file types is implementation-dependent. The result of using O_TRUNC with O_RDONLY is undefined.
So, O_TRUNC is probably passed when opening a file with "w" or "w+". This gives "truncation" a different meaning, not what I want.
With python the solution seems to open file at low-level I/O with os.open()
function.
The following python function:
def touchopen(filename, *args, **kwargs):
# Open the file in R/W and create if it doesn't exist. *Don't* pass O_TRUNC
fd = os.open(filename, os.O_RDWR | os.O_CREAT)
# Encapsulate the low-level file descriptor in a python file object
return os.fdopen(fd, *args, **kwargs)
has the behavior I wanted. You can use it like this (it's in fact my use case):
# Open an existing file or create if it doesn't exist
with touchopen("./tool.run", "r+") as doing_fd:
# Acquire a non-blocking exclusive lock
fcntl.lockf(doing_fd, fcntl.LOCK_EX)
# Read a previous value if present
previous_value = doing_fd.read()
print previous_value
# Write the new value and truncate
doing_fd.seek(0)
doing_fd.write("new value")
doing_fd.truncate()
Well, there are only these modes, and all of them have the "defects" you listed.
Your only option is to wrap open()
.
Why not something like this? (Python)
def touchopen(filename, *args, **kwargs):
open(filename, "a").close() # "touch" file
return open(filename, *args, **kwargs)
it behaves just like open, you could even rebind it to open() if you really wish.
all of open's features are preserved, you can even do:
with touchopen("testfile", "r+") as testfile:
do_stuff()
You could of course create a contextmanager which opens the file in a+ mode, reads it into memory, and intercepts writes so you handle truncation by magically creating a temporary file in w mode, and renames that tempfile to your original file when you close it, but that would be overkill I guess.