Although the original intention of inventing Node.js was primarily to write Web servers, developers found other applications (and not!). ) The purpose of node. Surprisingly, one of these uses is to write a shell script. And that does make sense: node's cross-platform support is already pretty good, since both the front-end and the backend are written in JavaScript, wouldn't it be better if the build system was written in JavaScript, right?
The disadvantage of asynchronous shell scripting
The most commendable library in this use is grunt, which is built on Shelljs. But Shelljs has a tough nut to chew: node forces it to use asynchronous I/O. Although asynchronous I/O is great for Web servers because it must respond at any time, asynchronous I/O does not make much sense for shell scripts that need to be phased out.
So, Shelljs's authors have found an "interesting" solution that allows it to run a shell command and wait for the command to complete. This is roughly the following code:
var child_process = require (' child_process ');
var fs = require (' FS ');
function Execsync (command) {
//run commands in a child shell
child_process.exec (Command + ' 2>&1 1>output && Echo done! > Done ');
Blocking the event loop, knowing that the command was executed while
(!fs.existssync (' done ')) {
//Nothing to do
}/read out
var output = Fs.readfilesync (' output ');
Deletes temporary files.
fs.unlinksync (' output ');
Fs.unlinksync (' done ');
return output;
}
Back to the column page: http://www.bianceng.cnhttp://www.bianceng.cn/webkf/script/
In other words, when the shell executes your command, SHELLJS is still running, and continues to poll the file system to see if it can find the file that indicates that the command has been completed. A bit like a donkey.
This inefficient and ugly solution has stimulated the Node core team and implemented a real solution –node v0.12 should eventually support the synchronous run of child processes. In fact, this feature has been on the road map for a long time – I remember sitting with the retired node maintainer, Felix Geisendoerfer, on the jsconf.eu in 2011, outlining a way to achieve execsync. After more than two years, this feature has now finally appeared on the master branch.
Congratulations, the Shelljs people picked a good thorn! :)
The benefits of synchronization for Shell scripting
The API Spawnsync We just added is similar to its asynchronous gadget, which provides a low-level API that allows you to fully control the settings of the child process. It also returns all the information we can gather: exit code, termination signal, possible startup errors, and the full output of this process. Of course, using Spawnsync in a stream has no meaning-it is synchronized, so the event handler cannot run before the process exits-so all output from the process is buffered into a single instance string or buffer object.
And like the well-known exec (run shell command) and execfile (for running an executable file) method, we add execsync and execfilesync to common situations, which are easier to use than Spawnsync. If you use these api,node you will assume that you care only about the data that the process writes to the STDOUT. If the process or shell returns a Non-zero exit code, node will think that there is an error and exec (Sync) will throw it.
For example, getting the project Git history code is as simple as the following:
var history = Child_process.execsync (' git log ', {encoding: ' UTF8 '});
Process.stdout.write (history);
Now you're probably thinking, "What's taking so long?" "On the surface, starting a subprocess and reading its output looks like a piece of cake." It is also true-if you only care about very common situations. But we don't want to make out the solution just half.
When you need to send input at the same time and read one or more output streams, there are two choices: thread-or loop with events. For example Python implementations, we find them either with an event loop (on a UNIX system platform) or with threads (on Windows). And its implementation is really not a vegetable dish.
We realized in 2011 that node already had a great event-loop library, the LIBUV. All the conditions for achieving this feature are theoretically available. However, there are always problems of large or small, so that it does not really work reliably.
For example, when a subprocess exits, kernel sends a SIGCHLD signal to node to notify it, but when there are multiple event loops, there is a long time LIBUV does not handle the signal correctly. Also, the ability to delete an event loop without leaving a stack trace is only recently added. Node doesn't care, it just exits at some point and lets the OS clean the battlefield. This strategy is not appropriate if we need a temporary event loop and continue to run after it is not needed.
Slowly, over time, all these problems have been solved. So if you try to look at the buffer management, the parameter parsing, the timeout processing, and so on, you'll find that the core of this feature is just an event loop, a tape process, a timer, and a bunch of pipes attached to it.
If you don't care how it works, just look at the documentation and let node give you a jolt of the rich options for controlling the child process. Who wants to fix shelljs now? :)