nvm icon indicating copy to clipboard operation
nvm copied to clipboard

suggestion to make nvm install easier

Open indynov opened this issue 2 years ago • 32 comments

Hello, I noticed that when you install nvm you have to restart the shell / ssh session to get it to work or run exec $SHELL to reload the bashrc etc. You can do this by adding "&& exec $SHELL" to the end of the command which will no longer require that and make it easier for new users who may be encountering this issue and "nvm command not found"

Simply replace: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

With: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && exec $SHELL

indynov avatar Apr 18 '22 21:04 indynov

This is a bit of a duplicate of #2794?

That suggestion presumes that $SHELL is set, and it wouldn't actually help, because it'd open a new subshell, and wouldn't affect the shell the command was launched from.

I don't think it's really incumbent on us to make things easier for folks that don't read the literal text printed out to the screen?

ljharb avatar Apr 18 '22 21:04 ljharb

I'm not aware of any place that exec $SHELL wouldn't work but I could be wrong, are you aware of any?

If it didn't work for all people then there is a way to run the command silently so it wouldn't complain if there is any error.

indynov avatar Apr 18 '22 21:04 indynov

I don't think it's worth making the install command more complex for everybody, given that the problem is caused by people not reading the terminal output that tells them what to do.

ljharb avatar Apr 18 '22 21:04 ljharb

I think it would help a lot of people out. For instance, when I first used NVM I copy/pasted the command from Github into terminal and couldn't understand why the nvm command was not working. 99.9% of people are copy/pasting the command so I don't think it will really matter if it's longer in this case. It's also nice to not have to restart the terminal or manually type it in to get nvm to work.

indynov avatar Apr 18 '22 21:04 indynov

using exec $SHELL would launch a subshell though. Which wouldn't be the same shell session the user was pasting into, and would require an additional layer of exiting to get out of. I don't think that's a worthy tradeoff.

ljharb avatar Apr 18 '22 21:04 ljharb

I just discovered the ability to do this by Googling and I'm always learning new things, but I don't understand that.

If you look at the exec command it says it runs in the current session: https://www.geeksforgeeks.org/exec-command-in-linux-with-examples/

$shell is just a variable that gets replaced with the string ./bashrc or whichever file is used on that linux distro.

Where does it say that the "exec" command creates subshell and if so why is that a problem? I'm genuinely curious because I'd like to learn about it.

indynov avatar Apr 18 '22 21:04 indynov

hm, maybe i misunderstood then. I'll give it a try and report back.

ljharb avatar Apr 18 '22 21:04 ljharb

afaik exec replaces the current process with the command that is passed to it. So the shell would be replaced by a new shell, which could remove all that was (temporarily) set in the current shell.

imme-emosol avatar May 11 '22 12:05 imme-emosol

If that is the case you can drop the exec command and just write $SHELL such as wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && $SHELL Now that I've thought about it that would be better since it makes the command shorter. However, if you create a terminal and type "export TEST=abc" setting the TEST environmental variable and then use "exec $SHELL" then $TEST will still be set to "abc" if you use "echo $TEST" so it doesn't seem to unset what was temporarily set in the current shell.

indynov avatar May 11 '22 12:05 indynov

A quick search on what/where SHELL is set didn't give me much info. According to https://unix.stackexchange.com/questions/277944/what-sets-the-shell-environment-variable SHELL is set on login. So when I happen to be using another shell (for instance a terminal window set to use zsh instead of bash), it will not bring me back into a zsh program but will bring me to the shell that is set for my user-account.

