Can I include a folder relative to the current directory in PATH in Powershell? - node.js

I run a lot of node projects and often have binaries located in:
.\node_modules\.bin
...relative to the projects folder. I'd like to be able to have PATH always include these directories, if they exist. I don't want to include other directories, just the one relative to the current directory. I'm familiar with
Add-PathVariable from PSCX and other Powershell basics, but how do I include a folder relative to the current dir in PATH?
Edit: as mentioned in the question, already, I expect the path to stay updated as the directory changes. This is not simply asking about how to use pwd.

You can use a relative path in Env:PATH and the binaries found will update dynamically:
Eg:
$env:PATH += ';.\node_modules\.bin'
Or with the PowerShell Community Extensions (PSCX):
Add-PathVariable '.\node_modules\.bin'
Unlike using $(pwd) the . is not immediately resolved to an absolute path, so PATH is always relative to the current working directory.
Testing this:
$ which uuid
C:\Users\username\Documents\myapp\node_modules\.bin\uuid.cmd
Then changing directory, uuid now refers to a program in a different dir:
$ cd ..\blog\
$ which uuid
C:\Users\username\Documents\blog\node_modules\.bin\uuid.cmd
It's also possible to persistently change PATH in the user or system environment:
[Environment]::SetEnvironmentVariable(($env:PATH + ';.'), 'User')
or
[Environment]::SetEnvironmentVariable(($env:PATH + ';.'), 'Machine')
Security note: when entering a command Windows will automatically search all directories in $env:PATH for files with one of the extensions listed in $env:PATHEXT and execute the first match it finds. Depending on where exactly in the search path you placed . that may even supersede system executables.
You may want to take a look at how to use package installed locally in node_modules for alternative approaches.

Related

WSL PATH contains windows directories, How to (elegantly) fix it to exclude only certain ones?

I have been tinkering with WSL2 and found out that when trying to use some software, such as gcloud I bump into an annoying inconvenience.
$ gcloud
/mnt/c/google-cloud-sdk/bin/gcloud: 113: exec: python: not found
This happens because the PATH variable in WSL is set by default to include some windows directories. If we examine it, we can find /mnt/c/google-cloud-sdk/bin/gcloud amongst the set directories:
$ echo $PATH | grep /mnt/c/google-cloud-sdk/bin/
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/mnt/c/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin:/mnt/c/Windows/system32:/mnt/c/Windows:/mnt/c/Windows/System32/Wbem:/mnt/c/Windows/System32/WindowsPowerShell/v1.0/:/mnt/c/Windows/System32/OpenSSH/:/mnt/c/google-cloud-sdk/bin/:/mnt/c/Program Files (x86)/Microsoft SQL Server/150/DTS/Binn/:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/:/mnt/c/WINDOWS/System32/OpenSSH/:/mnt/c/Program Files/Docker/Docker/resources/bin:/mnt/c/ProgramData/DockerDesktop/version-bin:/mnt/c/Users/iyid/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/iyid/AppData/Local/Programs/Microsoft VS Code/bin:/snap/bin
I can understand why there are some windows directories such as /mnt/c/Program Files/Docker/Docker/resources/bin, or /mnt/c/Users/iyid/AppData/Local/Programs/Microsoft VS Code/bin because I use them, but when gcloud is unusable "out-of-the-box", why include it?
So an ugly fix is to just export a new PATH variable that excludes the gcloud directory, and add this to .bashrc within the user & root home directories. Note that when replacing the path variable, I need to manually set double-quotes " around directories with spaces in it, such as "/mnt/c/Users/iyid/AppData/Local/Programs/Microsoft VS Code/bin", which makes the fix even uglier. What If I install some other software and need to include its binaries in the PATH variable? Should I just pile-up path update instructions in my .bashrc? What If I want to use zsh later-on, and I have to switch all the instructions to .zshrc? etc... All of these questions make this solution purely a "workaround" that involves a lot of maintenance overhead.
Is there a more elegant way to fix this issue?

