npx not found (after install with nvm) [solved] lazarus / 32-Linux ---------------------------------------------------------------------- Michael Collier · 2024-06-10 18:05 Hi I installed nodejs via Node Package Manager (npm) and could execute npx command via TProcess. Recently I installed node via Node Version Manager (nvm) and now the TProcess reports it cannot find the command npx. But I can execute the TProcess npx when my application is opened via a terminal window, rather then clicking the Desktop icon. So I suspect the problem is to do with how linux sets environment- variables/paths but I haven't been able to get it working. I noticed nvm modifies my ~./bashrc file, and the lazarus documentation says this will not be loaded https://wiki.freepascal.org/Executing_External_Programs  and .bashrc is not read by non-interactive shells unless you set the BASH_ENV environment variable How do I set the BASH_ENV variable?, should I do this with another TProcess, or somehow configure my existing TProcess  (pipe?) ? Or maybe there is another solution? Maybe modify the contents of a different system file in the same way ~./bashrc has been modified? Note I have this working on windows by setting executable to 'c:\windows\system32\cmd.exe' and using /c with npx as a parameter. I tried similar with linux using '/bin/bash' and 'bin/sh' but no luck. Maybe I'm close to a solution but just not the right combination of modifications.. Thanks, Mike ---------------------------------------------------------------------- Red_prig · 2024-06-10 19:59 You literally need to manually read the data and find the full path to the file, that is, imitate the behavior of the shell (PS: BASH_ENV refers to the '/bin/bash' itself and not the launch RunCommand) ---------------------------------------------------------------------- Red_prig · 2024-06-10 20:02 https://www.freepascal.org/docs- html/rtl/sysutils/getenvironmentvariable.html For example, reading environment variables ---------------------------------------------------------------------- Michael Collier · 2024-06-10 21:38 Thanks for that but I need to set the variable rather than reading it and can't find way to do it. I tried playing with TProces.environment.add() but it never keeps my settings (it reads back the same string as it started with). Temporary solution: I've managed to save my command to a batch file, and executed that instead so now it works (the example below gets npx version number). Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     my_batchfile.Add('#!/bin/bash');    my_batchfile.Add('export PATH="$PATH:/home/user1/.nvm/versions/node/v21.7.3/bin/"');    my_batchfile.Add('npx -v'); But this seems like a hack, is there a more elegant solution I wonder? ---------------------------------------------------------------------- TRon · 2024-06-11 04:05 Does the following code output the correct environment variables in the memo for you or does the nxp command modify things on the fly ? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    Memo1: TMemo;    Panel1: TPanel;    procedure Button1Click(Sender: TObject);  private    procedure info(const s: string);    procedure info(const s: string; const args: array of const);  public  end; var  Form1: TForm1; implementation {$R *.lfm} uses  process;  { TForm1 } procedure TForm1.Button1Click(Sender: TObject);const  cmd_bash  = '/bin/bash';  cmd_test = './test';var  rc_result: string;begin  info('>$ %s -c %s', [cmd_bash, cmd_test]);  if RunCommand(cmd_bash, ['-c', cmd_test], rc_result) then  begin    info('output:');    info(rc_result);  end  else info('error running command %s -c %s', [cmd_bash, cmd_test]);end; procedure TForm1.info(const s: string);begin  Memo1.Append(S);  {$ifdef linux} // fix gtk bug not updating memo scroll  Memo1.selstart := MaxInt;  {$endif}end; procedure TForm1.info(const s: string; const args: array of const);begin  info(format(s, args));end;  (*program test; {$mode objfpc}{$H+} uses  sysutils; var  n : integer;begin  if GetEnvironmentVariableCount > 0 then    for n := 1 to GetEnvironmentVariableCount      do Writeln(n:3,' : ', GetEnvironmentString(n))  else writeln('no environment variables found');end.*)end.  Note that the test program must be compiled with FPC (you can use Lazarus if you know how) and the executable placed in your project directory (or adjust the path to the test program in the example code). You can ofc replace the test program with the nxp command (don't forget to add the -v option and/or full path to cmd_test variable in that case) but you said you already did that (and was not working for you). ---------------------------------------------------------------------- Michael Collier · 2024-06-11 20:23 The output from the test program shows the PATH variable which does include path to where npx is located (or an alias for it). PATH=/home/user1/.nvm/versions/node/v21.7.3/bin:/usr/local/bin:/usr/bi n:/bin:/usr/local/games:/usr/games[ But the memo doesn't show it. PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games I think this will affect anyone wanting to execute a nodejs application from lazarus. But I think the install process for nodejs makes a difference. When I used npm (node package manager) rather than nvm (node version manager) the PATH variable was set correctly for TProcess to use. ---------------------------------------------------------------------- TRon · 2024-06-11 22:51 OK, thank you for the test. In that case try replace Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i cmd_test = './test' with Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i cmd_test = 'source ~/.bashrc; ./test' and see if that helps. It might be that you need to double quote the test command e.g. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i cmd_test = '"source ~/.bashrc; ./test"'  I am not always sure how RunCommand handles that. ---------------------------------------------------------------------- Michael Collier · 2024-06-11 23:24 I executed the code without double quotes (error otherwise). Unfortunately using source didn't make a difference (I kinda thought it would though), the PATH variable is still missing the part that appears in a standard terminal. ---------------------------------------------------------------------- Michael Collier · 2024-06-12 01:48 The following works.. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i cmd_test = 'export PATH="$PATH:/home/user1/.nvm/versions/node/v21.7.3/bin/"; npx -v' ;  It finds npx in the updated PATH and prints out version number.. I tried with a more complex npx example and it worked too.. I'll try more tomorrow.. Looks promising.. Thanks ---------------------------------------------------------------------- TRon · 2024-06-12 07:54 Quote from: Michael Collier on June 12, 2024, 01:24:59 amUnfortunately using source didn't make a difference (I kinda thought it would though), the PATH variable is still missing the part that appears in a standard terminal. Hmz, I would have expected that to work as well. Do you happen to know whether or not the sourcing of bashrc actually took place or that the command was skipped (e.g perhaps the "~" is not expanding properly, e.g then one could try replace with things like 'source "$HOME/bashrc"' (double quotes included as shown)) Quote from: Michael Collier on June 12, 2024, 03:48:05 amThe following works.. Yes, that was indeed the next step to suggest  If that is enough for you then that is ok as well. I would personally prefer for the bashrc to be 'included' in the same environment though as there might be other things that your programs depends on. It is always possible to start in interactive mode with -i bash option but I don't think that is going to make things easier. I have not experimented enough with these things to know for sure. PS: also note that there is a special crafted runcommand named runcommandindir that allows you to set the path to/of the executable and which you could perhaps use and get away with it. It all depends on whether or not the nxp command (or whatever it executes further along in the process) depends on the path to be set/be present. If it works then you could get rid of the whole bin/bash invocation. It is similar in nature as supplying the full path to the nxp command. ---------------------------------------------------------------------- MarkMLl · 2024-06-12 09:45 Quote from: Michael Collier on June 10, 2024, 11:38:08 pmI tried playing with TProces.environment.add() but it never keeps my settings (it reads back the same string as it started with). Remember that that only works in the context of the TProcess you've just created, and the program it is about to run. It does not change anything else in the current program. And- unlike DOS (can't speak for Windows here) unix has no way of changing a shell (environment) variable in a parent process. MarkMLl ---------------------------------------------------------------------- MarkMLl · 2024-06-12 11:32 Quote from: Michael Collier on June 10, 2024, 08:05:05 pmI installed nodejs via Node Package Manager (npm) and could execute npx command via TProcess. Recently I installed node via Node Version Manager (nvm) and now the TProcess reports it cannot find the command npx. But I can execute the TProcess npx when my application is opened via a terminal window, rather then clicking the Desktop icon. So running from a shell session, what does Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i $ which npx  and Code: Text  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i $ file `which npx`  return? Note the usual convention there of a leading $ to indicate that you're running at a shell prompt as an unprivileged user. MarkMLl ---------------------------------------------------------------------- Michael Collier · 2024-06-12 19:52 From a terminal window.. $ which npx /home/user1/.nvm/versions/node/v21.7.3/bin/npx file `which npx` /home/user1/.nvm/versions/node/v21.7.3/bin/npx: symbolic link to ../lib/node_modules/npm/bin/npx-cli.js From the test program.. I tried variations as suggested but no luck. I'm pretty certain the source command is executing because if I deliberatley mis-spell the filename it reports an error (btw changed semicolon to && to get it to report error, also tried pipe | to see what happens). ---------------------------------------------------------------------- MarkMLl · 2024-06-12 20:37 Quote from: Michael Collier on June 12, 2024, 09:52:06 pm/home/user1/.nvm/versions/node/v21.7.3/bin/npx: symbolic link to ../lib/node_modules/npm/bin/npx-cli.js So now look what's at the start of that .js file. It's probably a shebang pointing directly at a binary rather than using the path, and while the kernel ** will honour that you can't rely on a TProcess doing so: it wants the name of an ELF file. ** Years (decades!) ago, I had to contend with somebody who insisted that "the UNIX way" was for the shebang to be processed by the shell, but as a result of that I can say that under at least some circumstances the Linux kernel handles it. MarkMLl ---------------------------------------------------------------------- Michael Collier · 2024-06-12 23:02 QuoteSo now look what's at the start of that .js file. It's probably a shebang pointing directly at a binary rather than using the path, This is the content of the file..shebang line 1.. Code: Javascript  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #!/usr/bin/env noderequire('../lib/cli.js')(process) ---------------------------------------------------------------------- MarkMLl · 2024-06-13 06:59 Fine. And as I said, a TProcess wants the name an ELF file. A Javascript file is not an ELF file, it's text. A shebang is not handled specially, it's text. It might be fixable by asking a TProcess to run /usr/bin/env etc., or you might have to ask it to run the node binary (wherever it is) with a bit of fossicking around with explicitly provided shell (environment) variables, or you might just be able to do it by invoking a shell. That's your problem as a Javascript etc. user, not ours. This is hardly the first time I've seen this sort of thing, in fact it might not even be the first time this week. I suspect that @TRon's sitting there giggling :-) MarkMLl ---------------------------------------------------------------------- Michael Collier · 2024-07-08 16:11 To solve the problem of PATH not containing the path information I needed, I ended up using the -c or /c option for executing terminal commands as a string and inserting the code to modify the PATH variable first.. e.g. Linux cmd_test = 'export PATH="$PATH:/home/user1/.nvm/versions/node/v21.7.3/bin/"; npx -v' ; I had to play around with quotation marks ` and ' and " , but it works on windows / linux and Mac. Thanks, Mike