Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
940 views
in Technique[技术] by (71.8m points)

bash - Why piping to the same file doesn't work on some platforms?

In cygwin, the following code works fine

$ cat junk
bat
bat
bat

$ cat junk | sort -k1,1 |tr 'b' 'z' > junk

$ cat junk
zat
zat
zat

But in the linux shell(GNU/Linux), it seems that overwriting doesn't work

[41] othershell: cat junk
cat
cat
cat
[42] othershell: cat junk |sort -k1,1 |tr 'c' 'z'
zat
zat
zat
[43] othershell: cat junk |sort -k1,1 |tr 'c' 'z' > junk
[44] othershell: cat junk

Both environments run BASH.

I am asking this because sometimes after I do text manipulation, because of this caveat, I am forced to make the tmp file. But I know in Perl, you can give "i" flag to overwrite the original file after some operations/manipulations. I just want to ask if there is any foolproof method in unix pipeline to overwrite the file that I am not aware of.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Four main points here:

  1. "Useless use of cat." Don't do that.
  2. You're not actually sorting anything with sort. Don't do that.
  3. Your pipeline doesn't say what you think it does. Don't do that.
  4. You're trying to over-write a file in-place while reading from it. Don't do that.

One of the reasons you are getting inconsistent behavior is that you are piping to a process that has redirection, rather than redirecting the output of the pipeline as a whole. The difference is subtle, but important.

What you want is to create a compound command with Command Grouping, so that you can redirect the input and output of the whole pipeline. In your case, this should work properly:

{ sort -k1,1 | tr 'c' 'z'; } < junk > sorted_junk

Please note that without anything to sort, you might as well skip the sort command too. Then your command can be run without the need for command grouping:

tr 'c' 'z' < junk > sorted_junk

Keep redirections and pipelines as simple as possible. It makes debugging your scripts much easier.

However, if you still want to abuse the pipeline for some reason, you could use the sponge utility from the moreutils package. The man page says:

sponge reads standard input and writes it out to the specified file. Unlike a shell redirect, sponge soaks up all its input before opening the output file. This allows constricting pipelines that read from and write to the same file.

So, your original command line can be re-written like this:

cat junk | sort -k1,1 | tr 'c' 'z' | sponge junk

and since junk will not be overwritten until sponge receives EOF from the pipeline, you will get the results you were expecting.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...