4

I found the following command in the .profile file while installing node through nvm:

[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" 

I want to know what is the the purpose of [] and && in the following command.

I have not encountered this command syntax before, and I want to understand, what is the following command doing, and what is the syntax called? My guess is that it is creating a soft link, am I right?

EDIT: nvm.sh is not an executable file.

isnvi23h4
  • 143

2 Answers2

11

The [ is a test construct:

$ help [
[: [ arg... ]
    Evaluate conditional expression.

    This is a synonym for the "test" builtin, but the last argument must
    be a literal `]', to match the opening `['.

The -s is one of the available tests, it returns true if the file both exists and is not empty:

$ help test | grep -- -s
      -s FILE        True if file exists and is not empty.

The && is the AND operator. It will run the command on the right only if the command on the left was successful.

Finally, the . is the source command which tells the shell to evaluate any code in the sourced file within the same shell session:

$ help .
.: . filename [arguments]
    Execute commands from a file in the current shell.

    Read and execute commands from FILENAME in the current shell.  The
    entries in $PATH are used to find the directory containing FILENAME.
    If any ARGUMENTS are supplied, they become the positional parameters
    when FILENAME is executed.

So, the command you posted is the same as:

## If the file exists and is not empty
if [ -s "$NVM_DIR/nvm.sh" ]; then
    ## Source it
    . "$NVM_DIR/nvm.sh"
fi
terdon
  • 242,166
  • is sourcing a file, the same as doing .///nvm.sh or executing the file in the bash? and one more catch, nvm.sh is not an executable file. – isnvi23h4 Nov 22 '15 at 14:22
  • 4
    @souparnomajumder no, sourcing doesn't require execution rights and sourcing is different from executing. When you execute a script, it is run in its own subshell and doesn't affect the parent shell. Sourcing is run in the same shell. This is usually used to define variables that are then available in the parent shell. If you execute a file that contains foo=bar, then $foo will not be set after the script exits. If you source it instead, $foo will be set in the parent shell. – terdon Nov 22 '15 at 14:30
  • so instead of . "$NVM_DIR/nvm.sh" , can it be written as source "$NVM_DIR/nvm.sh"? – isnvi23h4 Nov 22 '15 at 19:13
  • 1
    @souparnomajumder yes, source is a synonym of . in bash, but . is more portable. – terdon Nov 22 '15 at 19:18
  • @souparnomajumder occasionally help can be really minimalist: in help . it is said "Execute commands from a file in the current shell" and not "Execute a file with commands". Each word is heavy :) but useful. You can check the bash equivalence with help . and help source that usually return exactly the same text. – Hastur Nov 23 '15 at 09:34
6
cmd1 && cmd2

Runs cmd2 if and only if cmd1 is successful. So cmd1 && cmd2 is successful if both cmd1 and cmd2 are successful which is the idea for a "AND" operator.

Here, cmd1 is the [ command (used to perform tests) and cmd2 is the . command (used to tell the shell to evaluate the code contained in a given file).

For the [ command -s file is to check whether the file file has a non-zero size (and return success if that's the case).

So that command line means: interpret the code in the $NVM_DIR/nvm.sh file if that file is not empty.

If it was empty, that would not be a problem as the . command would just do nothing. That test is still useful though as the [ command will also return false if the file doesn't exist. In Bourne-like shells (not bash though unless in POSIX conformance mode), doing . a-file-that-does-not-exist would cause the shell to exit (and even in bash that would cause an error message to be displayed).

So if that file is not guaranteed to exist (and it's OK if not), it's a good idea to check for that beforehand, and [ -s file ] is one way to do it. It also has the benefit to skip the . command if the file is empty.

Note that [ -s file ] will also return true/success if file is non-empty and not a regular file (like a directory) which would also cause the . command to fail. Another method could have been [ -f file ] which tests that file exists and is a regular file (or symlink to regular file), though that means that one cannot use non-regular files (like fifos) any longer, and one could argue that outputting an error message if the file is a directory would be preferable as that would clearly be a pathological case.

Another approach could be [ -r file ] to check whether the file exists and is readable. But then again, if it's not, you'd probably prefer an error message to tell you of the problem.

There's also [ -e file ] to only check if the file exists (regardless of whether it's empty, regular, readable or not).

  • I have edited the question, to mention that nvm.sh file doesn't have execute permission. in that case will . "$NVM_DIR/nvm.sh" execute the file? – isnvi23h4 Nov 22 '15 at 14:25
  • @souparnomajumder The . command tells the shell to evaluate the code in the content of the file, it does not execute it. . only needs read permission. – Stéphane Chazelas Nov 22 '15 at 14:34
  • Well, technically, the . command only "loads" the "text" from the file at the position where the command . is placed. What happens next is that the shell really starts executing such code as if it was actually placed there in the present running script. That is text (data) transformed into an executable script (code). And implicitly the text is also raised (or demoted) to the permissions of the executor of the present running script. –  Nov 24 '15 at 03:59