1

There's a way to cat a file, filter it by something and then output a new file with the same name? I'm doing that and I'm getting an empty file, but if I create it with different file name is working. I don't want to create a new file.

Example:

File="My_test_file.txt"
cat ${File} | grep -v "test" > ${File}

in that way is not working, I have to create another file to make it work, as follow:

File="My_test_file.txt"
cat ${File} | grep -v "test" > ${File}.tmp

any idea?

Javier Salas
  • 1,064
  • 5
  • 15
  • 38
  • 2
    I don't think either of the present answers address "why". To explain: **All parts of a pipeline execute in parallel**. Thus, the file is opened for output (and thus truncated!) while the `cat` from the input is still running (or, potentially, just starting up and not yet running yet at all). – Charles Duffy Jan 24 '18 at 19:12
  • (BTW, `cat ${File}` is better written as `cat "$File"`: The curly braces do nothing for correctness in this situation, whereas the lack of double quotes makes the contents prone to string-splitting and glob expansion, so it wouldn't work if you had a filename with spaces. Even better than that is to do away with the `cat` altogether and just let `grep` read directly from the input file). – Charles Duffy Jan 24 '18 at 19:13
  • @CharlesDuffy I agree this question's answers don't explain why but the accepted answer in your citation doesn't say how, which is what the OP wants to know. – Jeff Holt Jan 24 '18 at 19:15
  • 1
    @jeff6times7, I've added three (err, now four) additional duplicates that focus specifically on the "how", putting them above the one focused on "why". – Charles Duffy Jan 24 '18 at 19:19

2 Answers2

3

There's a package called moreutils that contains the tool sponge for this exact purpose:

grep -v test foo.txt | sponge foo.txt

If installing tools is not an option, you can implement a naive version that first reads all data into memory, and then finally writes it out:

#!/bin/sh

sponge() (
  var="$(cat; printf x)"
  printf '%s' "${var%x}" > "$1"
)

grep -v test foo.txt | sponge foo.txt
that other guy
  • 116,971
  • 11
  • 170
  • 194
  • 2
    It's probably worth pointing out *why* this works (specifically, that it consumes all available input, buffering it, *before* opening the output file). – Charles Duffy Jan 24 '18 at 19:15
1

What you are attempting to do with cat and grep -v can be easily done using sed -i '/pattern/d' and that allows to save changes inline as well:

sed -i.bak '/test/d' "$file"
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • its works to but I would need to do this: `sed -i.bak '/test/d' "$file" > "$file"` I have to create the new file outputted and overwrite the existing one, I did that with your command and is creating me an empty file, I would have to do something like `sed -i.bak '/test/d' "$file" > "${file}.tmp"` – Javier Salas Jan 24 '18 at 18:52
  • 2
    @JavierSalas No, `sed -i` modifies the file for you. Do not redirect it anywhere. – that other guy Jan 24 '18 at 18:55
  • @anubhava unfortunately sed -i option not work in my shell `sed: illegal option -- i` – Javier Salas Jan 24 '18 at 18:56
  • `-i` is present in BSD `sed` as well and of course `gnu sed` also has it. What is your OS and sed version? – anubhava Jan 24 '18 at 18:57