Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nvm uninstall <number> should uninstall all <number>.x.x #1792

Closed
KSXGitHub opened this issue Apr 23, 2018 · 19 comments
Closed

nvm uninstall <number> should uninstall all <number>.x.x #1792

KSXGitHub opened this issue Apr 23, 2018 · 19 comments
Labels
feature requests I want a new feature in nvm!

Comments

@KSXGitHub
Copy link

This is a feature request.

e.g. nvm uninstall 6 should remove all 6.x.x versions

@ljharb ljharb changed the title nvm install <number> should uninstall all <number>.x.x nvm uninstall <number> should uninstall all <number>.x.x Apr 23, 2018
@ljharb ljharb added the feature requests I want a new feature in nvm! label Apr 23, 2018
@ljharb
Copy link
Member

ljharb commented Apr 23, 2018

I think it would be very unintuitive if the version matching logic worked any differently for uninstall than for anything else.

I suppose i could see an --all option or something, that would uninstall all matching versions, but the risk of uninstalling something unintended is high - and you can write your own shell command to do this already.

@KSXGitHub
Copy link
Author

KSXGitHub commented Apr 23, 2018

You could also prompt user a yes/no question

Versions to be removed: <list versions here...>
Proceed? [Y/n]

@ljharb
Copy link
Member

ljharb commented Apr 23, 2018

nvm’s commands all work in a noninteractive terminal, so prompting isn’t a feature we have available.

@KSXGitHub
Copy link
Author

Then --all would be enough.

As for the risk of unintended removal, user has already insisted in removing all matching versions by adding --all option (making an extra effort).

@fl0w
Copy link
Contributor

fl0w commented Apr 25, 2018

Maybe nvm uninstall 6.* as an option? Personally I wouldn't mind being able to further navigate a "version-expression" with e.g. 8.11.*.

@KSXGitHub
Copy link
Author

@fl0w Shell may interpret 6.* and 8.11.* as glob pattern. For example: When you have some files named 6.txt, 8.11.txt, shell might end up execute nvm uninstall --all 6.txt/nvm uninstall --all 8.11.txt instead.

@ljharb
Copy link
Member

ljharb commented Apr 25, 2018

Yes, asterisks will likely never be part of nvm because of the inherent risk with shell globs.

@jcsahnwaldt
Copy link
Contributor

A workaround is to simply delete the nvm directories for all these versions. For example, to delete all v6* versions:

find ~/.nvm/versions/node -mindepth 1 -maxdepth 1 -name 'v6*' -exec rm -fr {} \;

This may leave parts of your nvm configuration inconsistent though. I tried it and noticed no real problems, but maybe @ljharb can comment on whether this approach is reasonable.

@ljharb
Copy link
Member

ljharb commented Feb 23, 2019

@jcsahnwaldt please don’t do that; a better approach would be to use nvm ls 6 and run nvm uninstall for each of them.

@jcsahnwaldt
Copy link
Contributor

I'd prefer that myself, but I failed to write a script that works correctly. With which awk / sed / grep / etc. commands would you parse the output of nvm ls --no-colors 6?

@ljharb
Copy link
Member

ljharb commented Feb 23, 2019

I'd probably start with nvm ls --no-colors | egrep -o 'v\d+.\d+.\d+'? nvm ls --no-colors | egrep -o 'v6.\d+.\d+' | uniq seems to filter it down to v6 versions pretty well.

@jcsahnwaldt
Copy link
Contributor

Prints v6.16.0 on my machine, although I don't have any v6 version installed. It picks up the version of the lts/boron alias.

In some cases, it may be useful to also get versions that are not installed, but I guess in most cases, the user will only want the installed versions. We would probably have to have another grep in the pipeline.

