Bash find where the current execution is located - bash

I have a shell program in a directory (ie dir1/dothis.sh) - works fine when I cd to that directory and ./dothis.sh
if I created a ln to that directoy with a new name - dir2 and do dir2/dothis.sh
it would execute but it thinks the current execution path is the new dir where dir2 is pointing to at
in dothis.sh - how do I find where dothis.sh actually located? The problem I have is that the dir1/dothis.sh can be relocated from system to system so there is no warranty where dir1/dothis.sh can be hard code

Use the bash built-in
#!/bin/bash
echo "Current path: $PWD"

try this:
#!/bin/bash
echo $0
a=`pwd`
echo $a
b=$a"/"$0
echo `dirname $b`

How about
dirname $(readlink -f $0)
It will also resolve symbolic link if any...

Related

Server shell backup script (bash)

Name of a script - backup_script.sh
Location of a script on server - /home/company_folder/company_site_backups
Line added to the cron file:
#monthly /home/company_folder/company_site_backups/backup_script.sh
#!/bin/bash
DIR="/home/company_folder/company_applications/*"
BACKUPDIR="/home/company_folder/company_site_backups"
NOW=`date +\%Y\%m\%d`
cd $DIR
for i in $DIR; do zip -r "${i%/}.zip" "$BACKUPDIR/$i-$NOW"; done
ls -l
echo "Done!"
But unfortunately my script does not work properly. Actually. It does not run at all! I do not see any errors in the syntax.
Does anyone know how to fix it?
The cd $DIR seems strange; if the first entry found by /home/company_folder/company_applications/* is a directory it will change to that directory; if it is a file (or company_applications is empty) it will get an error.
Perhaps everything is running correctly except that because of the above your ls -l is not running in the directory you expect? Try removing the cd and changing it to ls -l $DIR.
It also seems very strange to me that you are zipping up content from a backup directory into an applications directory. Perhaps you meant to be doing:
zip -r "$BACKUPDIR/`basename $i`-$NOW" $i
could you try this;
#!/bin/bash
DIR="/home/company_folder/company_applications/*"
BACKUPDIR="/home/company_folder/company_site_backups"
NOW=`date +\%Y\%m\%d`
cd $DIR
for i in $DIR
do
base=$(basename "$i")
zip -r $BACKUPDIR/$base-${NOW}.zip $i
done
ls -l $BACKUPDIR
echo "Done!"

How to resolve a symbolic link to a parent directory in a shell script?

The following line introduces the local variable PROGUARD_HOME within a shell script:
PROGUARD_HOME=`dirname "$0"`/..
This points to the parent folder of the shell script. The script executes normally. - Then, I created the symlink /usr/local/bin/proguard which refers to ~/bin/proguard4.10/bin/proguard.sh. When I run proguard using the symlink PROGUARD_HOME is no longer resolved correctly. This causes the following error message output by the shell script:
Error: Unable to access jarfile /usr/local/bin/../lib/proguard.jar
How can I rewrite the allocation of the enviroment variable so that it resolves an symbolic link if present?
I am aware of a very similar question on resolving symbolic links in shell scripts but still cannot figure out how to combine those solutions with the parent folder approach here.
I think a readlink -f $0 to reveal the target of the shell script itself, then a dirname to strip off the script, then another readlink -f on the product of that should do the trick:
PROGUARD_HOME=$(readlink -f $(dirname $(readlink -f "$0"))/..)
A more step-by-step breakdown:
echo "\$0 is $0"
TRUENAMEOFSCRIPT=$(readlink -f $0)
echo "readlink -f of \$0 reveals $TRUENAMEOFSCRIPT"
DIRNAMEOFSCRIPT=$(dirname $TRUENAMEOFSCRIPT)
echo "The script lives in directory $DIRNAMEOFSCRIPT"
PARENTDIR=$(readlink -f "$DIRNAMEOFSCRIPT"/..)
echo "Its parent dir is $PARENTDIR"

Get `dirname $0` of a KornShell script called by a symbolic link

I have a folder organization that looks like this:
link.sh
dist/MyApp-3.0.0/script.sh
dist/MyApp-3.0.0/lib/*.jar
The link.sh is a symbolic link to the KornShell (ksh) script script.sh. In the shell script, I want to call a Java program with following command:
java -cp lib/*
When I try to launch the application from the symbolic link, I get ClassNotFound because the relative path is resolved from the link base dir (this is normal).
Inside the shell script, how can I get the full path of the script (<...>/dist/MyApp-3.0.0/)? It will allow me to modify my Java call:
java -cp ${SCRIPT_DIR}/lib/*
Edit: using readlink
You can use readlink, and it boils down to:
SCRIPT_DIR=$(dirname "$(readlink -f $0)")
Edit: without readlink
if test -h $0; then
symdir=$(dirname "$(ls -l $0 | sed -n 's/.*-> //p')")
if [[ -z $symdir ]]; then
symdir=.
fi
fullreldir=$(dirname $0)/$symdir
fi
script_dir=$(cd $fullreldir; /bin/pwd)
I misunderstood the location of the script, and had assumed that the directory of the script being invoked was in the directory structure of the target application, where the following would work:
SCRIPT_DIR=$(cd $(dirname $0); /bin/pwd)
You have to use the readlink function (man readlink)
my2c

Quick bash script to run a script in a specified folder?

I am attempting to write a bash script that changes directory and then runs an existing script in the new working directory.
This is what I have so far:
#!/bin/bash
cd /path/to/a/folder
./scriptname
scriptname is an executable file that exists in /path/to/a/folder - and (needless to say), I do have permission to run that script.
However, when I run this mind numbingly simple script (above), I get the response:
scriptname: No such file or directory
What am I missing?! the commands work as expected when entered at the CLI, so I am at a loss to explain the error message. How do I fix this?
Looking at your script makes me think that the script you want to launch a script which is locate in the initial directory. Since you change you directory before executing it won't work.
I suggest the following modified script:
#!/bin/bash
SCRIPT_DIR=$PWD
cd /path/to/a/folder
$SCRIPT_DIR/scriptname
cd /path/to/a/folder
pwd
ls
./scriptname
which'll show you what it thinks it's doing.
I usually have something like this in my useful script directory:
#!/bin/bash
# Provide usage information if not arguments were supplied
if [[ "$#" -le 0 ]]; then
echo "Usage: $0 <executable> [<argument>...]" >&2
exit 1
fi
# Get the executable by removing the last slash and anything before it
X="${1##*/}"
# Get the directory by removing the executable name
D="${1%$X}"
# Check if the directory exists
if [[ -d "$D" ]]; then
# If it does, cd into it
cd "$D"
else
if [[ "$D" ]]; then
# Complain if a directory was specified, but does not exist
echo "Directory '$D' does not exist" >&2
exit 1
fi
fi
# Check if the executable is, well, executable
if [[ -x "$X" ]]; then
# Run the executable in its directory with the supplied arguments
exec ./"$X" "${#:2}"
else
# Complain if the executable is not a valid
echo "Executable '$X' does not exist in '$D'" >&2
exit 1
fi
Usage:
$ cdexec
Usage: /home/archon/bin/cdexec <executable> [<argument>...]
$ cdexec /bin/ls ls
ls
$ cdexec /bin/xxx/ls ls
Directory '/bin/xxx/' does not exist
$ cdexec /ls ls
Executable 'ls' does not exist in '/'
One source of such error messages under those conditions is a broken symlink.
However, you say the script works when run from the command line. I would also check to see whether the directory is a symlink that's doing something other than what you expect.
Does it work if you call it in your script with the full path instead of using cd?
#!/bin/bash
/path/to/a/folder/scriptname
What about when called that way from the command line?

