sed command find and replace in file and overwrite

2018-12-31 14:46发布

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?

标签: shell unix sed
13条回答
与风俱净
2楼-- · 2018-12-31 15:05

You should try using the option -i for in-place editing.

查看更多
永恒的永恒
3楼-- · 2018-12-31 15:05

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 of sed you can use the tee utility.

From man:

tee - read from standard input and write to standard output and files

So, the solution would be:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee | tee index.html

-- 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 command tee 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:

sed s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html | tee index.html

-- 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.) The tee 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.

查看更多
只若初见
4楼-- · 2018-12-31 15:08

You can use Vim in Ex mode:

ex -sc '%s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g|x' index.html
  1. % select all lines

  2. x save and close

查看更多
大哥的爱人
5楼-- · 2018-12-31 15:09

With all due respect to the above correct answers, it's always a good idea to "dry run" scripts like that, so that you don't corrupt your file and have to start again from scratch.

Just get your script to spill the output to the command line instead of writing it to the file, for example, like that:

sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g index.html

OR

less index.html | sed -e s/STRING_TO_REPLACE/STRING_TO_REPLACE_IT/g 

This way you can see and check the output of the command without getting your file truncated.

查看更多
倾城一夜雪
6楼-- · 2018-12-31 15:13

An alternative, useful, pattern is:

sed -e 'script script' index.html > index.html.tmp && mv index.html.tmp index.html

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.

查看更多
公子世无双
7楼-- · 2018-12-31 15:13

To change multiple files (and saving a backup of each as *.bak):

perl -p -i -e "s/\|/x/g" *  

will take all files in directory and replace | with x this is called a “Perl pie” (easy as a pie)

查看更多
登录 后发表回答