4

I recently installed a few softwares and needed to add their bin/ directories to my PATH. Nothing abnormal so far. But I decided to be smart about it, and rewrote a part of my .profile so I didn't have to copy/paste the same few lines of codes over and over. Here was my idea:

# Create an array with directories to be added to PATH
declare -a addpath=("$HOME/bin" "$HOME/.cabal/bin" "/opt/vert.x/current/bin")

# Add directories recursively
for dir in "${addpath[@]}"; do
if [ -d "$dir" ]; then
    PATH="$dir:$PATH"
fi
done

I thought this had worked well.. until I rebooted my PC and got locked out of my session when trying to log in. It took me a while to figure out that it was actually because of my .profile; once I commented those lines out, I was able to log into my session without being bounced back.

My question is; what did I do wrong with these lines? Is there a syntax error? Is there an other/better way to do that? What happened?

2 Answers2

6

In the default setup on Ubuntu 12.04, the .profile file is loaded by /usr/sbin/lightdm-session. This is shell script, executed by /bin/sh.

On Ubuntu, /bin/sh is dash. You've used features of bash that dash doesn't support. Dash and Bash both have the same core features, but dash sticks to these core features in order to be fast and small whereas bash adds a lot of features at the cost of requiring more resources. It is common to use dash for scripts that don't need the extra features and bash for interactive use (though zsh has a lot of nicer features).

Dash doesn't have arrays, nor the declare built-in, so it's bombing out on that line. You can put the list of paths inline:

for dir in "$HOME/bin" "$HOME/.cabal/bin" "/opt/vert.x/current/bin"; do
  if [ -d "$dir" ]; then
    PATH="$dir:$PATH"
  fi
done

See keep duplicates out of $PATH on source if you want to make sure not to end up with duplicate entries.


Rather than add new directories to PATH for each program you install, you might want to set up symbolic links in an existing directory instead. For ~/.cabal/bin, you'll want to have it in your path because executables there will be coming and going; I'd put it at the end of the PATH though, to avoid potential conflicts with existing programs on your system. ~/bin is already in your PATH on Ubuntu. For programs that you install manually such as vert.x, stow or xstow is nice for managing symbolic links. See Keeping track of programs for an introduction to stow.

  • Sorry for the time it took me to get back to you. Thank you so much for your detailed answer, I definitely learned something right here! – Jonathan H Aug 20 '13 at 12:33
-2

I think that, in your case, the best practice to make your binaries avaiable as commands is to link what you need to the /usr/bin. Mess up with $PATH isn't ever a good thing to do.

You can use 'cp' to make symbolic links, recursively, like this:

cp -rs $HOME/bin/* /usr/bin #This will link all non hidden files
cp -rs $HOME/bin/.[^.]* /usr/bin # This will link all the hidden files

Just adapt this to your scenario, using the same logic of your present code.

EDIT: you can use just the first line of the above code if you set dotglob to true on bash. Like this:

shopt -s dotglob #This will make Bash include filenames beginning with a '.' in the results of filename expansion
cp -rs $HOME/bin/* /usr/bin #Now, this will link ALL files in that directory to the destination

EDIT 2: As you can read in the comments below, is preferable that you use /usr/local/bin instead of /usr/bin. Read the comments to understand why.

  • 1
    It's exactly the opposite. You should never mess with /usr/bin, let the package manager manage it. You could add programs to /usr/local/bin, but as symbolic links, not as copies. – Gilles 'SO- stop being evil' Aug 20 '13 at 07:41
  • If you read my code, you will see that I'm not using copies. 'cp' can write symbolic links instead of copying. This is more simple that make all the symbolic links with 'ln'.

    What you say about /usr/bin can be half true here on Ubuntu (that actually uses a package manager) but isn't a global true, remember this.

    P.S.: NEVER is a strange word on Linux world. You can, obviously, do that (and all will work on most cases). The difference here is that /usr/bin hosts distribution-managed software, and /usr/local/bin hosts the other software. But this is a convention, not a technical problem.

    – Alexandre Teles Aug 20 '13 at 11:16
  • Oh, right, I'd missed the -s. But the main part of my objection still stands: don't mess with /usr/bin. It's true on Ubuntu and pretty much any other unix. Sure, it's not a law of physics. It's a convention, like driving on a certain side of the road. Some conventions are important. – Gilles 'SO- stop being evil' Aug 20 '13 at 11:23
  • 1
    @AlexandreTeles Thanks a lot for your answer. I know being downvoted can be very frustrating; you're intention was to help, and you got "punished" in return. I'm really sorry about that (I didn't downvote myself, even if I should). The truth is, you are actually wrong, but I encourage you to read the following two articles to find out why: https://wiki.debian.org/FilesystemHierarchyStandard and then http://unix.stackexchange.com/questions/11544/what-is-the-difference-between-opt-and-usr-local. Truly yours :) – Jonathan H Aug 20 '13 at 13:23