(In the case of nvm uninstall, it doesn't matter much: nvm uninstall v6.16.0 on my machine prints an error message, but return code is 0.)

@ljharb
Copy link
Member

ljharb commented Feb 23, 2019

Ah, that's true.

You could use nvm_ls (the internal function) but it's ideal not to have to rely on that. I suppose nvm ls --no-alias would also be useful.

@jcsahnwaldt
Copy link
Contributor

jcsahnwaldt commented Feb 23, 2019

Yes, nvm_ls is exactly what I'm looking for! :-) But I understand that one shouldn't rely on it.

Yeah, I think something like --no-alias would definitely be useful. See #2005.

One little thing: When I use a pattern that matches no installed version, nvm_ls prints N/A (to stdout) and returns 3. I guess for some scripts it might be better if nvm_ls simply did nothing in this case. Like ls on an empty directory. On the other hand, ls foo also signals an error when foo doesn't exist, so I guess nvm_ls should print N/A to stderr and return an error code.

@ljharb
Copy link
Member

ljharb commented Feb 23, 2019

nvm_ls is an internal function, so I don't think it's that important what conventions it follows; I'm more interested in that argument for the external API.

@InNoobWeTrust
Copy link

InNoobWeTrust commented Feb 9, 2020

I'd probably start with nvm ls --no-colors | egrep -o 'v\d+.\d+.\d+'? nvm ls --no-colors | egrep -o 'v6.\d+.\d+' | uniq seems to filter it down to v6 versions pretty well.

I want to uninstall stale installed node versions from my machine so I try to list first with (note that stale version lines do not contain "->" as I see the pattern):

❯ nvm ls --no-colors | grep -o '^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]*' | tr -d '[[:blank:]]v'
13.2.0
13.6.0

But if I chain with xargs later, don't know why but it results in weird error:

❯ nvm ls --no-colors | grep -o '^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]*' | tr -d '[[:blank:]]v' | xargs -I % nvm uninstall %
xargs: nvm: No such file or directory

Hope that nvm will have a way to support uninstalling stale versions. Highly voted for this issue to be resolved soon...

@nnmrts
Copy link

nnmrts commented Feb 2, 2021

After some research and tinkering, I found this "solution", inspired by @InNoobWeTrust's approach:

nvm ls --no-colors | grep -o '^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]*' | tr -d '[[:blank:]]v' | xargs -p -I % sh -c ". ~/.nvm/nvm.sh && nvm uninstall %"

CLICK HERE for a short and long explanation of everything this command does for all the people that are mainly JavaScript devs and don't know a lot about the shell like me

The | character here passes the output from the previous command to the input of the next one, just saying.

Short: Lists all node versions, takes all node versions expect the one you're using right now and uninstalls them.

Long:

  1. nvm ls lists all your node versions installed by nvm PLUS aliases for specific node versions, installed or not.
    1. The --no-colors removes all the funky colors from the output so the next command (it also adds asterisks (*) after some version numbers to indicate things previously indicated by color)
    2. It outputs something like this (the -> indicates what version you're currently using):
               v8.0.0 *
              v10.0.0 *
              v12.0.0 *
              v14.0.0 *
              v15.5.1 *
              v15.6.0 *
      ->      v15.7.0 *
               system *
      default -> node (-> v15.7.0 *)
      iojs -> N/A (default)
      node -> stable (-> v15.7.0 *) (default)
      stable -> 15.7 (-> v15.7.0 *) (default)
      unstable -> N/A (default)
      lts/* -> lts/fermium (-> N/A)
      lts/argon -> v4.9.1 (-> N/A)
      
  2. grep outputs lines of your input that match one or more specific patterns
    1. The -o flag is added to only print the actual parts of lines that got matched instead of the whole lines.
    2. The pattern ^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]* looks familiar if you know RegEx from JavaScript, except for the [[:blank:]] part. I don't even want to get into the mess of different equally confusing RegEx implementations, just accept that [[:blank:]] here would translate to [ \t] in JavaScript RegEx. So the whole command here basically translates to this in JavaScript:
      [...string.matchAll(/^[ \t]*v[0-9]*\.[0-9]*\.[0-9]*/gm)].map(match => match[0])
    3. It outputs this:
               v8.0.0
              v10.0.0
              v12.0.0
              v14.0.0
              v15.5.1
              v15.6.0
      
  3. tr "translates" or "deletes" characters from your input and outputs the result. It basically replaces single characters in a string, just like string.replace("a", "b") in JavaScript would do.
    1. The -d flag is added to just delete characters instead of "translating" them. So basically replacing with an empty string, like this in JavaScript:
      string.replace("a", "");
    2. The pattern [[:blank:]]v is there to temove all spaces, tabs and occurrences of v, so equivalent to this JavaScript:
      string.replace(/[ \tv]/g, "");
    3. This will be the output:
      8.0.0
      10.0.0
      12.0.0
      14.0.0
      15.5.1
      15.6.0
      
    4. Together, these two (grep and tr) commands translate to something like this in JavaScript:
      [...string.matchAll(/(?<=^[ \t]*v)[0-9]*\.[0-9]*\.[0-9]*/gm)].map(match => match[0])
      Explore this regex here on regexr.
  4. xargs builds commands by splitting the input at every occurrence of whitespace and reading it as an array of inputs, so you can for example pass a list of filenames to a command that only accepts one file per execution, by calling it consecutively with the respective filename. xargs basically automates the process of copy-pasting your last command just to replace a small part of it and run it again over and over.
    1. The -p flag shows you the next command it would execute and basically asks you if you really want it to run. Only typing y and then pressing allows it to run.
    2. The -I flag accepts a string as argument, and tells xargs to replace that string with whatever the input is in the command. % is used here as the replacement string.
    3. Normally we would then expect xargs -p -I % nvm uninstall % to just work, but apparently xargs doesn't execute commands like they would behave in your shell, I guess it opens a new isolated shell? Also nvm isn't like most other commands in your shell, it's actually a shell script. Anyways, you have to invoke a new shell inside xargs using sh, then add the -c flag, which interprets following strings as commands. These commands are then first . ~/.nvm/nvm.sh to source that shell script and then finally nvm uninstall %, connected by && to execute them consecutively.

EDIT: Oh and btw, yeah, I would totally love a builtin way to do this, both what the OP asked for and uninstalling all stale versions or at least a range of versions.

@InNoobWeTrust
Copy link

InNoobWeTrust commented Feb 2, 2021

After some research and tinkering, I found this "solution", inspired by @InNoobWeTrust's approach:

nvm ls --no-colors | grep -o '^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]*' | tr -d '[[:blank:]]v' | xargs -p -I % sh -c ". ~/.nvm/nvm.sh && nvm uninstall %"

CLICK HERE for a short and long explanation of everything this command does for all the people that are mainly JavaScript devs and don't know a lot about the shell like me
EDIT: Oh and btw, yeah, I would totally love a builtin way to do this, both what the OP asked for and uninstalling all stale versions or at least a range of versions.

Didn't work on my machine, sadly, still stuck at nvm uninstall...

Edit: The '-p' option seems to cause problem on my machine, after removing that option, everything work beautifully.

nvm ls --no-colors | grep -o '^[[:blank:]]*v[0-9]*.[0-9]*.[0-9]*' | tr -d '[[:blank:]]v' | xargs -I % sh -c ". ~/.nvm/nvm.sh && nvm uninstall %"

@ljharb
Copy link
Member

ljharb commented Feb 2, 2021

for v in $(nvm_ls 6); do echo nvm uninstall $v; done

seems like this works just fine; you can remove the echo to actually do the uninstallation.

@ljharb ljharb closed this as completed Sep 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature requests I want a new feature in nvm!
Projects
None yet
Development

No branches or pull requests

6 participants