0

I am facing this strange behaviour in a simple pipe:

me$ echo "AAA" > tmp.txt
me$ cat tmp.txt | sed 's/A/B/g' > tmp.txt
me$ cat tmp.txt

The result is an empty file and not the desired "BBB" inside the tmp.txt It works though if I chose a different file for output. some ideas? thx in advance!

Anton Harald
  • 817
  • 1
  • 6
  • 10
  • not a duplicate - see my answer, which provides a different kind of solution. – La-comadreja Apr 27 '15 at 19:09
  • @La-comadreja, I disagree; that answer (write-and-rename) *is* already given in the other post, by Etan. – Charles Duffy Apr 27 '15 at 19:23
  • @La-comadreja, ...also, the approach you're using (with a fixed temporary filename rather than one generated by `mktemp`) is open to security vulnerabilities if (as is often the case) the directory in use is writable by other users (as is the case by `/tmp`). In that case, a hostile user with write to the same directory can create a symlink to a location which they don't have access to but the account running the script _does_, and thereby trigger overwrite of an arbitrary file which the latter account owns. – Charles Duffy Apr 27 '15 at 19:24
  • This is also enough of a FAQ to be in the BashPitfalls list: http://mywiki.wooledge.org/BashPitfalls#cat_file_.7C_sed_s.2Ffoo.2Fbar.2F_.3E_file – Charles Duffy Apr 27 '15 at 19:28

3 Answers3

0

You can write this:

sed 's/A/B/g' tmp.txt > tmp2.txt
mv tmp2.txt tmp.txt

The first line writes the contents of the file, with the relevant string replacement, to a new file. The second line moves the new file to the location of the old file, overwriting the old file.

La-comadreja
  • 5,627
  • 11
  • 36
  • 64
0

To change a file in place, use sed -i:

$ echo "AAA" > tmp.txt
$ sed -i 's/A/B/g' tmp.txt
$ cat tmp.txt
BBB

The above uses GNU sed syntax. If you are using Mac OSX (BSD), use:

sed -i '' 's/A/B/g' tmp.txt

Discussion

From the question, consider this line of code:

cat tmp.txt | sed 's/A/B/g' > tmp.txt

cat tmp.txt attempts to read from tmp.txt. When the shell sees > tmp.txt, however, it truncates tmp.txt to an empty file in preparation for input. The result of something like this is not reliable.

sed -i by contrast was explicitly designed to handle this situation. It completely avoids the conflict.

If you like, sed -i can create a back-up of the original file. With GNU sed, use:

sed -i.bak 's/A/B/g' tmp.txt

With BSD (Mac OSX) sed, add a space:

sed -i .bak 's/A/B/g' tmp.txt
John1024
  • 109,961
  • 14
  • 137
  • 171
0

Why not prefer the 'in-place' edit to a cat or rename ?

sed 's/A/B/g' -i tmp.txt
Balmipour
  • 2,985
  • 1
  • 24
  • 28