0

I find myself trying to "edit" a file in-place quite a lot in my shell. Say, I want to remove all lines in words.txt that contain the letter e. Then, I would execute:

$ grep -v e words.txt > words-without-e.txt
$ mv words-without-e.txt words.txt

Many people are confused why they are left with an empty file when executing the following (I am not):

$ grep -v e words.txt > words.txt
grep: words.txt: input file is also the output

Some commands have special flags for in-place behaviour, like --in-place (see sed(1)) to allow this. It's possible to use sponge(1), but this requires me to type the file name twice, which I don't like.

$ grep -v e words.txt | sponge words.txt

I have defined a function that I find handy:

function inplace { "${@:2}" < "$1" | sponge $1 }

With it, I can now do:

$ inplace words.txt grep -v e

I know that this is limited to "stream processing", i.e. commands that take standard input, process it, and produce standard output. But many commands do.

Maybe I looked in the wrong places, but I haven't stumbled across a similar definition yet, especially given how often the question about in-place editing comes up.

  • Is there a common utility that serves the same purpose?
  • Have you defined such a function? How does your definition look like? What did you call it?
  • Why does this not come up more often? Or did I just miss it?
5
  • In a way, it's odd that sponge itself doesn't provide the wrapper functionality... But it's also impossible to make a really generic wrapper, because an arbitrary tool might edit the given file in-place itself, and not print to stdout. Using the wrapper with one like that might be problematic. Also there's the question of caching the data to memory vs. to a temporary file, and overwriting the target in-place vs. creating a new file with the same name... (not sure what sponge does) Commented Oct 10, 2023 at 20:54
  • 1
    Anyway, quote the expansions so that your function works with arbitrary filenames: inplace() { "${@:2}" < "$1" | sponge "$1" } Commented Oct 10, 2023 at 20:56
  • Related, first solution in this answer Commented Oct 10, 2023 at 23:14
  • @ilkkachu, rather inplace() { "${@:2}" < "$1" | sponge -- "$1"; } to deal with arbitrary file paths. Or inplace() (file="${1?}"; shift; <"$file" "$@" | sponge -- "$file") to avoid that ksh93ism. Setting the pipefail option in there would also be useful to report failure of the command (in any case, the file is lost if the command fails). Commented Oct 11, 2023 at 5:47
  • @StéphaneChazelas, yes, indeed Commented Oct 11, 2023 at 5:52

1 Answer 1

0

As far as I know, there isn't a generic utility that can make any command work in-place.

Usually, when people want to programatically edit files in-place, they use sed or perl with -i parameter.

Probably that's the reason why this topic does not come up often - contrary to what you may think, it is not so much needed if there are utilities that can edit files in-place.

1
  • Yeah, I know about the -i flags. Will edit the question. Commented Oct 10, 2023 at 21:33

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.