How can I set the current working directory to the directory of the script in Bash?

I'm writing a Bash script. I need the current working directory to always be the directory that the script is located in.
The default behavior is that the current working directory in the script is that of the shell from which I run it, but I do not want this behavior.
#!/bin/bash
cd "$(dirname "$0")"
The following also works:
cd "${0%/*}"
The syntax is thoroughly described in this StackOverflow answer.
Try the following simple one-liners:
For all UNIX/OSX/Linux
dir="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
Bash
dir="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
Note: A double dash (--) is used in commands to signify the end of command options, so files containing dashes or other special characters won't break the command.
Note: In Bash, use ${BASH_SOURCE[0]} in favor of $0, otherwise the path can break when sourcing it (source/.).
*For Linux, Mac and other BSD:
cd "$(dirname "$(realpath -- "$0")")";
Note: realpath should be installed in the most popular Linux distribution by default (like Ubuntu), but in some it can be missing, so you have to install it.
Note: If you're using Bash, use ${BASH_SOURCE[0]} in favor of $0, otherwise the path can break when sourcing it (source/.).
Otherwise you could try something like that (it will use the first existing tool):
cd "$(dirname "$(readlink -f -- "$0" || realpath -- "$0")")"
For Linux specific:
cd "$(dirname "$(readlink -f -- "$0")")"
*Using GNU readlink on BSD/Mac:
cd "$(dirname "$(greadlink -f -- "$0")")"
Note: You need to have coreutils installed
(e.g. 1. Install Homebrew, 2. brew install coreutils).
In bash
In bash you can use Parameter Expansions to achieve that, like:
cd "${0%/*}"
but it doesn't work if the script is run from the same directory.
Alternatively you can define the following function in bash:
realpath () {
[[ "$1" = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
This function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).
or here is the version taken from Debian .bashrc file:
function realpath()
{
f=$#
if [ -d "$f" ]; then
base=""
dir="$f"
else
base="/$(basename -- "$f")"
dir="$(dirname -- "$f")"
fi
dir="$(cd -- "$dir" && /bin/pwd)"
echo "$dir$base"
}
Related:
How to detect the current directory in which I run my shell script?
How do I get the directory where a Bash script is located from within the script itself?
Bash script absolute path with OS X
Reliable way for a Bash script to get the full path to itself
See also:
How can I get the behavior of GNU's readlink -f on a Mac?
cd "$(dirname "${BASH_SOURCE[0]}")"
It's easy. It works.
The accepted answer works well for scripts that have not been symlinked elsewhere, such as into $PATH.
#!/bin/bash
cd "$(dirname "$0")"
However if the script is run via a symlink,
ln -sv ~/project/script.sh ~/bin/;
~/bin/script.sh
This will cd into the ~/bin/ directory and not the ~/project/ directory, which will probably break your script if the purpose of the cd is to include dependencies relative to ~/project/
The symlink safe answer is below:
#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" # cd current directory
readlink -f is required to resolve the absolute path of the potentially symlinked file.
The quotes are required to support filepaths that could potentially contain whitespace (bad practice, but its not safe to assume this won't be the case)
This script seems to work for me:
#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd
The pwd command line echoes the location of the script as the current working directory no matter where I run it from.
There are a lot of correct answers in here, but one that tends to be more useful for me (making sure a script's relative paths remain predictable/work) is to use pushd/popd:
pushd "$(dirname ${BASH_SOURCE:0})"
trap popd EXIT
# ./xyz, etc...
This will push the source file's directory on to a navigation stack, thereby changing the working directory, but then, when the script exits (for whatever reason, including failure), the trap will run popd, restoring the current working directory before it was executed. If the script were to cd and then fail, your terminal could be left in an unpredictable state after the execution ends - the trap prevents this.
I take this and it works.
#!/bin/bash
cd "$(dirname "$0")"
CUR_DIR=$(pwd)
Get the real path to your script
if [ -L $0 ] ; then
ME=$(readlink $0)
else
ME=$0
fi
DIR=$(dirname $ME)
(This is answer to the same my question here: Get the name of the directory where a script is executed)
cd "`dirname $(readlink -f ${0})`"
Most answers either don't handle files which are symlinked via a relative path, aren't one-liners or don't handle BSD (Mac). A solution which does all three is:
HERE=$(cd "$(dirname "$BASH_SOURCE")"; cd -P "$(dirname "$(readlink "$BASH_SOURCE" || echo .)")"; pwd)
First, cd to bash's conception of the script's directory. Then readlink the file to see if it is a symlink (relative or otherwise), and if so, cd to that directory. If not, cd to the current directory (necessary to keep things a one-liner). Then echo the current directory via pwd.
You could add -- to the arguments of cd and readlink to avoid issues of directories named like options, but I don't bother for most purposes.
You can see the full explanation with illustrations here:
https://www.binaryphile.com/bash/2020/01/12/determining-the-location-of-your-script-in-bash.html
echo $PWD
PWD is an environment variable.
If you just need to print present working directory then you can follow this.
$ vim test
#!/bin/bash
pwd
:wq to save the test file.
Give execute permission:
chmod u+x test
Then execute the script by ./test then you can see the present working directory.

Resources