paulo.albuquerque
paulo.albuquerque

Reputation: 292

Equivalent of %~dp0 (retrieving source file name) in sh

I'm converting some Windows batch files to Unix scripts using sh. I have problems because some behavior is dependent on the %~dp0 macro available in batch files.

Is there any sh equivalent to this? Any way to obtain the directory where the executing script lives?

Upvotes: 17

Views: 20849

Answers (9)

Don Johnny
Don Johnny

Reputation: 31

This is a script can get the shell file real path when executed or sourced.
Tested in bash, zsh, ksh, dash.

BTW: you shall clean the verbose code by yourself.

#!/usr/bin/env bash

echo "---------------- GET SELF PATH ----------------"
echo "NOW \$(pwd) >>> $(pwd)"
ORIGINAL_PWD_GETSELFPATHVAR=$(pwd)

echo "NOW \$0 >>> $0"
echo "NOW \$_ >>>  $_"
echo "NOW \${0##*/} >>> ${0##*/}"

if test -n "$BASH"; then
    echo "RUNNING IN BASH..."
    SH_FILE_RUN_PATH_GETSELFPATHVAR=${BASH_SOURCE[0]}
elif test -n "$ZSH_NAME"; then
    echo "RUNNING IN ZSH..."
    SH_FILE_RUN_PATH_GETSELFPATHVAR=${(%):-%x}
elif test -n "$KSH_VERSION"; then
    echo "RUNNING IN KSH..."
    SH_FILE_RUN_PATH_GETSELFPATHVAR=${.sh.file}
else
    echo "RUNNING IN DASH OR OTHERS ELSE..."
    SH_FILE_RUN_PATH_GETSELFPATHVAR=$(lsof -p $$ -Fn0 | tr -d '\0' | grep "${0##*/}" | tail -1 | sed 's/^[^\/]*//g')
fi

echo "EXECUTING FILE PATH: $SH_FILE_RUN_PATH_GETSELFPATHVAR"

cd "$(dirname "$SH_FILE_RUN_PATH_GETSELFPATHVAR")" || return 1

SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_RUN_PATH_GETSELFPATHVAR")

# Iterate down a (possible) chain of symlinks as lsof of macOS doesn't have -f option.
while [ -L "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR" ]; do
    SH_FILE_REAL_PATH_GETSELFPATHVAR=$(readlink "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR")
    cd "$(dirname "$SH_FILE_REAL_PATH_GETSELFPATHVAR")" || return 1
    SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_REAL_PATH_GETSELFPATHVAR")
done

# Compute the canonicalized name by finding the physical path
# for the directory we're in and appending the target file.
SH_SELF_PATH_DIR_RESULT=$(pwd -P)
SH_FILE_REAL_PATH_GETSELFPATHVAR=$SH_SELF_PATH_DIR_RESULT/$SH_FILE_RUN_BASENAME_GETSELFPATHVAR
echo "EXECUTING REAL PATH: $SH_FILE_REAL_PATH_GETSELFPATHVAR"
echo "EXECUTING FILE DIR: $SH_SELF_PATH_DIR_RESULT"

cd "$ORIGINAL_PWD_GETSELFPATHVAR" || return 1
unset ORIGINAL_PWD_GETSELFPATHVAR
unset SH_FILE_RUN_PATH_GETSELFPATHVAR
unset SH_FILE_RUN_BASENAME_GETSELFPATHVAR
unset SH_FILE_REAL_PATH_GETSELFPATHVAR
echo "---------------- GET SELF PATH ----------------"
# USE $SH_SELF_PATH_DIR_RESULT BEBLOW

Upvotes: 3

Badr Elmers
Badr Elmers

Reputation: 1706

The correct answer is this one: How do I determine the location of my script? I want to read some config files from the same place.

It is important to realize that in the general case, this problem has no solution. Any approach you might have heard of, and any approach that will be detailed below, has flaws and will only work in specific cases. First and foremost, try to avoid the problem entirely by not depending on the location of your script!

Before we dive into solutions, let's clear up some misunderstandings. It is important to understand that: Your script does not actually have a location! Wherever the bytes end up coming from, there is no "one canonical path" for it. Never. $0 is NOT the answer to your problem. If you think it is, you can either stop reading and write more bugs, or you can accept this and read on. ...

Upvotes: 2

Joel.L
Joel.L

Reputation: 11

This should work for bash shell:

dir=$(dirname $(readlink -m $BASH_SOURCE))

Test script:

#!/bin/bash
echo $(dirname $(readlink -m $BASH_SOURCE))

Run test:

$ ./somedir/test.sh 
/tmp/somedir
$ source ./somedir/test.sh 
/tmp/somedir
$ bash ./somedir/test.sh 
/tmp/somedir
$ . ./somedir/test.sh 
/tmp/somedir

Upvotes: 1

Steve Baker
Steve Baker

Reputation: 4373

In bash under linux you can get the full path to the command with:

readlink /proc/$$/fd/255

and to get the directory:

dir=$(dirname $(readlink /proc/$$/fd/255))

It's ugly, but I have yet to find another way.

Upvotes: 3

paulo.albuquerque
paulo.albuquerque

Reputation: 292

I was trying to find the path for a script that was being sourced from another script. And that was my problem, when sourcing the text just gets copied into the calling script, so $0 always returns information about the calling script.

I found a workaround, that only works in bash, $BASH_SOURCE always has the info about the script in which it is referred to. Even if the script is sourced it is correctly resolved to the original (sourced) script.

Upvotes: 2

Sarien
Sarien

Reputation: 6992

Yes, you can! It's in the arguments. :)

look at

${0}

combining that with

{$var%Pattern}
Remove from $var  the shortest part of $Pattern that matches the back end of $var.

what you want is just

${0%/*}

I recommend the Advanced Bash Scripting Guide (that is also where the above information is from). Especiall the part on Converting DOS Batch Files to Shell Scripts might be useful for you. :)

If I have misunderstood you, you may have to combine that with the output of "pwd". Since it only contains the path the script was called with! Try the following script:

#!/bin/bash
called_path=${0%/*}
stripped=${called_path#[^/]*}
real_path=`pwd`$stripped
echo "called path: $called_path"
echo "stripped: $stripped"
echo "pwd: `pwd`"
echo "real path: $real_path

This needs some work though. I recommend using Dave Webb's approach unless that is impossible.

Upvotes: 7

David Webb
David Webb

Reputation: 193714

The problem (for you) with $0 is that it is set to whatever command line was use to invoke the script, not the location of the script itself. This can make it difficult to get the full path of the directory containing the script which is what you get from %~dp0 in a Windows batch file.

For example, consider the following script, dollar.sh:

#!/bin/bash
echo $0

If you'd run it you'll get the following output:

# ./dollar.sh
./dollar.sh
# /tmp/dollar.sh
/tmp/dollar.sh

So to get the fully qualified directory name of a script I do the following:

cd `dirname $0`
SCRIPTDIR=`pwd`
cd -

This works as follows:

  1. cd to the directory of the script, using either the relative or absolute path from the command line.
  2. Gets the absolute path of this directory and stores it in SCRIPTDIR.
  3. Goes back to the previous working directory using "cd -".

Upvotes: 12

paulo.albuquerque
paulo.albuquerque

Reputation: 292

I have tried $0 before, namely:

dirname $0

and it just returns "." even when the script is being sourced by another script:

. ../somedir/somescript.sh

Upvotes: 0

C. K. Young
C. K. Young

Reputation: 223123

Try this:

${0%/*}

Upvotes: 2

Related Questions