I would like to run a find and replace on an HTML file through the command line.
My command looks something like this:
sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html > index.html
When I run this and look at the file afterward, it is empty. It deleted the contents of my file.
When I run this after restoring the file again:
sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html
The stdout
is the contents of the file, and the find and replace has been executed.
Why is this happening?
use sed's -i option, e.g.
To change multiple files (and saving a backup of each as *.bak):
will take all files in directory and replace
|
withx
this is called a “Perl pie” (easy as a pie)When the shell sees
> index.html
in the command line it opens the fileindex.html
for writing, wiping off all its previous contents.To fix this you need to pass the
-i
option tosed
to make the changes inline and create a backup of the original file before it does the changes in-place:Without the .bak the command will fail on some platforms, such as Mac OSX.
Warning: this is a dangerous method! It abuses the i/o buffers in linux and with specific options of buffering it manages to work on small files. It is an interesting curiosity. But don't use it for a real situation!
Besides the
-i
option ofsed
you can use thetee
utility.From
man
:So, the solution would be:
-- here the
tee
is repeated to make sure that the pipeline is buffered. Then all commands in the pipeline are blocked until they get some input to work on. Each command in the pipeline starts when the upstream commands have written 1 buffer of bytes (the size is defined somewhere) to the input of the command. So the last commandtee index.html
, which opens the file for writing and therefore empties it, runs after the upstream pipeline has finished and the output is in the buffer within the pipeline.Most likely the following won't work:
-- it will run both commands of the pipeline at the same time without any blocking. (Without blocking the pipeline should pass the bytes line by line instead of buffer by buffer. Same as when you run
cat | sed s/bar/GGG/
. Without blocking it's more interactive and usually pipelines of just 2 commands run without buffering and blocking. Longer pipelines are buffered.) Thetee index.html
will open the file for writing and it will be emptied. However, if you turn the buffering always on, the second version will work too.An alternative, useful, pattern is:
That has much the same effect, without using the
-i
option, and additionally means that, if the sed script fails for some reason, the input file isn't clobbered. Further, if the edit is successful, there's no backup file left lying around. This sort of idiom can be useful in Makefiles.Quite a lot of seds have the
-i
option, but not all of them; the posix sed is one which doesn't. If you're aiming for portability, therefore, it's best avoided.If you have a link to be added, try this. Search for the URL as above (starting with https and ending with.com here) and replace it with a URL string. I have used a variable
$pub_url
here.s
here means search andg
means global replacement.It works !