Can fish, zsh and bash import the same configuration file? - linux

I've recently been trying to switch from bash to zsh, or fish.
I have some alisa and PATH settings, but I don't want to manually copy them to zshrc or config.fish.
I tried writing them in a single file and using source ~/.myshrc to use them.
The alisa statement can be sourced normally. But when sourcing PATH in fish shell I got an error:
In fish, please use {$JAVA_HOME}. export
CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
I know that fish and bash syntax are incompatible.
So is there a general syntax that allows me to modify the PATH in myshrc and then all three shells can use it?
myshrc file like this:
# alias
alias apts="apt search"
alias sf="aptitude search -F '%c %p %d %D %I'"
alias apti="sudo aptitude install"
alias aptup="sudo aptitude update"
alias aptgr="sudo aptitude upgrade"
alias aptpu="sudo aptitude purge"
# transset xterm
transset -t -a 0.95 >> /dev/null 2>&1
# set npm path
export PATH=~/.npm-global/bin:$PATH
# set java path
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export JRE_HOME=${java_home}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
# set android path
export ANDROID_HOME="/home/moly/Launcher/AndroidSDK/"
export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"

Short answer:
If you embrace the "fish way" of doing things, you can get your startup config down to just one line ...
Explanation:
First off, just to pass along some random knowledge (that I originally learned here in a related answer on Stack Overflow), code that is written in such a way that it runs the same way in two different languages is known as a polyglot. It's typically considered a "puzzle" of sorts, rather than a practical implementation. It's really not advised as a solution to your question.
Next up, my general recommendation is to embrace the fish way of doing things as much as possible. Sure, there are times you'll need to drop back to POSIX for compatibility reasons, but fish syntax is (IMHO) much cleaner.
I especially like that, under fish, my config files are almost empty. It's just not necessary under fish to have many of the items that you have in your bash startup.
Let's break down the four types of items you have in your startup config:
Aliases
$PATH changes
Other exported variables
Other Command(s)
Aliases
I recommend against putting aliases in your fish startup files. It's just not necessary. Instead, define the alias once at the commandline and use the -s (save) option. In your case:
alias -s apts="apt search"
alias -s sf="aptitude search -F '%c %p %d %D %I'"
alias -s apti="sudo aptitude install"
alias -s aptup="sudo aptitude update"
alias -s aptgr="sudo aptitude upgrade"
alias -s aptpu="sudo aptitude purge"
Your aliases will be "permanently" defined at that point in all fish instances, with the advantage that they are "lazy loaded" (only loaded into memory when you run them the first time) rather than being loaded at all times.
$PATH modifications
Likewise, fish provides a handy helper with which you can add to your path once and have it take effect in all instances (both currently running and future).
fish_add_path "~/.npm-global/bin" "/usr/lib/jvm/java-17-openjdk-amd64/bin" "~/Launcher/AndroidSDK/tools" "~/Launcher/AndroidSDK/platform-tools"
This prepends those paths to the built-in fish universal variable$fish_user_paths which is automatically prepended to the system path. Note that, in your bash rc, you have appended the Android SDK paths, but that's probably not necessary. Typically, you will want to prepend user paths ahead of the system path.
Other exported variables
This part is a little contentious, because fish universal variables can have unintended side-effects when exported to other processes.
On one hand, one of the fish maintainers (#faho) mentions in this answer that:
In general, what you want is to just put the set -gx into ~/.config/fish/config.fish. That's fish's configuration file.
Fish also has "universal" variables, which are stored persistently, but they interact awkwardly with exporting so I wouldn't recommend it.
I've had others (see comments on this answer) who have worked extensively with fish advise against it as well.
On the other hand, 196 upvotes on this answer seem to indicate that folks like universals for this purpose. That's not to say that the majority is correct -- I've seen some really bad answers with a lot of upvotes.
However, I personally like using them to simplify my config files.
If you so choose, you can:
set -Ux JAVA_HOME "/usr/lib/jvm/java-17-openjdk-amd64"
set -Ux JRE_HOME "$JAVA_HOME/jre"
set -Ux --path CLASSPATH ".:$JAVA_HOME/lib:$JRE_HOME/lib"
set -Ux ANDROID_HOME "/home/moly/Launcher/AndroidSDK/"
Again, as these are universal variables, they only need to be set once on the commandline. You can then remove those statements from your fish startup.
Understanding universal/global variable shadowing:
Primarily, if a global variable is set (for instance, by the parent bash process from which you launch fish), then it will override a universal variable of the same name.
For instance (pathologic example, but can easily occur in the real world, especially if you aren't aware of the potential):
# Start in Fish
-> set -Ux JAVA_HOME "/usr/lib/jvm/java-17-openjdk-amd64"
-> set --show JAVA_HOME
$JAVA_HOME: set in universal scope, exported, with 1 elements
$JAVA_HOME[1]: |/usr/lib/jvm/java-17-openjdk-amd64|
-> bash
-> export JAVA_HOME="~/.local/share/jvm/java-17-openjdk-amd64"
-> fish
-> set --show JAVA_HOME
$JAVA_HOME: set in global scope, exported, with 1 elements
$JAVA_HOME[1]: |~/.local/lib/jvm/java-17-openjdk-amd64|
$JAVA_HOME: set in universal scope, exported, with 1 elements
$JAVA_HOME[1]: |/usr/lib/jvm/java-17-openjdk-amd64|
In that particular fish session, the Universal variable will be shadowed by the global one that was exported in bash.
See this Github issue of many that demonstrate the real world potential for problems.
Again, with that in mind, I personally believe the benefits of exported universal variables outweigh the risks, but I wanted to be sure to present both viewpoints.
Other commands
If you've followed along so far (and haven't fallen asleep -- I known I over-explain ...), then you'll realize that there's (potentially) only the one command that must remain in your fish startup:
transset -t -a 0.95 >> /dev/null 2>&1
Even then, I have a suggestion. Fish automatically sources any .fish file in ~/.config/fish/conf.d. I like to keep my config "modularized", so that I can tell at-a-glance what is being loaded at startup.
I would just create:
~/.config/fish/conf.d/xterm.fish:
transset -t -a 0.95 >> /dev/null 2>&1
At that point, you are running like I do -- no fish.config at all! (Well, other than the fact that the newer fish versions irritatingly, automatically create a completely unnecessary empty one if it doesn't exist ...)

One hacky way to use your existing configuration while trying to transition to a new shell like fish is simply to end your ~/.bashrc with fish, like so:
# alias
alias apts="apt search"
alias sf="aptitude search -F '%c %p %d %D %I'"
alias apti="sudo aptitude install"
alias aptup="sudo aptitude update"
alias aptgr="sudo aptitude upgrade"
alias aptpu="sudo aptitude purge"
# transset xterm
transset -t -a 0.95 >> /dev/null 2>&1
# set npm path
export PATH=~/.npm-global/bin:$PATH
# set java path
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export JRE_HOME=${java_home}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
# set android path
export ANDROID_HOME="/home/moly/Launcher/AndroidSDK/"
export PATH="${PATH}:${ANDROID_HOME}tools/:${ANDROID_HOME}platform-tools/"
# start fish
exec fish
I don't think this is a very good solution, but this is the only one I know of to make fish inherit your bash environment without rewriting/translating your bashrc. While this is probably bad practice and could cause other sorts of issues, on the short term this could be what you need if you just want to try out fish in known territory with your aliases and PATH without spending time writing a configuration file perhaps for nothing.
I have no experience with zsh and don't know if it can equally inherit the bash environment like that, but wouldn't be surprised if it can.

Related

Android Studio - cmake - access environement variable?

This question is specific to using cmake as part of Android Studio build process.
I'm using Android Studio 2.2 preview 7 on linux (ubuntu)
Inside the CMakeLists.txt I am able to access the Android NDK path using: ${ANDROID_NDK}
But how can I access:
Any environment variable ?
If not possible, at least the Android SDK path ?
I already tried to used $ENV{name_of_the_environment_variable_here} but it's always empty, even if the environment variable exist. I guess that when gradle invoke cmake it "hide" the env var somehow.
I don't think you can use $ENV, it's just an example of a variable because they're environment variables. However, you should be able to type env and hit enter for a list of the variables you currently have set. Then, the ones you see in the list, you can invoke by typing $VARIABLE_NAME, using a command before them to get them to do something. E.g. echo $VARIABLE will echo your variable to stdout.
I'm not sure how $ANDROID_SDK was set, if it was part of an install process, etc. but generally you would set user environment variables in .profile, .bash_profile or .bashrc configuration files. These files are read by the shell in that order. System-wide variables are set in /etc/environment, /etc/profile, and /etc/bash.bashrc, but you probably don't want to mess with those (most distros encourage making ancillary additions in /etc/profile.local, but that's a story for another answer).
It doesn't particularly matter which one of these files you use, unless what you're trying to do interacts with the order in which they are loaded. Generally, I look for where the variables have been set by either the OS or other stuff I've added and put them near those. You can find where environment variables are set by typing:
% for i in .profile .bash_profile .bashrc; do grep -H PATH $HOME/$i; done
(% is the prompt, don't type %)
.. and this will loop through the 3 files and show you if a user $PATH is set in any of them.
Bash uses the export ENV command as opposed to set ENV, which is from the original sh, which AFAIK is only default on FreeBSD and derivatives like pfSense anymore. Almost all other OS use Bash by default, except MacOS which recently moved to zsh and also uses export, and OpenBSD which uses ksh (nobody uses OpenBSD).
If you want to verify which shell you are using, type echo $SHELL, or echo $0 and hit enter, and it should let you know.
You can add the environment variable ephemerally by typing this command in your bash terminal and pressing enter:
% export ANDROID_SDK_ROOT=/home/username/AndroidSDK
To be clear, this is an example path, so it'd be best to use the actual path in which your android SDK files reside. However, this example was a default install location Android Studio tried to use when I installed it recently, so if you're not sure where they are, it's probably a good place to check.
To have a more permanent setting of your environment variable, open a text editor and add the line above to one of the configuration files I mentioned in the first paragraph (they'll be in your $HOME folder). Or, you can run this from the prompt and it'll add it to your file automatically:
% echo 'export ANDROID_SDK_ROOT=/home/username/AndroidSDK' >> $HOME/.bashrc
Take care to use two angle brackets and not one, as one angle bracket will overwrite the entire file with the single line.
How can I access Any environment variable ?
If you're not sure which folder is $HOME, try typing cd $HOME and hitting enter - that'll take you there. That's how you access environment variables - use a command with the invocation of the variable and it should act as if you had typed out the entire thing.
To access environment variables, type echo $NAME_OF_VARIABLE and it should echo it to the screen. If you want to search your three config files I mentioned in the beginning for where an environment variable is set, you can use grep as I did earlier, just changing the search string for whatever you're looking for. E.g. (while in $HOME):
% grep SDK_ROOT .bashrc .profile .bash_profile
Or you can type env to list all the currently set variables and filter them by piping the output to the grep command:
% env | grep SDK
If you want to just list all of the set variables and root around the entire thing, just type env instead of piping it to grep (grep's a filter).
Lastly, I'll give you an example of my $ANDROID_SDK_ROOT $ANDROID_SDK and $SDK_ROOT variables in my .bashrc - I noticed while installing these tools, they use all three (isn't that fun?):
% grep ANDROID .bashrc
export ANDROID_SDK=$HOME/development/Android/SDK
export ANDROID_SDK_ROOT=$HOME/development/Android/SDK
export PATH=$PATH:$ANDROID_SDK:$JAVA_HOME:$ANDROID_SDK/cmdline-tools/latest/bin:$ANDROID_SDK/build-tools/32.0.0:$ANDROID_SDK/emulator:$ANDROID_SDK/emulator/bin64:$ANDROID_SDK/tools:$ANDROID_SDK/tools/bin:$ANDROID_SDK/extras:$ANDROID_SDK/platform-tools:$HOME/development/AndroidStudio/bin
export ANDROID_STUDIO=$HOME/development/AndroidStudio
% grep SDK_ROOT .bashrc
export SDK_ROOT=$HOME/development/Android/SDK
export ANDROID_SDK_ROOT=$HOME/development/Android/SDK
Hope that answers some questions, sorry it took so long to give you a response.

Set a temporary environment ($PATH)

I may fall into a X-Y problem with this question and I encourage you guys to correct me if I am wrong.
I would like to configure a toolchain environment that can work on different platforms and compiler versions. I initially wrote a long Perl script that generates a configuration Makefile that contain only variables. I wanted to be simple so I did not write anything complex using automake or autoconf. Moreover I wanted the reconfiguration process to be very fast. In my case my own written ./configure does everything in less than a second. I am very happy with that.
However I feel I can use a better approach using environment variables. Instead of writing a Makefile with the specific variables I can set the current shell environment directly. For example:
export cc=gcc
Unfortunately, some variables are already declared in the $PATH. The solution is to add the new $PATH in the front of the other:
export PATH=/new/toolchain/path:$PATH
echo $PATH
/new/toolchain/path:/old/toolchain/path:/usr/bin:/bin...
I feel this is ugly I would like to remove the old path before adding the new one.
To conclude:
Is it better to use the environment instead of custom makefiles to set a build configuration?
How to properly adjust existing environment variables?
When I have several variables to set, I write a wrapper script which I then use as a prefix to the command that I want to modify. That lets me use the prefix either
applying to a single command, such as make, or
initializing a shell, so that subsequent commands use the altered settings.
I use wrappers for
setting compiler options (such as clang, to set the CC variable, making configure scripts "see" it as the chosen compiler),
setting locale variables, to test with POSIX C versus en_US versus en_US.UTF-8, etc.
testing with reduced environments, such as in cron.
Each of the wrappers does what is needed to identify the proper PATH, LD_LIBRARY_PATH, and similar variables.
For example, I wrote this ad hoc script about ten years ago to test with a local build of python:
#!/bin/bash
ver=2.4.2
export TOP=/usr/local/python-$ver
export PATH=$TOP/bin:$PATH
export LD_LIBRARY_PATH=`newpath -n LD_LIBRARY_PATH -bd $TOP/lib $TOP/lib/gcc/i686-pc-linux-gnu/$ver`
if test -d $TOP
then
exec $*
else
echo no $TOP
exit 1
fi
and used it as with-python-2.4.2 myscript.
Some wrappers simply call another script.
For example, I use this wrapper around the configure script to setup variables for cross-compiling:
#!/bin/sh
# $Id: cfg-mingw,v 1.7 2014/09/20 20:49:31 tom Exp $
# configure to cross-compile using mingw32
BUILD_CC=${CC:-gcc}
unset CC
unset CXX
TARGET=`choose-mingw32`
if test -n "$TARGET"
then
PREFIX=
test -d /usr/$TARGET && PREFIX="--prefix=/usr/$TARGET"
cfg-normal \
--with-build-cc=$BUILD_CC \
--host=$TARGET \
--target=$TARGET \
$PREFIX "$#"
else
echo "? cannot find MinGW compiler in path"
exit 1
fi
where choose-mingw32 and cfg-normal are scripts that (a) find the available target name for the cross-compiler and (b) provide additional options to the configure script.
Others may suggest shell aliases or functions. I do not use those for this purpose because my command-line shell is usually tcsh, while I run these commands from (a) other shell scripts, (b) directory editor, or (c) text-editor. Those use the POSIX shell (except of course, for scripts requiring specific features), making aliases or functions of little use.
You can create an individualized environment for a particular command invocation:
VAR1=val1 VAR2=val2 VAR3=val3 make
I find this cleaner than doing:
export VAR1=val1
export VAR2=val2
export VAR3=val3
make
unless you're in a wrapper script and maybe even then as with
VAR1=val1 VAR2=val2 VAR3=val3 make the VAR variables will be whatever they were before the make invocation (including but not limited to unexported and nonexistent).
Long lines is a non-issue, you can always split it across several lines:
VAR1=val1\
VAR2=val2\
VAR3=val3\
make
You can set up environment variables like this for any Unix command.
The shell will all set it up.
Some applications (such as make or rake) will modify their environment based on arguments that look like variable definitions (see prodev_paris's answer), but that depends on the application.
Is it better to use the environment instead of custom makefiles to set a build configuration?
The best practice for build systems is to not depend on any environment variables at all. So that nothing more is necessary to build your project than:
git clone ... my_project
make -C my_project
Having to set environment variables is error prone and may lead to inconsistent builds.
How to properly adjust existing environment variables?
You may not need to adjust those at all. By using complete paths to tools like compilers you disentangle your build system from the environment.
As we all know, it is preferrable to integrate standard tools for a task like building your products instead of creating your own approach. The effort usually pays off in the long term.
That being said, a simple approach would be to define different environment files (e.g. build-phone.env) setting working directory, PATH, CC etc. for your different products and source your environment files interactively on demand:
. /path/to/build-phone.env
[your build commands]
. /path/to/build-watch.env
[your build commands]
I think you may benefit from using direct variable definition when you call your makefile, like in the following:
make FOO=bar target
Where FOO is the variable you want to set with value bar.
Note that in this case it take precedence over environment definition! So you can easily override your PATH variable...
Please have a look at this detail topic for more info: https://stackoverflow.com/a/2826178/4716013

I want to get a tip of rm command filter by using bash script

Some weeks ago, a senior team member removed an important oracle database file(.dbf) unexpectedly. Fortunately, We could restore the system by using back-up files which was saved some days ago.
After seeing that situation, I decided to implement a solution to make atleast a double confirmation when typing rm command on the prompt. (checks more than rm -i)
Even though we aliasing rm -i as default, super speedy keyboardists usually make mistakes like that member, including me.
At first, I replaced(by using alias) basic rm command to a specific bash script file which prints and confirms many times if the targets are related on the oracle database paths or files.
simply speaking, the script operates as filter before to operate rm. If it is not related with oracle, then rm will operate as normal.
While implementing, I thought most of features are well operated as I expected only user prompt environment except one concern.
If rm command are called within other scripts(provided oracle, other vendor modifying oracle path, installer, etc) or programs(by using system call).
How can i distinguish that situation?
If above provided scripts met modified rm, That execution doesn't go ahead anymore.
Do you have more sophisticated methods?
I believe most of reader can understand my lazy explanation.
If you couldn't get clear scenery from above, let me know. I will elaborate more.
We read at man bash:
Aliases are not expanded when the shell is not interactive, unless the
expand_aliases shell option is set using shopt.
Then if you use alias to make rm invoke your shell script, other scripts won't use it by default. If it's what you want, then you're already safe.
The problem is if you want your version of rm to be invoked by scripts and do something smart when it happens. Alias is not enough for the former; even putting your rm somewhere under $PATH is not enough for programs explicitly calling /bin/rm. And for programs that aren't shell scripts, unlink system call is much more likely to be used than something like system("rm ...").
I think that for the whole "safe rm" thing to be useful, it should avoid prompts even when invoked interactively. Every user will develop the habit of saying "yes" to it, and there is no known way around that. What might work is something that moves files to recycle bin instead of deletion, making damage easy to undo (as I seem to recall, there were ready to use solutions for this).
The answer is into the alias manpage:
Note aliases are not expanded by default in non-interactive
shell, and it can be enabled by setting the expand_aliases shell
option using shopt.
Check it by yourself with man alias ;)
Anyway, i would do it in the same way you've chosen
To distinguish the situation: You can create an env variable say, APPL, which will be set to say export APPL="DATABASE . In your customized rm script, perform the double checkings only if the APPL is DATABASE (which indicates a database related script), not otherwise which means the rm call is from other scripts.
If you're using bash, you can export your shell function, which will make it available in scripts, too.
#!/usr/bin/env bash
# Define a replacement for `rm` and export it.
rm() { echo "PSYCH."; }; export -f rm
Shell functions take precedence over builtins and external utilities, so by using just rm even scripts will invoke the function - unless they explicitly bypass the function by invoking /bin/rm ... or command rm ....
Place the above (with your actual implementation of rm()) either in each user's ~/.bashrc file, or in the system-wide bash profile - sadly, its location is not standardized (e.g.: Ubuntu: /etc/bash.bashrc; Fedora /etc/bashrc)

Adding a new entry to the PATH variable in ZSH

I'm using zsh terminal, and I'm trying to add a new entry (/home/david/pear/bin) to the PATH variable. I don't see a reference to the PATH variable in my ~/.zshrc file, but doing echo $PATH returns:
/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
So I know that the path variable is being set somewhere. Where is the PATH variable set / modified for the zsh terminal?
Actually, using ZSH allows you to use special mapping of environment variables. So you can simply do:
# append
path+=('/home/david/pear/bin')
# or prepend
path=('/home/david/pear/bin' $path)
# export to sub-processes (make it inherited by child processes)
export PATH
For me that's a very neat feature which can be propagated to other variables.
Example:
typeset -T LD_LIBRARY_PATH ld_library_path :
Here, add this line to .zshrc:
export PATH=/home/david/pear/bin:$PATH
EDIT: This does work, but ony's answer above is better, as it takes advantage of the structured interface ZSH provides for variables like $PATH. This approach is standard for bash, but as far as I know, there is no reason to use it when ZSH provides better alternatives.
You can append to your PATH in a minimal fashion. No need for
parentheses unless you're appending more than one element. It also
usually doesn't need quotes. So the simple, short way to append is:
path+=/some/new/bin/dir
This lower-case syntax is using path as an array, yet also
affects its upper-case partner equivalent, PATH (to which it is
"bound" via typeset).
(Notice that no : is needed/wanted as a separator.)
Common interactive usage
Then the common pattern for testing a new script/executable becomes:
path+=$PWD/.
# or
path+=$PWD/bin
Common config usage
You can sprinkle path settings around your .zshrc (as above) and it will naturally lead to the earlier listed settings taking precedence (though you may occasionally still want to use the "prepend" form path=(/some/new/bin/dir $path)).
Related tidbits
Treating path this way (as an array) also means: no need to do a
rehash to get the newly pathed commands to be found.
Also take a look at vared path as a dynamic way to edit path
(and other things).
You may only be interested in path for this question, but since
we're talking about exports and arrays, note that
arrays generally cannot be exported.
You can even prevent PATH from taking on duplicate entries
(refer to
this
and this):
typeset -U path
PATH pre-populated
The reason your path already has some entries in it is due to your system shell files setting path for you. This is covered in a couple other posts:
Why and where the $PATH env variable is set?
Where is the source of $PATH? I cannot find it in .zshrc
one liner, without opening ~/.zshrc file
echo -n 'export PATH=~/bin:$PATH' >> ~/.zshrc
or
echo -n 'export PATH=$HOME/bin:$PATH' >> ~/.zshrc
To see the effect, do source ~/.zshrc in the same tab or open a new tab
Added path to ~/.zshrc
sudo vi ~/.zshrc
add new path
export PATH="$PATH:[NEW_DIRECTORY]/bin"
Update ~/.zshrc
Save ~/.zshrc
source ~/.zshrc
Check PATH
echo $PATH
OPTION 1: Add this line to ~/.zshrc:
export "PATH=$HOME/pear/bin:$PATH"
After that you need to run source ~/.zshrc in order your changes to take affect OR close this window and open a new one
OPTION 2: execute it inside the terminal console to add this path only to the current terminal window session. When you close the window/session, it will be lost.
If you are on macOS (I'm on Monterey 12.3.1), you may have been pulling your hair like I did metaphorically. These instructions above all worked for me within the terminal session, but I could never get it to persist no matter what I did with export. Moreover, I couldn't find the .zshrc anywhere.
Turns out Apple does it differently. The file you need to edit is etc/paths. You can simply sudo nano /etc/paths and add your path in a new line. Then simply restart terminal and voila.
for me PATH=$PATH:/path/to/file/bin
then export PATH worked.
to check echo $PATH . other solutions are adding the path temporarily.
I'm on Monterey 12.4 and the only way I could change the path was using the helper function. Editing text files in nano did diddly squat
# append
path+=('/foo/bar/yourpath')
# export to sub-processes
export PATH
to verify your new directory has been added correctly, you can use
print -l $path
thanks to the fact that its type is known to be an array

Aliasing two different versions of the same program in linux?

I have an old version of a program sitting on my machine. This program recently had a version upgrade. The way I used to run my old program was by typing "runProgram". The path to the runscript of my program was specified in my PATH variable as
PATH = ....:/path/to/my/old/programs/bin
I want to run the new version of this same program alongside my old program and the way I was thinking of doing it was by modifying my PATH variable as follows:
PATH = ....:/path/to/my/old/programs/bin:/path/to/my/new/programs/bin
What I want to achieve is some way to alias these two paths so that when I type 'runVersion1', the previous version is executed and when I type 'runVersion2', the new version is executed?
Is there a way to achieve that?
Thanks
If the program itself runs other programs from the bin directory, then when you run a version 1 program, you want to ensure that the version 1 directory is on the PATH ahead of the version 2 directory, and vice versa when you run a version 2 program. That is something I deal with all the time, and I deal with it by ensuring that the PATH is set appropriately.
In my $HOME/bin, I would place two scripts:
RunVersion1
export PATH=/path/to/my/old/programs/bin:$PATH
# Set other environment variables as needed
exec runProgram "$#"
RunVersion2
export PATH=/path/to/my/new/programs/bin:$PATH
# Set other environment variables as needed
exec runProgram "$#"
This technique of placing shell scripts on my PATH ahead of other programs allows me to pick which programs I run.
Semi-Generic Version
Often, I'll use a single program to set the environment and then link it to the various program names that I want to handle. It then looks at $0 and runs that:
export PATH=/path/to/my/new/programs/bin:$PATH
# Set other environment variables as needed
exec $(basename $0 2) "$#"
If this script is linked to RunProgram2, the basename command lops off the 2 from the end of RunProgram2 and then executes RunProgram from the more recent directory.
I've used this general technique for accessing 32-bit and 64-bit versions of the software on a single machine, too. The programs I deal with tend to have more complex environments than just a setting of $PATH, so the scripts are bigger.
One of the main advantages of scripts in $HOME/bin over aliases and the like is that it doesn't much matter which shell I'm stuck with using; it works the same way. Plus I don't have so many places to look to find where the alias is defined (because it isn't defined).
I would put two alias definitions in your ~/.bashrc (depending what shell you are using).
alias runVersion1='/path/to/my/old/programs/bin/program'
alias runVersion2='/path/to/my/new/programs/bin/program'
After editing that file you need to relogin or simply execute
. ~/.bashrc
The way you suggest with $PATH won't do what you want. One way that might:
Given that usually, /usr/local/bin is in $PATH, and that that is the standard location for "local binaries", you do the following:
sudo ln -s /path/to/my/old/programs/bin/myprogram /usr/local/bin/runVersion1
sudo ln -s /path/to/my/new/programs/bin/myprogram /usr/local/bin/runVersion2
Alternatively, if you don't want it to be system-wide (i.e. instead, just for your user), you could:
ln -s /path/to/my/old/programs/bin/myprogram $HOME/bin/runVersion1
ln -s /path/to/my/new/programs/bin/myprogram $HOME/bin/runVersion2
(assuming $HOME/bin is in your $PATH)
Now this won't necessarily fix your problem - could use a little more information in the question, BUT it should help you get further with what you're trying to do.

Resources