Unix Standard Directory to Put Custom Executables or Scripts

Unix standard directory to put custom executables or scripts?

/usr/local/bin exists precisely for this purpose: for system-wide installation. For your own private use, ~/bin is the de facto standard.

If you want to keep each binary in its own subdirectory, you can do that, and add a symlink to a directory already in your PATH. So, for example:

curl -o $HOME/downloads/fnord http://fnord.example.com/script.exe
ln -s $HOME/downloads/fnord $HOME/bin/

This assumes $HOME/bin is in your PATH.

There are tools like stow which do this -- and much more -- behind the scenes for you.

Linux dev | Good practice for program files

I put my small stuff in a directory called ~/src/, and if it grows or uses more than one file, in a subdirectory ~/src/topicX/. If I start a real thorough project, it gets a directory like ~/prj/projectX/.

Executables typically reside in the directory where they have been created. If they are tools to be used in everyday life, I create a symbolic link in ~/bin/ pointing to the executable in its original position.

I never install anything in /usr/local/bin/.

You were asking if there is a standard. No, there isn't. There are lots of half-standards, though, and depending on who you ask, from which company, out of which school, you might hear various answers, some of them probably stating that their way is the one standard.

I have seen too many variants to believe that BS.

How to make a programme executable anywhere in the SHELL


  1. Make the scripts executable: chmod +x $HOME/scrips/* This needs to be done only once.
  2. Add the directory containing the scripts to the PATH variable: export PATH=$HOME/scrips/:$PATH (Verify the result with echo $PATH.) The export command needs to be run in every shell session. For convenience, you want to add that command to the $HOME/.profile file so it is run automatically for you at log-in time.

Now you can execute script.pl some-arguments or script.py some-arguments from anywhere.

How to make a shell script global?

/usr/local/bin would be the most appropriate location. Mac OS X has it in the PATH by default

How to execute bash script from any location?

If you want to run the script from everywhere you need to add it to your PATH. Usually /usr/local/bin is in the path of every user so this way it should work.
So check if in your system /usr/local/bin is in your PATH doing, on your terminal:

echo $PATH 

You should see a lot of paths listed (like /bin, /sbin etc...). If its not listed you can add it. A even better solution is to keep all your scripts inside a directory, for example in your home and add it to your path.

To add a directory in your path you can modify your shell init scripts and add the new directories, for example if you're usin the BASH shell you can edi your .bashrc and add the line:

PATH=$PATH:/the_directory_you_want_to_add/:/another_directory/

This will append the new directories to your existing PATH.

How to run a .sh-script from any path in a terminal?

One option is simply to type the path to the script:

~/Desktop/script

This works fine, but gets a bit unwieldy.

This is what the PATH environment variable is for. And it is what $HOME/bin is for.

  1. Create yourself a directory $HOME/bin. Put all your executable scripts in it (make them executable with chmod +x script if need be††). This way, there's one place to look for the scripts you want to run.
  2. Add $HOME/bin to your PATH. I put mine at the front: PATH="$HOME/bin:$PATH, but you could put it at the back if you prefer.
  3. Update your .profile or .bash_profile (or possibly .bashrc) file to set PATH. Beware of a continually growing PATH, though.

As tripleee noted, once the command is installed in a directory on PATH, you no longer type ./script, but just script. This is exactly like you type ls and not /bin/ls, etc. Once the program is installed in a directory on your PATH, it is (for many purposes) indistinguishable from a system-provided command.

I have about 500 scripts and programs in my $HOME/bin directory.

Note that this doesn't require any special privileges. If you have administrator access to your machine and you think other users might find your commands useful, then you could install the scripts/programs in one of the system-provided directories on your PATH. However, it is usually best not to add programs to any of:

  • /bin
  • /usr/bin
  • /sbin
  • /usr/sbin

There is often/usually /usr/local/bin which is a suitable place for widely used commands not provided by the system.


†† It would be better to use chmod a+x,go-w script; your scripts should not be writable by other people. You could even simply use chmod 555 script or chmod 755 script. I tend to keep my scripts non-writable. That way, I have to go through a formal change process with the version control system. It means there's less danger of uncontrolled changes.

How to permanently set $PATH on Linux/Unix

There are multiple ways to do it. The actual solution depends on the purpose.

The variable values are usually stored in either a list of assignments or a shell script that is run at the start of the system or user session. In case of the shell script you must use a specific shell syntax and export or set commands.

System wide

  1. /etc/environment List of unique assignments. Allows references. Perfect for adding system-wide directories like /usr/local/something/bin to PATH variable or defining JAVA_HOME. Used by PAM and systemd.

  2. /etc/environment.d/*.conf List of unique assignments. Allows references. Perfect for adding system-wide directories like /usr/local/something/bin to PATH variable or defining JAVA_HOME. The configuration can be split into multiple files, usually one per each tool (Java, Go, and Node.js). Used by systemd that by design do not pass those values to user login shells.

  3. /etc/xprofile Shell script executed while starting X Window System session. This is run for every user that logs into X Window System. It is a good choice for PATH entries that are valid for every user like /usr/local/something/bin. The file is included by other script so use POSIX shell syntax not the syntax of your user shell.

  4. /etc/profile and /etc/profile.d/* Shell script. This is a good choice for shell-only systems. Those files are read only by shells in login mode.

  5. /etc/<shell>.<shell>rc. Shell script. This is a poor choice because it is single shell specific. Used in non-login mode.

User session

  1. ~/.pam_environment. List of unique assignments, no references allowed. Loaded by PAM at the start of every user session irrelevant if it is an X Window System session or shell. You cannot reference other variables including HOME or PATH so it has limited use. Used by PAM.

  2. ~/.xprofile Shell script. This is executed when the user logs into X Window System system. The variables defined here are visible to every X application. Perfect choice for extending PATH with values such as ~/bin or ~/go/bin or defining user specific GOPATH or NPM_HOME. The file is included by other script so use POSIX shell syntax not the syntax of your user shell. Your graphical text editor or IDE started by shortcut will see those values.

  3. ~/.profile, ~/.<shell>_profile, ~/.<shell>_login Shell script. It will be visible only for programs started from terminal or terminal emulator. It is a good choice for shell-only systems. Used by shells in login mode.

  4. ~/.<shell>rc. Shell script. This is a poor choice because it is single shell specific. Used by shells in non-login mode.

Notes

GNOME on Wayland starts a user login shell to get the environment. It effectively uses the login shell configurations ~/.profile, ~/.<shell>_profile, ~/.<shell>_login files.

Man pages

  • environment
  • environment.d https://linux.die.net/man/1/environment.d
  • bash
  • dash

Distribution-specific documentation

  • Ubuntu
  • Arch Linux

Difference between Login Shell and Non-Login Shell?

How does bash tell scripts and executables apart?

Well the answer is complicated.

TLDR

bash doesn't do any shebang interpretation! That's no its job. It actually simply attempts to execute the program with a normal exec system call. The only exception is when the exec call returns an errno of -ENOEXEC the it attempts to make itself the interpreter.

Longer answer:

  • bash doesn't! Actually in most *nix like systems, and even in windows, shells do not tell the difference between "shell scripts" and other types of executables, and treat the shell scripts in any special way. That is left to the underlying exec engine of the OS. There are however caveats to this general statement. I will give the example of Linux, and it pretty much applies (at least in terms of user experience) to POSIX.1 compliant systems.

Treatment of #! in Linux

In this case the shebang is processed by the kernel. All the shell (e.g. bash) does is perform a system call in the exec(2) family of calls to request the running of a program.

The kernel itself is responsible for figuring out what to do with it. It understands a whole host of executable formats like AOUT, ELF, COFF and yes SHEBANG.

The relevant code is found in the linux kernel tree at fs/binfmt_script.c and of course in fs/exec.c

the static int load_script(struct linux_binprm *bprm) actually performs the loading of the script and executing the appropriate binary.

Now if you look at that code you'll notice that around line 58 it returns -ENOEXEC if no interpreter entry is found.

However, if an interpreter entry is found (and all else is okay) the execution continues as expected

So what happens when there is no interpreter entry found?

That means the script has no #! in the first line as expected by the kernel.

In this case bash (and other interactive shells) go ahead and try to either inject themselves in as the interpreters or the system default shell (e.g. /bin/sh) as the interpreter.

For more now what bash is doing I suggest that you create a simple shell script foo with the following content:

  # Shebang would go here
# This is foo
echo "Hello World"


chmod a+x foo

Then run foo with strace:

  strace ./foo

Followed by running it with strace but invoking bash and getting it to run foo:

  strace /bin/bash -c ./foo

Now inject #!/bin/sh in there and repeat the two steps again.

Use Unix Executable File to Run Shell Script and MPKG File

The most common issue when handling variables containing paths of directories and files is the presence of special characters such as spaces. To handle those correctly, you should always quote the variables, using double quotes. Better code would therefor be:

sudo sh "$path/join.sh"
sudo sh "$path/join2.sh"

It is also advised to wrap the variables using curly braces, this can also help to avoid unwanted issues. Resulting in following code:

sudo sh "${path}/join.sh"
sudo sh "${path}/join2.sh"

While this should work, it's also appropriate to mention that it's advised to check whether the files actually exist before executing them. Checking a file for existence can be done using -f and checking execute permission using -x. The proper code is therefor:

[ -f "${path}/join.sh" ] && [ -x "${path}/join.sh" ] && sudo sh "${path}/join.sh"
[ -f "${path}/join2.sh" ] && [ -x "${path}/join2.sh" ] && sudo sh "${path}/join2.sh"

Note that if you have a bunch of these, you'd be better off executing them using a for loop. Note also that -f becomes redundant when checking -x so better code would be:

[ -x "${path}/join.sh" ] && sudo sh "${path}/join.sh"
[ -x "${path}/join2.sh" ] && sudo sh "${path}/join2.sh"


Related Topics



Leave a reply



Submit