I wanted to use the /usr/bin/ksh93 interpreter on AIX and Linux wherever possible but switch to /usr/bin/ksh where it's not applicable like Mac OS X and wanted the script to be universally compatible in unix. I don't think there is any fallback mechanism in shebang
Since ksh and sh have some syntax in common, you can prefix the start of the
script with a test for ksh or ksh93 in the PATH and rerun the script with
the right interpreter. Replace the #! with the pathname to sh. (Hopefully
it is the same on both machines, or you are back where you started. You can
still try #!/usr/bin/env sh if your env will find the path for you). Add:
#!/bin/sh
if [ "$DONEIT" != true ]
then export DONEIT=true # avoid recursion
if command -v ksh > /dev/null 2>&1
then exec ksh $0 "$#"
else exec ksh93 $0 "$#"
fi
fi
... rest of your script ...
Note: command -v is the POSIX way for finding a command's path.
(Often in these situations, at the installation of a package a script goes
through the #! files and updates the interpreter path to that needed by the
target machine).
Alternatively, you could replace the #! line by any fixed path you control, eg #!/home/user/myksh, and link that file to the right ksh.
You can make a symbolic links.
if [ -f /usr/bin/ksh93 ]; then
ln -s /usr/bin/ksh93 /usr/bin/localksh
else
ln -s /usr/bin/ksh /usr/bin/localksh
fi
The shebang will be #!/usr/bin/localksh.
I would prefer using a normal shebang #!/bin/ksh, but when that one already exists and is the wrong version you will be stuck.
This is a shell script (.sh file). I need to create an absolute path based on the current directory. I know about pwd, but how do I concatenate it with another string? Here is an example of what I am trying to do:
"$pwd/some/path"
Sounds like you want:
path="$(pwd)/some/path"
The $( opens a subshell (and the ) closes it) where the contents are executed as a script so any outputs are put in that location in the string.
More useful often is getting the directory of the script that is running:
dot="$(cd "$(dirname "$0")"; pwd)"
path="$dot/some/path"
That's more useful because it resolves to the same path no matter where you are when you run the script:
> pwd
~
> ./my_project/my_script.sh
~/my_project/some/path
rather than:
> pwd
~
> ./my_project/my_script.sh
~/some/path
> cd my_project
> pwd
~/my_project
> ./my_script.sh
~/my_project/some/path
More complex but if you need the directory of the current script running if it has been executed through a symlink (common when installing scripts through homebrew for example) then you need to parse and follow the symlink:
if [[ "$OSTYPE" == *darwin* ]]; then
READLINK_CMD='greadlink'
else
READLINK_CMD='readlink'
fi
dot="$(cd "$(dirname "$([ -L "$0" ] && $READLINK_CMD -f "$0" || echo "$0")")"; pwd)"
More complex and more requirements for it to work (e.g. having a gnu compatible readlink installed) so I tend not to use it as much. Only when I'm certain I need it, like installing a command through homebrew.
Using the shell builtin pwd in a command substitution ($(...)) is an option, but not necessary, because all POSIX-compatible shells define the special $PWD shell variable that contains the current directory as an absolute path, as mandated by POSIX.
Thus, using $PWD is both simpler and more efficient than $(pwd):
"$PWD/some/path" # alternatively, for visual clarity: "${PWD}/some/path"
However, if you wanted to resolve symlinks in the directory path, you DO need pwd, with its -P option:
"$(pwd -P)/some/path"
Note that POSIX mandates that $PWD contain an absolute pathname with symlinks resolved.
In practice, however, NO major POSIX-like shell (bash, dash, ksh, zsh) does that - they all retain symbolic link components. Thus, the (POSIX-compliant) pwd -P is needed to resolve them.
Note that all said POSIX-like shells implement pwd as a builtin that supports -P.
Michael Allen's helpful answer points out that it's common to want to know the directory of where the running script is located.
The challenge is that the script file itself may be a symlink, so determining the true directory of origin is non-trivial, especially when portability is a must.
This answer (of mine) shows a solution.
wd=`pwd`
new_path="$wd/some/path"
with "dirname $0" you can get dynamin path upto current run scipt.
for example : your file is locateted in shell folder file name is xyz and there are anthor file abc to include in xyz file.
so put in xyz file LIke:
php "`dirname $0`"/abc.php
In bash i get the executing script's parent folder name by this line
SCRIPT_PARENT=`readlink -f ${BASH_SOURCE%/*}/..`
Is there any way to achieve this in zsh in a way that works both in zsh and bash?
Assume i have got a file /some/folder/rootfolder/subfolder/script with the contents:
echo `magic-i-am-looking-for`
I want it to behave this way:
$ cd /some/other/folder
$ . /some/folder/rootfolder/subfolder/script
/some/folder/rootfolder
$ . ../../folder/rootfolder/subfolder/script
/some/folder/rootfolder
$ cd /some/folder/rootfolder
$ . subfolder/script
/some/folder/rootfolder
$ cd subfolder
$ . script
/some/folder/rootfolder
This should work in bash and zsh. My first implements this behavior, but does due to $BASH_SOURCE not work in zsh.
So basically its:
Is there a way to emulate $BASH_SOURCE in zsh, that also works in bash?
I now realized that $0 in zsh behaves like $BASH_SOURCE in bash. So using $BASH_SOURCE when available and falling back to $0 solves my problem:
${BASH_SOURCE:-$0}
There is a little zsh edge case left, when sourcing from $PATH like:
zsh> cat ../script
echo \$0: $0
echo \$BASH_SOURCE: $BASH_SOURCE
echo '${BASH_SOURCE:-$0}:' ${BASH_SOURCE:-$0}
zsh> . script
$0: script
$BASH_SOURCE:
${BASH_SOURCE:-$0}: script
bash> . script
$0: bash
$BASH_SOURCE: /home/me/script
${BASH_SOURCE:-$0}: /home/me/script
I could do a which script but this would not play nice with other cases
While it would be easy to do this in zsh, it is just as easy to use pure bash which is able to be evaluated in zsh. If you cannot use any command that may or may not be on your path, then you can only use variable alteration to achieve what you want:
SCRIPT_SOURCE=${0%/*}
This is likely to be a relative path. If you really want the full path then you will have to resort to an external command (you could implement it yourself, but it would be a lot of work to avoid using a very available command):
SCRIPT_SOURCE=$(/bin/readlink -f ${0%/*})
This doesn't depend on your $PATH, it just depends on /bin/readlink being present. Which it almost certainly is.
Now, you wanted this to be a sourced file. This is fine, as you can just export any variable you set, however if you execute the above then $0 will be the location of the sourced file and not the location of the calling script.
This just means you need to set a variable to hold the $0 value which the sourced script knows about. For example:
The script you will source:
echo ${LOCATION%/*}
The script that sources that script:
LOCATION=$0
<source script here>
But given that the ${0%/*} expansion is so compact, you could just use that in place of the script.
Because you were able to run the command from your $PATH I'll do something like that:
SCRIPT_PARENT=$(readlink -f "$(which $0)/..")
Is that your desired output?
How do I refer to the current directory in a shell script?
So I have this script which calls another script in the same directory:
#! /bin/sh
#Call the other script
./foo.sh
# do something ...
For this I got ./foo.sh: No such file or directory
So I changed it to:
#! /bin/sh
#Call the other script
foo.sh
# do something ...
But this would call the foo script which is, by default, in the PATH. This is not what I want.
So the question is, what's the syntax to refer ./ in a shell script?
If both the scripts are in the same directory and you got the ./foo.sh: No such file or directory error then the most likely cause is that you ran the first script from a different directory than the one where they are located in. Put the following in your first script so that the call to foo.sh works irrespective of where you call the first script from:
my_dir=`dirname $0`
#Call the other script
$my_dir/foo.sh
The following code works well with spaces and doesn't require bash to work:
#!/bin/sh
SCRIPTDIR="$(dirname "$0")"
#Call the other script
"$SCRIPTDIR/foo.sh"
Also if you want to use the absolute path, you could do this:
SCRIPTDIR=`cd "$(dirname "$0")" && pwd`
This might help you:
Unix shell script find out which directory the script file resides?
But as sarnold stated, "./" is for the current working directory.
In order to make it POSIX:
a="/$0"; a=${a%/*}; a=${a:-.}; a=${a#/}/; BASEDIR=$(cd $a; pwd)
Tested on many Bourne-compatible shells including the BSD ones.
As far as I know I am the author and I put it into public domain. For more info see:
https://blog.jasan.tk/posix/2017/05/11/posix_shell_dirname_replacement
script_dir="${BASH_SOURCE%/*}" # rm the last / and the file name from BASH_SOURCE
$script_dir/foo.sh
Reference: Alex Che's comment above.
The accepted solution does not work if you have a space in the path to the directory containing the scripts.
If you can use bash, this worked for me:
#!/bin/bash
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
"${SCRIPTDIR}/foo.sh"
The way you would normally include a script is with "source"
eg:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
incl.sh:
echo "The included script"
The output of executing "./main.sh" is:
The included script
The main script
... Now, if you attempt to execute that shell script from another location, it can't find the include unless it's in your path.
What's a good way to ensure that your script can find the include script, especially if for instance, the script needs to be portable?
I tend to make my scripts all be relative to one another.
That way I can use dirname:
#!/bin/sh
my_dir="$(dirname "$0")"
"$my_dir/other_script.sh"
I know I am late to the party, but this should work no matter how you start the script and uses builtins exclusively:
DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"
. (dot) command is an alias to source, $PWD is the Path for the Working Directory, BASH_SOURCE is an array variable whose members are the source filenames, ${string%substring} strips shortest match of $substring from back of $string
An alternative to:
scriptPath=$(dirname $0)
is:
scriptPath=${0%/*}
.. the advantage being not having the dependence on dirname, which is not a built-in command (and not always available in emulators)
If it is in the same directory you can use dirname $0:
#!/bin/bash
source $(dirname $0)/incl.sh
echo "The main script"
I think the best way to do this is to use the Chris Boran's way, BUT you should compute MY_DIR this way:
#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh
To quote the man pages for readlink:
readlink - display value of a symbolic link
...
-f, --canonicalize
canonicalize by following every symlink in every component of the given
name recursively; all but the last component must exist
I've never encountered a use case where MY_DIR is not correctly computed. If you access your script through a symlink in your $PATH it works.
A combination of the answers to this question provides the most robust solution.
It worked for us in production-grade scripts with great support of dependencies and directory structure:
#!/bin/bash
# Full path of the current script
THIS=`readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0`
# The directory where current script resides
DIR=`dirname "${THIS}"`
# 'Dot' means 'source', i.e. 'include':
. "$DIR/compile.sh"
The method supports all of these:
Spaces in path
Links (via readlink)
${BASH_SOURCE[0]} is more robust than $0
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
1. Neatest
I explored almost every suggestion and here is the neatest one that worked for me:
script_root=$(dirname $(readlink -f $0))
It works even when the script is symlinked to a $PATH directory.
See it in action here: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
2. The coolest
# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')
This is actually from another answer on this very page, but I'm adding it to my answer too!
3. The most reliable
Alternatively, in the rare case that those didn't work, here is the bullet proof approach:
# Copyright http://stackoverflow.com/a/7400673/257479
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; }
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; }
script_root=$(dirname $(whereis_realpath "$0"))
You can see it in action in taskrunner source: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner
Hope this help someone out there :)
Also, please leave it as a comment if one did not work for you and mention your operating system and emulator. Thanks!
This works even if the script is sourced:
source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
You need to specify the location of the other scripts, there is no other way around it. I'd recommend a configurable variable at the top of your script:
#!/bin/bash
installpath=/where/your/scripts/are
. $installpath/incl.sh
echo "The main script"
Alternatively, you can insist that the user maintain an environment variable indicating where your program home is at, like PROG_HOME or somesuch. This can be supplied for the user automatically by creating a script with that information in /etc/profile.d/, which will be sourced every time a user logs in.
I'd suggest that you create a setenv script whose sole purpose is to provide locations for various components across your system.
All other scripts would then source this script so that all locations are common across all scripts using the setenv script.
This is very useful when running cronjobs. You get a minimal environment when running cron, but if you make all cron scripts first include the setenv script then you are able to control and synchronise the environment that you want the cronjobs to execute in.
We used such a technique on our build monkey that was used for continuous integration across a project of about 2,000 kSLOC.
Shell Script Loader is my solution for this.
It provides a function named include() that can be called many times in many scripts to refer a single script but will only load the script once. The function can accept complete paths or partial paths (script is searched in a search path). A similar function named load() is also provided that will load the scripts unconditionally.
It works for bash, ksh, pd ksh and zsh with optimized scripts for each one of them; and other shells that are generically compatible with the original sh like ash, dash, heirloom sh, etc., through a universal script that automatically optimizes its functions depending on the features the shell can provide.
[Fowarded example]
start.sh
This is an optional starter script. Placing the startup methods here is just a convenience and can be placed in the main script instead. This script is also not needed if the scripts are to be compiled.
#!/bin/sh
# load loader.sh
. loader.sh
# include directories to search path
loader_addpath /usr/lib/sh deps source
# load main script
load main.sh
main.sh
include a.sh
include b.sh
echo '---- main.sh ----'
# remove loader from shellspace since
# we no longer need it
loader_finish
# main procedures go from here
# ...
a.sh
include main.sh
include a.sh
include b.sh
echo '---- a.sh ----'
b.sh
include main.sh
include a.sh
include b.sh
echo '---- b.sh ----'
output:
---- b.sh ----
---- a.sh ----
---- main.sh ----
What's best is scripts based on it may also be compiled to form a single script with the available compiler.
Here's a project that uses it: http://sourceforge.net/p/playshell/code/ci/master/tree/. It can run portably with or without compiling the scripts. Compiling to produce a single script can also happen, and is helpful during installation.
I also created a simpler prototype for any conservative party that may want to have a brief idea of how an implementation script works: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype.bash. It's small and anyone can just include the code in their main script if they want to if their code is intended to run with Bash 4.0 or newer, and it also doesn't use eval.
Steve's reply is definitely the correct technique but it should be refactored so that your installpath variable is in a separate environment script where all such declarations are made.
Then all scripts source that script and should installpath change, you only need to change it in one location. Makes things more, er, futureproof. God I hate that word! (-:
BTW You should really refer to the variable using ${installpath} when using it in the way shown in your example:
. ${installpath}/incl.sh
If the braces are left out, some shells will try and expand the variable "installpath/incl.sh"!
I put all my startup scripts in a .bashrc.d directory.
This is a common technique in such places as /etc/profile.d, etc.
while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE
The problem with the solution using globbing...
for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done
...is you might have a file list which is "too long".
An approach like...
find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done
...runs but doesn't change the environment as desired.
This should work reliably:
source_relative() {
local dir="${BASH_SOURCE%/*}"
[[ -z "$dir" ]] && dir="$PWD"
source "$dir/$1"
}
source_relative incl.sh
Using source or $0 will not give you the real path of your script. You could use the process id of the script to retrieve its real path
ls -l /proc/$$/fd |
grep "255 ->" |
sed -e 's/^.\+-> //'
I am using this script and it has always served me well :)
Of course, to each their own, but I think the block below is pretty solid. I believe this involves the "best" way to find a directory, and the "best" way to call another bash script:
scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh
echo "The main script"
So this may be the "best" way to include other scripts. This is based off another "best" answer that tells a bash script where it is stored
Personally put all libraries in a lib folder and use an import function to load them.
folder structure
script.sh contents
# Imports '.sh' files from 'lib' directory
function import()
{
local file="./lib/$1.sh"
local error="\e[31mError: \e[0mCannot find \e[1m$1\e[0m library at: \e[2m$file\e[0m"
if [ -f "$file" ]; then
source "$file"
if [ -z $IMPORTED ]; then
echo -e $error
exit 1
fi
else
echo -e $error
exit 1
fi
}
Note that this import function should be at the beginning of your script and then you can easily import your libraries like this:
import "utils"
import "requirements"
Add a single line at the top of each library (i.e. utils.sh):
IMPORTED="$BASH_SOURCE"
Now you have access to functions inside utils.sh and requirements.sh from script.sh
TODO: Write a linker to build a single sh file
we just need to find out the folder where our incl.sh and main.sh is stored; just change your main.sh with this:
main.sh
#!/bin/bash
SCRIPT_NAME=$(basename $0)
SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")"
source $SCRIPT_DIR/incl.sh
echo "The main script"
According man hier suitable place for script includes is /usr/local/lib/
/usr/local/lib
Files associated with locally installed programs.
Personally I prefer /usr/local/lib/bash/includes for includes.
There is bash-helper lib for including libs in that way:
#!/bin/bash
. /usr/local/lib/bash/includes/bash-helpers.sh
include api-client || exit 1 # include shared functions
include mysql-status/query-builder || exit 1 # include script functions
# include script functions with status message
include mysql-status/process-checker; status 'process-checker' $? || exit 1
include mysql-status/nonexists; status 'nonexists' $? || exit 1
Most of the answers I saw here seem to overcomplicate things. This method has always worked reliably for me:
FULLPATH=$(readlink -f $0)
INCPATH=${FULLPATH%/*}
INCPATH will hold the complete path of the script excluding the script filename, regardless of how the script is called (by $PATH, relative or absolute).
After that, one only needs to do this to include files in the same directory:
. $INCPATH/file_to_include.sh
Reference: TecPorto / Location independent includes
here is a nice function you can use. it builds on what #sacii made. thank you
it will let you list any number of space separated script names to source (relative to the script calling source_files).
optionally you can pass an absolute or relative path as the first argument and it will source from there instead.
you can call it multiple times (see example below) to source scripts from different dirs
#!/usr/bin/env bash
function source_files() {
local scripts_dir
scripts_dir="$1"
if [ -d "$scripts_dir" ]; then
shift
else
scripts_dir="${BASH_SOURCE%/*}"
if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
fi
for script_name in "$#"; do
# shellcheck disable=SC1091 disable=SC1090
. "$scripts_dir/$script_name.sh"
done
}
here is an example you can run to show how its used
#!/usr/bin/env bash
function source_files() {
local scripts_dir
scripts_dir="$1"
if [ -d "$scripts_dir" ]; then
shift
else
scripts_dir="${BASH_SOURCE%/*}"
if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
fi
for script_name in "$#"; do
# shellcheck disable=SC1091 disable=SC1090
. "$scripts_dir/$script_name.sh"
done
}
## -- EXAMPLE -- ##
# assumes dir structure:
# /
# source_files.sh
# sibling.sh
# scripts/
# child.sh
# nested/
# scripts/
# grandchild.sh
cd /tmp || exit 1
# sibling.sh
tee sibling.sh <<- EOF > /dev/null
#!/usr/bin/env bash
export SIBLING_VAR='sibling var value'
EOF
# scripts/child.sh
mkdir -p scripts
tee scripts/child.sh <<- EOF > /dev/null
#!/usr/bin/env bash
export CHILD_VAR='child var value'
EOF
# nested/scripts/grandchild.sh
mkdir -p nested/scripts
tee nested/scripts/grandchild.sh <<- EOF > /dev/null
#!/usr/bin/env bash
export GRANDCHILD_VAR='grandchild var value'
EOF
source_files 'sibling'
source_files 'scripts' 'child'
source_files 'nested/scripts' 'grandchild'
echo "$SIBLING_VAR"
echo "$CHILD_VAR"
echo "$GRANDCHILD_VAR"
rm sibling.sh
rm -rf scripts nested
cd - || exit 1
prints:
sibling var value
child var value
grandchild var value
You can also use:
PWD=$(pwd)
source "$PWD/inc.sh"