Why can globally installed NPM packages such as gulp, webpack, etc. be called from the command line from any folder like shell commands (or programs) such as mkdir and copy? What does it have to do with environment variables? What is a symbolic link?

Before explaining these issues, it’s easier to understand file execution on Linux

◎ How Linux files are executed

When you’re first introduced to the shell, it’s common to mention the different ways shell script files can be executed. Start by creating a shell script file hello.sh with a simple command

echo "hello world"
Copy the code
  • One way is to run the shell interpreter directly, taking the script name as an argument to the interpreter command
/bin/sh hello.sh 
# or /bin/zsh hello.sh or zsh hello.sh
hello world
Copy the code

/bin/sh and /bin/zsh are non-shell types supported by the system. Using ZSH and ZSH directly is equivalent, because shell finds /bin/sh and /bin/zsh according to the environment variable ($PATH)

  • Another way, now hello. Sh makes a small change
#! /bin/zsh
echo "hello world"
Copy the code

The hello.sh file will be executed using hello.sh. Otherwise, an error message “Permission denied” will be reported. Once you have execute permission, you can do this

>> ./hello.sh   # ro /path/to/hello.sh
hello world
Copy the code

In fact, it is still possible to use the /bin/sh hello.sh form. The question is, why #! /bin/zsh./hello.sh #! The /bin/zsh line has a term called Shebang(or Hashbang), which is the standard starting line for shell scripts, #! This is followed by specifying the absolute path to the interpreter. It is parsed by the shell program to tell the shell program to use #! The interpreter specified in the following path executes the file, taking the current file as an argument to the interpreter. Sh is equivalent to /bin/zsh hellow.sh (or /bin/zsh $PWD/hello.sh).

◎ Execute the js file

Shell scripts are executed in the same way as JS files and other files, as long as the corresponding interpreter (that is, program commands) is specified. Such as a simple node file, hello.js

console.log('hello world! ')
Copy the code

You can do this (node is already installed)

>> node hello.js
hello world! 
# or 
>> /usr/local/bin/node hello.js 
The path of the node command may vary depending on the system
hello world!
Copy the code

You can also use Shebang to add a line to hello.js that tells the shell program to execute the document with the Node interpreter and add execution permissions to the file

#! /usr/local/bin/node
console.log('hello world! ')

// The first line can do the same
/ / #! /usr/bin/env node
// The difference is that the Node interpreter that appears first in the environment variable is used
Copy the code

I can do this

>> ./hello.js # or $PWD/hello.js
hello world!
Copy the code

/hello.sh (./hello.js) can be used to execute files directly, but can hello.sh (hello.js) be used to execute files directly? Of course you can.

One way to do this is to either move hello. Js from the current directory to any directory of the current environment variable (use echo $PATH to view the currently configured environment variable) or add the current directory to the environment variable, which can be executed directly with test.js. Because the shell can retrieve the test.js file from the environment variables directory, it can be used not only directly in the current directory, but also in any other directory, just like any other command. Of course, this is not recommended, and it’s not good.

A better approach is to use Symbolic links.

◎ Symbolic link with package.json bin field

A Symbolic link, or soft link, is a special file that contains a reference to another file or directory in the form of an absolute or relative path, similar to a shortcut. Continue with hello.js as an example

As mentioned above, one way to execute directly with the name hello.js is to have the file in a directory that the shell program can retrieve through environment variables; The other is to use the ln command to create a symbolic link to a file

  • Creating symbolic links
# create symbolic command syntax, -s to create symbolic links
# ln -s /path/to/file /path/to/symbolic 
ln -s $PWD/hello.js /usr/local/bin/hello.js
Copy the code

The symbolic link file is placed under the environment variable path, so that it can be executed in any directory of the current user using the hello.js command, of course, if the symbolic link is not under the environment variable, the execution is the same./hello.js. The name of a symbolic link is arbitrary, as long as it is not the same as an existing symbolic link

Create a global symbolic link
>> hello.js
hello world!
# Change to something else
>> ln -s $PWD/hello.js /usr/local/bin/hello
>> hello
hello world!
Copy the code
  • Deleting a symbolic link You can delete a symbolic link in the same way as deleting a file, except that the symbolic file is deleted without affecting the original file
rm /usr/local/bin/hello
Copy the code
  • Packge. json bin field

    With the above concepts related to symbolic links, file execution, and environment variables. The execution of NPM package commands is well understood. NPM packages installed globally. NPM will be installed in the/usr/local/bin/The directory (typically in an environment variable) creates the symbolic value specified by the bin field, such as package.json where bin is configured as
{ "bin" : { "myapp" : "./cli.js"}}Copy the code

The /usr/local/bin/myapp symlink is created to point to the executable cli.js. If installed locally, symbolic links will be created under node_modules/.bin/myapp in the project (this can only be done under node_modules/.bin via./myapp). Myapp is a cli.js command, and Shebang, #! /use/bin/env node to specify the interpreter as node

Reference:

  • Shebang
  • Wikipedia – Symbolic links
  • stackoverflow – about shebang
  • npm-package.json
  • How to symlink a file in Linux?