Copying shell file to path

I'm new to WSL and Linux, but I'm trying to follow installation instructions for rhasspy (https://rhasspy.readthedocs.io/en/latest/installation/#windows-subsystem-for-linux-wsl). I have run the make install command successfully and the next step says I should copy rhasspy somewhere in my path but I can't quite figure out what copying to path means.
When installation is finished, copy rhasspy.sh somewhere in your PATH and rename it to rhasspy.
I added it to path but nothing changed so I was wondering if there is something I'm doing wrong. Right now when I run rhasspy on wsl it says rhasspy.sh: command not found. Any help would be really appreciated!
What it says is, put it in some place where the system will look for it when you type its name without full path in the shell.
There is an environment variable PATH that contains all those locations, separated by a :. (Check out echo $PATH.)
So, the author of these instructions leaves it up to you whether...
You want to copy the file to a location of your choice that is already in the PATH, such as /usr/local/bin or ~/bin.
Usually ~/bin is a good choice because it is per-user and doesn't pollute the system.
(Note that the directory ~/bin is added to the PATH by your .profile file only if it exists, so if you don't have this directory yet and create it now, you need to start a new login shell or run . ~/.profile1 before you can use it.)
- OR -
You want to create a new directory specifically for this application (say for example ~/opt/rhasspy) and append that directory to the PATH variable.
This can be done by adding the line export PATH=$PATH:~/opt/rhasspy to your ~/.profile file. Then, start a new login shell or reload the file using . ~/.profile1 for the changes to take effect.
If the directory in which this file is currently located is OK for you to keep permanently, then you can also just add that directory to the PATH instead of creating a new one.
Note: The PATH always contains directory paths in which the shell will look for executable files. It does not contain the actual file paths!
1: Yes, technically it is "cleaner" to log into a new shell or to run that one export statement manually instead of using . ~/.profile because the latter will apply things a second time that were already done before, so for example it can end up with the same directory in the PATH multiple times in the current session. In most cases that is fine though.
PATH is an environment variable. When you launch env, you see the list of known environment variables on your system.
In order to add something to your PATH variable, you need to take the variable, add the mentioned directory (preceeded by a semi-colon, most probably, as a separator) and store this again as the PATH variable. This can be done as follows (own example):
export PATH=$PATH:/home/this_user
the "PATH" it is referring to in linux is just inside the folder called /usr/bin. when you type a command into the terminal it looks for a program with that name inside the location. im not sure if this is the PATH you are looking for but hope it helps

Copying symbolic links but pointing to same files(copied) but different target base directories

I am trying to create a test environment by updating it with another test environment files. Heres my situation:
I have a directory SRC. It has directories "test_bundles" and "tools_bundles" which has all the required builds. Additionally, SRC has "latest" directory which has files "test_bundle_1", "tools_bundle_1" etc. which points to specific builds within directories "test_bundles" and "tools_bundles", i.e., symbolic links.
Now on a different Linux system, I have DEST directory which is regularly updated with contents of "test_bundles" and "tools_bundles" from the SRC directory. I also want to have "latest" directory in DEST that has same files as in "latest" within SRC directory, however, points to builds (these builds are same) inside "test_bundles" and "tools_bundles" within the DEST.
Note that files change their links to different builds and moreover, new files get added to "latest" within SRC as well. So whenever I do copy operation it should update everything.
I dont know what to call this. Am I trying to copy the SRC "latest" to DEST "latest" files (symbolic links) with links to different parent directory structure.
Note that my script is doing good by updating "test_bundles" and "tools_bundles". I just need a way for "latest" files with the test environment uses different base directories.
If the links in latest are relative there is nothing special that
needs to be done - parent directory names do not matter. – Michał
Politowski
Actually its the opposite, the links are not relative rather the
parent directory structure(tree) is different – user3685188
What Michał Politowski surely meant to suggest is that you should make the symbolic links relative in order to have your problem solved - and that is the most sane solution.

Choosing between multiple executables with same name in Linux

The system I am using has gnuplot installed in /usr/bin. I don't have root, but I needed a newer version of gnuplot, so I installed it to $HOME/usr/bin.
I added $HOME/usr/bin to my path, but it still executes the one in /usr/bin if I just use the gnuplot command. I'd rather not have to specify $HOME/usr/bin/gnuplot every time I have to use it.
How do I tell Linux to use the one in my home directory, and not the one in /usr/bin?
Executables are found in PATH order. You need to prepend ${HOME}/usr/bin to your path, like so:
export PATH="${HOME}/usr/bin:$PATH"
Executables are found in PATH order. Your PATH apparently is set up such that /usr/bin precedes ~/usr/bin/.
Besides modifying the PATH as has been explained, you can also use aliases like this (in BASH)
alias gn=$HOME/usr/bin/gnuplot
then you just run it with
gn
What Bombe says is ok. I would add that you should declare your user specific PATH entries inside your user's bashrc ($HOME/.bashrc), so your PATH settings only apply to your user.

Where to put helper-scripts with GNU autoconf/automake?

I'm working on a project that will be distributed with GNU autoconf/automake, and I have a set of bash scripts which call awk scripts. I would like the bash scripts to end up in the $PATH, but not the awk scripts. How should I insert these into the project? Should they be put in with other binaries?
Also, is there a way to determine the final location of the file after installation? I presume that /usr/local/bin isn't always where the executables end up...
You can just list the scripts that you want to be installed in Makefile.am:
bin_SCRIPTS = foo bar
This will cause foo and bar to be installed during make install. To get the path to their final location, you can use #bindir# in foo.in and let configure build foo for you. For example, in configure.ac:
AC_CONFIG_FILES([foo bar])
and then in foo.in:
#!/bin/sh
prefix=#prefix#
exec_prefix=#exec_prefix#
bindir=#bindir#
echo bindir = $bindir
Keep in mind that the person running configure may specify any of --prefix, --exec_prefix, or
--bindir, and the installation may be redirected with a DESTDIR. Using the technique described here, DESTDIR will not be taken into account and the script will be installed in a location other than the path that it will echo. This is by design, and is the correct behavior, as usually a DESTDIR installation is used to create a tarball that will eventually be unpacked into the filesystem in such a way that the bindir in the script becomes valid.
Add something like this to Makefile.am
scriptsdir = $(prefix)/bin
scripts_DATA = awkscript1 awkscript2
In this case it will install awkscript in $(prefix)/bin (you can also use $(bindir)).
Note: Dont forget that the first should be named name + dir (scripts -> scriptsdir) and the second should be name + _DATA (scripts -> scripts_DATA).
Jonathan, in response to your additional question: if you want to replace the value of prefix at the time of build, you will need to:
rename your script 'myscript' to 'myscript.in'
add a rule to configure.ac to generate it at the bottom
use a macro I made called AS_AC_EXPAND
use it like this:
AS_AC_EXPAND(BINDIR, $bindir)
in your 'myscript.in', you can now use #BINDIR# and it will get expanded to the full path where the script will end up being installed.
Note that you shouldn't use PREFIX directly, any of the installation directories can potentially be changed so you really want to use the value passed to configure for bindir and expand that.
If the awk scripts won't go into the main bin directory (prefix/bin), then you need to place them in an appropriate sub-directory - probably of lib but possibly libexec or share (since the awk scripts are probably platform neutral).
Correct: software won't necessarily end up in /usr/local/bin; on my machine, /usr/local/bin is managed by MIS and all the software I install therefore goes under /usr/gnu/. I use: ./configure --prefix=/usr/gnu to get the software installed where I want it.
You can embed the value of PREFIX in the bash scripts -- effectively, you will 'compile' the scripts to include the install location. Be aware of problems during the build testing - you may need to locate the scripts relative to the current directory first and relative to PREFIX later.

Resources