Is there a generic solution to modifying files in place?
Is there are generic solution to modifying files in place?
For example, suppose I've got a file with data in it I need to process as follows: Code:
grep "^tag-" MyFile | cut -d"-" -f2 Now suppose I want to write the results back to the original file. What I can't do is: Code:
grep "^tag-" MyFile | cut -d"-" -f2 > MyFile The obvious solution is to write to a temporary file and to copy it back, which is bit ugly. A more elegant solution would be some sort of buffering command that will * Read ALL of the original file * Close the file * Write the contents back to standard out. The bash solution would be: Code:
BufferingCommand MyFile | grep "^tag-" | cut -d"-" -f2 > MyFile Does such a command exist? Is there a better solution? |
What you call a "buffering command" would actually write a temporary file...
For example, the following SED command replaces all occurences of "yes" to "no" and writes back to the same file. But what it REALLY does is first make a temporary file. sed -i 's/yes/no/g' filename some utilities also have options to write a backup file as part of the process. |
Code:
FILE=`tempfile`; cat MyFile > $FILE; grep "^tag-" $FILE | cut -d"-" -f2 > MyFile; rm $FILE EDIT: I tried making a script to pipe in... but it appeared to have the same problem. |
Thanks for the feed back.
I knew that a few commands had the ability to modify files (sed --in-place being a great example), but I can see this being a more general issue. This is why I used an example that was a bit more complex. I don't mind commands that internally create temporary files (as sed does) as long as they clean up afterwards. These commands are certainly more elegant then managing the temp files myself. The thing is, most commands don't have an in-place flag, so what to do in these cases? It looks like managing the file my self (with lumak's suggestion of the `tempfile` command) is the only real option, which is strange as I would imaging the to be a fairly common problem. |
Quote:
|
Quote:
Quote:
Something like this should work though: Code:
grep "^tag-" MyFile | cut -d"-" -f2 | BufferingCommand MyFile |
@ jlliagre
Thanks for that solution. My question is then, why don't these commands already completely read and close before writing new files? would that cause issues with streaming to stdout? Would it only cause the pipe to respond slower? is there an existing command we can fake as a buffer? (e.g. somecommands MyFile | sed -i "s@@@" > MyFile)(that doesn't work but just illustrates my question) |
Whether the commands do buffer or not doesn't matter. They haven't even a chance to run before it's too late. The shell itself handles the output redirection before launching any command. That's the reason why I suggest not to use ">" in the rightmost command.
|
Hi jlliagre,
Knowing that the the commands are started in reverse (right to left) order explains what I'm seeing. MyFile is emptied by the last command (the > operator) before the first command has had chance to read it. I'm curious, why does the shell launch the commands in this order? I'd like to give your alternative suggestion a go: Code:
grep "^tag-" MyFile | cut -d"-" -f2 | BufferingCommand MyFile |
Quote:
Quote:
Code:
a=$(cat) |
Sorry, but I'm not sure I understood
Code:
a=$(cat) Incidentally I tried using tee as a buffering command. The following works: Code:
grep "^tag-" MyFile | cut -d"-" -f2 | tee MyFile Code:
grep "^tag-" MyFile | cut -d"-" -f2 | tee MyFile > /dev/null |
Quote:
Quote:
Quote:
|
You can use that construction:
Code:
grep "^tag-" MyFile | cut -d"-" -f2 | { a=$(cat) ; echo "$a" > MyFile ;} |
Works perfectly :-)
Thanks |
It also helps if know that
Code:
a=$(cat) Code:
a=`cat` Thanks again |
All times are GMT -5. The time now is 08:29 PM. |