I want to write a init script that should basically run

nvm use v0.11.12 && forever start /srv/index.js

as the user webconfig. nvm is a shell function that is declared in ~webconfig/.nvm/nvm.sh, which is included via source ~/.nvm/nvm.sh in webconfig's .bashrc.

I tried the following:

sudo -H -i -u webconfig nvm

echo "nvm" | sudo -H -i -u webconfig

but they fail with

-bash: nvm: command not found
-bash: line 1: nvm: command not found

When I run sudo -H -i -u webconfig and enter nvm manually in that shell, it works. What am I doing wrong?

  • Where is this function defined? – terdon Apr 21 '14 at 11:03
  • In ~webconfig/.bashrc, there is a line source ~/.nvm/nvm.sh. The function is defined in ~webconfig/.nvm/nvm.sh. – Yogu Apr 21 '14 at 11:08
  • Please edit your question and include those details. The answer depends on exactly where and how the files are sourced. – terdon Apr 21 '14 at 11:08
  • @terdon Did id, although I hoped there was a solution that does not hard-code the path. But it will be ok either way. – Yogu Apr 21 '14 at 11:11

The problem here, as is so often the case, is about the different types of shell:

  • When you open a terminal emulator (gnome-terminal for example), you are executing what is known as an interactive, non-login shell.

  • When you log into your machine from the command line, or run a command such as su username, or sudo -u username, you are running an interactive login shell.

So, depending on what type of shell you have started, a different set of startup files are read. From man bash:

   When  bash is invoked as an interactive login shell, or as a non-inter‐
   active shell with the --login option, it first reads and executes  com‐
   mands  from  the file /etc/profile, if that file exists.  After reading
   that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile,
   in  that order, and reads and executes commands from the first one that
   exists and is readable.  The --noprofile option may be  used  when  the
   shell is started to inhibit this behavior.

In other words, ~/.bashrc is ignored by login shells. Since you are using the -i option to sudo, the startup files for the user's login shell are being read (from man sudo):

 -i, --login
             Run the shell specified by the target user's password data‐
             base entry as a login shell.  This means that login-specific
             resource files such as .profile or .login will be read by the

So, what you can do is

  1. Define the function in the user's ~/.profile or ~/.bash_profile instead. Bear in mind that ~/.profile is ignored if ~/.bash_profile exists. Also keep in mind that ~/.bash_profile is bash-specific so I would use .profile instead, just make sure that ~/.bash_profile does not exist.

  2. Source ~/.nvm/nvm.sh from ~/.profile.

  • This doesn't explain why it works when they do sudo -H -i -u webconfig and run nvm manually. My suspicion is that there already is some code to source .bashrc from .profile, but for some reason it only works in some cases. – Graeme Apr 21 '14 at 11:22
  • Thank you for such a detailed answer! I took the second approach, but kept it also in ~/.bashrc beause otherwise it doesn't work with su webconfig. @Graeme No, .profile and .bash_profile did not exist at all. I tried including .bashrc in .profile, but it did not work. – Yogu Apr 21 '14 at 11:32
  • @Graeme that's probably because when run manually it is being run by the OP's normal user and some of his environment is being kept. When run from the init files, it is run by root so the situation is different. Not sure how, mind you. – terdon Apr 21 '14 at 11:33
  • @terdon: I don't have a "normal user" (it's a development machine), so only webconfig and root were involved. I ran the test lines in the question as root. – Yogu Apr 21 '14 at 11:37

@terdon has already added some good detail on this, so I won't repeat that here. To restate, the cause of the issue is most likely down to the somewhat odd behaviour that bash has when invoked as a login shell in that it doesn't automatically source the .bashrc and instead looks for ~/.bash_profile, ~/.bash_login, or ~/.profile only.

It is common practice to add code to one of these profile files to source the .bashrc. By default I have the following code in ~/.profile:

if [ "$BASH" ]; then
  if [ -f ~/.bashrc ]; then
    . ~/.bashrc


From the comments below, I think the best solution is probably to remove the [ -z "$PS1" ] && return line from the .bashrc and leave everything else as is.

  • Thanks! I now spotted why this did not work in the first place: .bashrc's first line is [ -z "$PS1" ] && return (Ubuntu 12.04). If I remove that line, this solution works. I wonder if it's a good idea... – Yogu Apr 21 '14 at 11:43
  • @Yogu that comes down to personal preference but that is the default on many distros now. Have a look here for the rationale. – terdon Apr 21 '14 at 11:51
  • @Yogu, [ -z "$PS1" ] && return would make the .bashrc exit it it is non-interactive. I think this has probably been the source of the issue and that there has been some code elsewhere that sources the .bashrc. This would explain why the function only works when the shell is run interactively. – Graeme Apr 21 '14 at 11:55
  • I found an explanation why it's that way, so to be safe I better not remove that line. – Yogu Apr 21 '14 at 12:05
  • @Yogu, I don't think that it makes much difference. Usually when you run a script, nothing gets sourced. By adding options for a login or interactive shell, you are basically saying you want things to be sourced. That said, bash does allow for separation between login and interactive which may be useful in this case. – Graeme Apr 21 '14 at 12:33
sudo -Hu webconfig bash -c '. ~/.nvm/nvm.sh && nvm'
  • Yeah, that would probably work, but I don't like having this hard-coded dependency. I might want to move nvm.sh at some time. – Yogu Apr 21 '14 at 19:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.