views:

85

answers:

6

Let's say I have a file /etc/conf1

it's contents are along the lines of

option = banana
name = monkey
operation = eat

and let's say I want to replace "monkey" with "ostrich". How can I do that without reading the file to memory, altering it and then just writing it all back? Basically, how can I modify the file "in place"?

+1  A: 

You should look at the fileinput module:

http://docs.python.org/library/fileinput.html

There's an option to perform inplace editing via the input method:

http://docs.python.org/library/fileinput.html#fileinput.input

UPDATE - example code:


import fileinput
import re
import sys

for line in fileinput.input(inplace=True):
    sys.stdout.write(re.sub(r'monkey', 'ostrich', line))

Using sys.stdout.write so as not to add any extra newlines in.

John Montgomery
Tried it, couldn't get it to work. The fact that fileinput exists is the reason I'm posing this question. Can you elaborate or provide an example please?
Neoscopio
I've added some example code now. NB it does technically read the file into memory (a line at a time), but this is equivalent to how sed operates anyway.The key thing is you have to output the line even if you haven't changed it.
John Montgomery
+2  A: 

You can't. "ostrich" is one letter more than "monkey", so you'll have to rewrite the file at least from that point onwards. File systems do not support "shifting" file contents upwards or downwards.

If it's just a small file, there's no reason to bother with even this, and you might as well rewrite the whole file.

If it's a really large file, you'll need to reconsider the internal design of the file's contents, for example, with a block-based approach.

Thomas
This is something that has bothered me for some time and I could never figure out how to do this. I just thought I sucked at manipulating files. I actually once implemented the "read block, modify, write block" approach a while back in python and was told that was a stupid thing to do because when you f.read(), the system actually only loads up a few blocks at a time... and yeah, it was a large (100MB large) file.
Neoscopio
A: 

It depends on what you mean by "in place". How can you do it if you want to replace monkey with supercalifragilisticexpialidocious? Do you want to overwrite the remaining file? If not, you are going to have to read ahead and shift subsequent contents of the file forwards.

fmark
Shifting is more expensive that straight up writing a new file, resource-wise, I assume?
Neoscopio
A: 

CPU instructions operate on data which come from memory.

The portion of the file you wish to read must be resident in memory before you can read it; before you write anything to disk, that information must be in memory.

The whole file doesn't have to be there at once, but to do a search-replace on an entire file, every character of the file will pass through RAM at some point.

What you're probably looking for is something like the mmap() system call. The above fileinput module sounds like a plausible thing to use.

Borealid
If you just parse until you reach the config option you want, you only read the whole file in the worst case scenario. My problem was related to shifting the contents of the file, I guess...
Neoscopio
A: 

In-place modifications are only easy if you don't alter the size of the file or only append to it. The following example replaces the first byte of the file by an "a" character:

fd = os.open("...", os.O_WRONLY | os.O_CREAT)
os.write(fd, "a")
os.close(fd)

Note that Python's file objects don't support this, you have to use the low-level functions. For appending, open file file with the open() function in "a" mode.

Philipp
This is what I always assumed... thanks.
Neoscopio
`fp= open("…", "r+b"); fp.write("a"); fp.close()` Now, have I misunderstood you when you said “Python's `file` objects don't support this”?
ΤΖΩΤΖΙΟΥ
A: 

sed -i.bak '/monkey$/newword/' file

ghostdog74
... in python, without sed, subprocess, os.system or the like :D
Neoscopio