Not using exec would start what is called a "subshell", that is: it would execute what is in $SHELL (in case of most sh-programs that would mean that it would take over the handling of stdin & stdout of the initial shell.

An example to see this:

echo $$ ; export A="B" ; alias C="echo 'D'" ; E="F" ; printf '%s\n' '#!/usr/bin/env bash' 'printf ">%s<\n" "E" "${@}" "$A" ; ' | bash && $SHELL
echo $$
echo $A # still available
alias C # no longer available
echo $E # no longer available
# ctrl-D
echo $$
echo $A # still available
alias C # still available
echo $E # still available

An example where SHELL is set to something other than bash:

export SHELL="/bin/zsh"
echo $$ ; export A="B" ; alias C="echo 'D'" ; printf '%s\n' '#!/usr/bin/env bash' 'printf ">%s<\n" "E" "${@}" "$A" ; ' | bash && $SHELL
echo $$
echo $A # still available
alias C # no longer available
# ctrl-D # back in the initial shell-program
echo $$
echo $A # still available
alias C # still available

And the same goes when using exec $SHELL.

So, exported variables do seem to be inherited, however other things are not (for instance local variables, defined alias`es, shell-options like set -o xtrace). All in all these all seem to be rather edge/fringe cases, but any user depending on something will be unpleasantly surprised.

imme-emosol avatar May 11 '22 13:05 imme-emosol

Another issue is that the ~/.profile could not be "sourced" by the shell when it is not invoked as a login-shell.

imme-emosol avatar May 11 '22 14:05 imme-emosol

Would it be possible to use "&& source (location of bash profile etc)" if you knew the location of it which install.sh seems to determine and put it somewhere the command can access it or have install.sh create a new script such as $HOME/nvm_command.sh and then run "&& source $HOME/nvm_command.sh"?

indynov avatar May 11 '22 14:05 indynov

WARNING!: untested code ahead!

Evaluating the line that is added by the install script would probably give direct access to nvm ( eval 'export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" ; [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" ' ).

Looking at it, that line could probably be replaced by export NVM_DIR="${XDG_CONFIG_HOME:+${XDG_CONFIG_HOME%%/}/}${XDG_CONFIG_HOME:-${HOME}/.}nvm")" ; [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

Or even (if extra / is no issue): export NVM_DIR="${XDG_CONFIG_HOME-}/${XDG_CONFIG_HOME:-${HOME}/.}nvm")" ; [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

And without exporting the NVM_DIR (leaving that for nvm.sh to figure out): test -s "${XDG_CONFIG_HOME-}/${XDG_CONFIG_HOME:-${HOME}/.}nvm")" && \. "${_}"

imme-emosol avatar May 11 '22 14:05 imme-emosol

Maybe it would be a good idea to just have install.sh create it's own script such as .nvm_abc in $HOME. Then just have .bashrc etc use "source $HOME/.nvm_abc" and then you could use curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && source $HOME/.nvm_abc

indynov avatar May 11 '22 14:05 indynov

Ah.. yes. I forgot to add the /nvm.sh part.. curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && test -s "${XDG_CONFIG_HOME-}/${XDG_CONFIG_HOME:-${HOME}/.}nvm/nvm.sh")" && \. "${_}"

Or even: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && \. "${XDG_CONFIG_HOME-}/${XDG_CONFIG_HOME:-${HOME}/.}nvm/nvm.sh")"

Since the install.sh probably exits non-zero when the script wasn't installable.

imme-emosol avatar May 11 '22 14:05 imme-emosol

That would be good, even if the command is much longer people just copy/paste it anyway.

indynov avatar May 11 '22 14:05 indynov

Oh, i see it also an option to set NVM_DIR to something else.., so the command would need to be even longer.

`curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && . "${NVM_DIR:-${XDG_CONFIG_HOME-}/${XDG_CONFIG_HOME:-${HOME}/.}nvm}/nvm.sh")"

I don't see all that much benefit in doing this in a "one-liner" though.

imme-emosol avatar May 11 '22 15:05 imme-emosol

In general, the install command for nvm is not something that's worth changing. It lives in a ton of scripts, blogs, and tutorials, and folks are used to only needing to update the version number to get the latest.

ljharb avatar May 11 '22 15:05 ljharb

If you changed the command then it wouldn't break the functionality of where it was posted and people would still be able to update the version number to get the latest, because "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash" would still work. It just adds the && part that allows nvm to be used immediately.

indynov avatar May 11 '22 19:05 indynov

Allowing it to be used immediately would skip what is currently a useful test - that a NEW terminal will properly source nvm.sh. It's a good thing that users are forced to close and reopen their shell, because it verifies that.

ljharb avatar May 11 '22 20:05 ljharb

I don't understand how that is useful, they will still discover later if it isn't properly sourcing which would be a major bug report and never happens unless that platform wasn't supported correctly. I think it would be more useful to be able to use the command without opening a new terminal after installing which often can lead to frustration for new users.

If you use the method I mentioned earlier which adds && source $HOME/.nvm_abc to the end, then if it works immediately that script must be working.

indynov avatar May 11 '22 20:05 indynov

That they will discover it later is the problem. The best time to discover the installation failed is when installing.

ljharb avatar May 11 '22 20:05 ljharb

I don't think that would be a problem, but as an alternative in the install script you can create a file at $HOME/.shell_path and add the location of the shell script that you added NVM to.

Then you can use: curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && source `cat $HOME/.shell_path`

That will source the shell script you are currently adding NVM to.

indynov avatar May 11 '22 21:05 indynov

i'd have to use NVM_DIR, and the knowledge of how to default that is trapped inside the install script, so that wouldn't work either.

I think the root problem here is the need to just accept that you should, in fact, close and open your shell, because that's the most reliable way to have a working nvm installation.

ljharb avatar May 11 '22 22:05 ljharb

It would be possible, inside the install.sh script you have already created a variable called DETECTED_PROFILE which holds the location of the shell script detected. You simply use cat to create the $HOME/.shell_path file with the contents of that location https://www.cyberciti.biz/faq/create-a-file-in-linux-using-the-bash-shell-terminal/

indynov avatar May 11 '22 23:05 indynov

the outer shell (running &&) doesn't have access to any of the variables created inside the install script.

ljharb avatar May 11 '22 23:05 ljharb

That is why you create a file called $HOME/.shell_path which holds the location.

indynov avatar May 11 '22 23:05 indynov

Inside install.sh, you create a file at $HOME/.shell_path using cat that reads for instance "/root/.bashrc"

Then curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash && source `cat $HOME/.shell_path` will work

indynov avatar May 11 '22 23:05 indynov

right, but it's too intrusive to create a file in $HOME - the only thing nvm can politely touch is $NVM_DIR, which defaults to $HOME/.nvm, unless there's XDG variables set, in which case it's something else.

That logic is complex and lives inside install.sh and nvm.sh, and isn't practical to extract into a oneliner.

ljharb avatar May 11 '22 23:05 ljharb

It would work, you would just create and read the file from the location $HOME/.nvm/.shell_path instead.

indynov avatar May 11 '22 23:05 indynov

I can't know it's $HOME/.nvm outside the install script tho.

ljharb avatar May 12 '22 00:05 ljharb

By default NVM installs into $HOME, does it always install into $HOME? If so then that would work.

Otherwise there is also the option of putting it in /tmp

Otherwise there is always the option of cleaning up the file if it does exist inside the .bashrc when the source command runs so it would disappear immediately after being used.

indynov avatar May 12 '22 00:05 indynov