I'm trying to concatenate a license to the top of my built sources. I'm use GNU Make. In one of my rules, I have:
cat src/license.txt build/3d-tags.js > build/3d-tags.js
But this seems to be causing an infinite loop. When I kill the cat command, I see that build/3d-tags is just the contents of src/license.txt over and over again? What's going on? I would have suspected the two files to be concatenated together, and the resulting ouput from cat to be redirected back into build/3d-tags.js. I'm not looking to append. I'm on OSX, in case the issue is related to GNU cat vs BSD cat.
The shell launches cat
as a subprocess. The output redirection (>
) is inherited by that subprocess as its stdout (file descriptor 1). Because the subprocess has to inherit the file descriptor at its creation, it follows that the shell has to open the output file before launching the subprocess.
So, the shell opens build/3d-tags.js
for writing. Furthermore, since you're not appending (>>
), it truncates the file. Remember, this happens before cat
has even been launched. At this point, it's impossible to achieve what you wanted because the original contents of build/3d-tags.js
is already gone, and cat
hasn't even been launched yet.
Then, when cat
is launched, it opens the files named in its arguments. The timing and order in which it opens them isn't terribly important. It opens them both for reading, of course. It then reads from src/license.txt
and writes to its stdout. This writing goes to build/3d-tags.js
. At this point, it's the only content in that file because it was truncated before.
cat
then reads from build/3d-tags.js
. It finds the content that was just written there, which is what cat previously read from src/license.txt
. It writes that content to the end of the file. It then goes back and tries to read some more. It will, of course, find more to read, because it just wrote more data to the end of the file. It reads this remaining data and writes it to the file. And on and on.
In order for cat
to work as you hoped (even ignoring the shell redirection obliterating the contents of build/3d-tags.js
), it would have to read and keep in memory the entire contents of build/3d-tags.js
, no matter how big it was, so that it could write it after it wrote the contents of src/license.txt
.
Probably the best way to achieve what you want is something like this:
cat src/license.txt build/3d-tags.js > build/3d-tags.js.new && mv build/3d-tags.js.new build/3d-tags.js || rm -f build/3d-tags.js.new
That is: concatenate the two files to a new file; if that succeeds, move the new file to the original file name (replacing the original); if either step fails, remove the temporary "new" file so as to not leave junk around.