Получить исходный каталог скрипта Bash из самого скрипта - программирование

Получить исходный каталог скрипта Bash из самого скрипта

Как получить путь к каталогу, в котором находится Bash script, внутри которого script?

Например, скажем, я хочу использовать Bash script в качестве запуска для другого приложения. Я хочу изменить рабочий каталог на тот, где находится Bash script, поэтому я могу работать с файлами в этом каталоге, например:

$ ./application
4b9b3361

Ответ 1

#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

полезный однострочный, который даст вам полное имя каталога скрипта независимо от того, откуда он вызывается.

Он будет работать до тех пор, пока последний компонент пути, используемый для поиска сценария, не является символической ссылкой (ссылки на каталоги в порядке). Если вы также хотите разрешить любые ссылки на сам скрипт, вам нужно многострочное решение:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

Этот последний будет работать с любой комбинацией псевдонимов, source, bash -c, символических ссылок и т.д.

Осторожно: если вы cd в другой каталог перед запуском этого фрагмента, результат может быть неверным!

Кроме того, $CDPATH ошибки $CDPATH и побочные эффекты вывода stderr, если у пользователя есть умный переопределение cd для перенаправления вывода на stderr (включая escape-последовательности, например, при вызове update_terminal_cwd >&2 для Mac). Добавление >/dev/null 2>&1 в конце вашей команды cd позаботится об обеих возможностях.

Чтобы понять, как это работает, попробуйте запустить эту более подробную форму:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

И это напечатает что-то вроде:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

Ответ 2

Используйте dirname "$ 0" :

<Предварительно > <код > #!/Bin/Баш echo "У вас запущен script basename` basename "$ 0" `, dirname` dirname '$ 0 "` "echo" Настоящий рабочий каталог -` pwd` " Код >

использование pwd не будет работать, если вы не используете script из каталога, в котором он содержится.

  [matt @server1 ~] $pwd
/Главная/матовый
[matt @server1 ~] $./test2.sh
script, который вы используете, имеет basename test2.sh, dirname.
Настоящий рабочий каталог /home/matt 
[matt @server1 ~] $cd/tmp
[matt @server1 tmp] $~/test2.sh
script, который вы используете, имеет basename test2.sh, dirname/home/matt
Настоящий рабочий каталог /tmp 
Код>

Ответ 3

Команда dirname является самой простой, просто анализируя путь до имени файла с переменной $0 (script name):

dirname "$0"

Но, как указал матовый b, возвращаемый путь отличается в зависимости от того, как вызывается script. pwd не выполняет эту работу, потому что это только говорит вам, что представляет собой текущий каталог, а не каталог, в котором находится script. Кроме того, если выполняется символическая ссылка на script, вы получите вероятно, относительный) путь туда, где находится ссылка, а не фактический script.

Некоторые другие упомянули команду readlink, но по своему простейшему вы можете использовать:

dirname "$(readlink -f "$0")"

readlink будет разрешать путь script к абсолютному пути из корня файловой системы. Таким образом, любые пути, содержащие одиночные или двойные точки, тильды и/или символические ссылки, будут разрешены для полного пути.

Здесь script, демонстрирующий каждый из них, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

Запуск этого script в моем домашнем каталоге, используя относительный путь:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

Опять же, но используя полный путь к script:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

Теперь изменяющиеся каталоги:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

И, наконец, используя символическую ссылку для выполнения script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

Ответ 4

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd 'dirname "$SCRIPT_PATH"'; 
  SCRIPT_PATH='readlink "${SCRIPT_PATH}"'; done
fi
cd 'dirname ${SCRIPT_PATH}' > /dev/null
SCRIPT_PATH='pwd';
popd  > /dev/null

Работает для всех версий, включая

  • при вызове через мягкую ссылку с множеством глубин,
  • когда файл
  • когда скрипт называется командой " source " . (точка).
  • когда arg $0 изменяется от вызывающего.
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

В качестве альтернативы, если сам скрипт bash является относительной символической ссылкой, вы хотите следовать ей и возвращать полный путь связанного скрипта:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd 'dirname "$SCRIPT_PATH"'; SCRIPT_PATH='readlink "${SCRIPT_PATH}"'; done
fi
cd 'dirname ${SCRIPT_PATH}' > /dev/null
SCRIPT_PATH='pwd';
popd  > /dev/null

SCRIPT_PATH задается полным путем, как бы он ни назывался.
Просто убедитесь, что вы нашли это в начале скрипта.

Этот комментарий и код Copyleft, выбираемая лицензия под GPL2.0 или более поздняя версия или CC-SA 3.0 (CreativeCommons Share Alike) или позже. (c) 2008. Все права защищены. Никаких гарантий. Вы были предупреждены.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

Ответ 6

Вы можете использовать $BASH_SOURCE

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Обратите внимание, что вам нужно использовать #!/bin/ bash, а не #!/bin/sh, так как его расширение bash

Ответ 7

Это должно сделать это:

DIR=$(dirname "$(readlink -f "$0")")

Работает с символическими ссылками и пробелами в пути. См. Man-страницы для dirname и readlink.

Изменить:

С комментария, похоже, не работает с Mac OS. Я понятия не имею, почему это так. Любые предложения?

Ответ 8

pwd можно использовать для поиска текущего рабочего каталога, а dirname для поиска каталога определенного файла (команда, которая была запущена, - $0, поэтому dirname $0 должна предоставить вам каталог текущий script).

Однако dirname дает точно часть каталога имени файла, которая скорее всего будет относиться к текущему рабочему каталогу. Если по какой-то причине ваш script должен изменить каталог, то вывод из dirname становится бессмысленным.

Я предлагаю следующее:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

Таким образом, вы получаете абсолютный, а не относительный каталог.

Так как script будет запущен в отдельном экземпляре bash, нет необходимости восстанавливать рабочий каталог впоследствии, но если вы по какой-либо причине хотите вернуться в свой script, вы можете легко присвойте значение pwd переменной перед изменением каталога для будущего использования.

Хотя просто

cd `dirname $0`

решает конкретный сценарий в вопросе, я считаю, что абсолютный путь к более полезному обычно.

Ответ 9

Я не думаю, что это так же просто, как другие сделали это. pwd не работает, так как текущий каталог не обязательно является каталогом с script. $0 также не всегда содержит информацию. Рассмотрим следующие три способа вызова script.

./script

/usr/bin/script

script

В первом и третьем способах $0 не имеет полной информации о пути. Во втором и третьем случае pwd не работает. Единственный способ получить dir в третьем способе - пропустить путь и найти файл с правильным совпадением. В принципе, код должен будет повторить то, что делает ОС.

Один из способов сделать то, что вы просите, - просто скопировать данные в каталог /usr/share и указать его полным путем. В любом случае данные не будут находиться в каталоге /usr/bin, так что, вероятно, это нужно сделать.

Ответ 10

SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

Ответ 11

Я устал приходить на эту страницу снова и снова, чтобы скопировать однострочный вкладыш в принятый ответ. Проблема в том, что это непросто понять и запомнить.

Вот простой сценарий:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be

Ответ 12

Получает текущий рабочий каталог в Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)

Ответ 13

$(dirname "$(readlink -f "$BASH_SOURCE")")

Ответ 14

Это конкретный Linux, но вы можете использовать:

SELF=$(readlink /proc/$$/fd/255)

Ответ 15

Вот однострочный совместимый с POSIX:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

Ответ 16

Я пробовал каждый из них, и никто из них не работал. Один был очень близок, но имел крошечную ошибку, которая сильно нарушала его; они забыли обернуть путь в кавычки.

Также многие полагают, что вы используете script из оболочки, поэтому забудьте, когда вы откроете новый script по умолчанию ваш дом.

Попробуйте этот каталог для размера:

/var/No one/Мысль/О пространствах Бытие/В каталоге/Имя/И здесь ваш файл .text

Это становится правильным, независимо от того, как и где вы его запускаете.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"

Итак, чтобы сделать это действительно полезным здесь, как перейти в каталог запуска script:

cd "`dirname "$0"`"

Надеюсь, что поможет

Ответ 17

Вот простой, правильный способ:

actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")

Пояснение:

  • ${BASH_SOURCE[0]} - полный путь к script. Значение этого параметра будет правильным, даже если источник script будет получен, например. source <(echo 'echo $0') печатает bash, а при замене его ${BASH_SOURCE[0]} будет напечатан полный путь к script. (Конечно, это предполагает, что вы в порядке, принимая зависимость от Bash.)

  • readlink -f - Рекурсивно разрешает любые символические ссылки по указанному пути. Это расширение GNU и недоступно для (например) BSD-систем. Если вы используете Mac, вы можете использовать Homebrew для установки GNU coreutils и вытеснить это с помощью greadlink -f.

  • И, конечно, dirname получает родительский каталог пути.

Ответ 18

Я бы использовал что-то вроде этого:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

Ответ 19

Небольшой пересмотр решения e-satis и 3bcdnlklvc04a указал в их ответ

SCRIPT_DIR=''
pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && {
    SCRIPT_DIR="$PWD"
    popd > /dev/null
}    

Это должно работать во всех случаях, которые они перечисляли.

РЕДАКТИРОВАТЬ: предотвратить появление popd после неудачного pushd, благодаря konsolebox

Ответ 20

#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

Ответ 21

$_ стоит упомянуть в качестве альтернативы $0. Если вы используете script из bash, принятый ответ можно сократить до:

DIR="$( dirname "$_" )"

Обратите внимание, что это должно быть первое выражение в вашем script.

Ответ 22

Я сравнил многие из приведенных ответов и придумал несколько более компактных решений. Кажется, что они обрабатывают все сумасшедшие грани, которые возникают из вашей любимой комбинации:

  • Абсолютные пути или относительные пути
  • Софт для файлов и каталогов
  • Вызов script, bash script, bash -c script, source script или . script
  • Пробелы, вкладки, новые строки, юникод и т.д. в каталогах и/или имени файла
  • Имена файлов, начинающиеся с дефиса

Если вы работаете в Linux, кажется, что использование дескриптора proc - лучшее решение для поиска полностью разрешенного источника текущего script (в интерактивном сеансе ссылка указывает на соответствующий /dev/pts/X):

resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"

У этого есть немного уродства, но исправление компактно и легко понять. Мы не используем только примитивы bash, но я в порядке, потому что readlink значительно упрощает задачу. echo X добавляет X в конец строки переменной, чтобы никакие конечные пробелы в имени файла не ели, а замена параметра ${VAR%X} в конце строки избавляется от X, Поскольку readlink добавляет новую строку (которая обычно будет использоваться в подстановке команд, если не для нашего предыдущего обмана), мы тоже должны избавиться от нее. Это проще всего выполнить с помощью схемы цитирования $'', которая позволяет использовать escape-последовательности, такие как \n для представления новых строк (это также то, как вы можете легко сделать коварно названные каталоги и файлы).

Вышеупомянутое должно охватывать ваши потребности в поиске текущего script в Linux, но если у вас нет файловой системы proc в вашем распоряжении или если вы пытаетесь найти полностью разрешенный путь некоторых другой файл, то, может быть, вы найдете приведенный ниже код полезным. Это лишь небольшая модификация от вышеупомянутого однострочного. Если вы играете со странным каталогом/именами файлов, проверка вывода с помощью ls и readlink информативна, так как ls выдаст "упрощенные" пути, заменив ? на такие вещи, как новые строки.

absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}

ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"

Ответ 23

Для систем, имеющих GNU coreutils readlink (например, linux):

$(readlink -f "$(dirname "$0")")

Нет необходимости использовать BASH_SOURCE когда $0 содержит имя файла сценария.

Ответ 24

Попробуйте использовать:

real=$(realpath $(dirname $0))

Ответ 25

Самый короткий и элегантный способ сделать это:

#!/bin/bash
DIRECTORY=$(cd 'dirname $0' && pwd)
echo $DIRECTORY

Это будет работать на всех платформах и очень чисто.

Более подробную информацию можно найти здесь: https://www.electrictoolbox.com/bash-script-directory/

Ответ 26

Попробуйте следующее кросс-совместимое решение:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

поскольку такие команды, как realpath или readlink могут быть недоступны (в зависимости от операционной системы).

Примечание. В Bash рекомендуется использовать ${BASH_SOURCE[0]} вместо $0, в противном случае путь может оборваться при поиске файла (source/.).

В качестве альтернативы вы можете попробовать следующую функцию в bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

Эта функция принимает 1 аргумент. Если аргумент уже имеет абсолютный путь, выведите его как есть, в противном случае выведите переменную $PWD + аргумент имени файла (без префикса ./).

Связанные с:

Ответ 27

Итак... Я считаю, что у меня есть этот. Поздно к вечеринке, но я думаю, что некоторые из них оценят, что именно здесь они сталкиваются с этой нитью. Комментарии должны быть объяснены.

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "[email protected]"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "[email protected]")"; link="$(readlink "$(basename "[email protected]")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "[email protected]" ]; then if $(ls -d "[email protected]" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "[email protected]" -a "$link" = "[email protected]" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "[email protected]" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "[email protected]" | cut -c1)" = '/' ]; then
   printf "[email protected]\n"; exit 0; else printf "$(pwd)/$(basename "[email protected]")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "[email protected]"
fi

Ответ 28

Это работает в bash -3.2:

path="$( dirname "$( which "$0" )" )"

Вот пример его использования:

Скажем, у вас есть каталог ~/bin, который находится в $PATH. У вас есть script A внутри этого каталога. Это источник s script ~/bin/lib/B. Вы знаете, где включен script относительно исходного (подкаталог lib), но не там, где он относится к текущему каталогу пользователя.

Это решается следующим (внутри A):

source "$( dirname "$( which "$0" )" )/lib/B"

Не имеет значения, где пользователь или как он называет script, это всегда будет работать.

Ответ 29

Хмм, если в basename и dirname пути просто не будет сокращаться и ходить по пути тяжело (что, если родитель не экспортировал PATH!). Однако оболочка должна иметь открытую ручку для своего script, а в bash дескриптор # 255.

SELF=`readlink /proc/$$/fd/255`

работает для меня.

Ответ 30

Подводя итог многим ответам:

    Script: "/tmp/src dir/test.sh"
    Calling folder: "/tmp/src dir/other"

Используемые команды

    echo Script-Dir : 'dirname "$(realpath $0)"'
    echo Script-Dir : $( cd ${0%/*} && pwd -P )
    echo Script-Dir : $(dirname "$(readlink -f "$0")")
    echo
    echo Script-Name : 'basename "$(realpath $0)"'
    echo Script-Name : 'basename $0'
    echo
    echo Script-Dir-Relative : 'dirname "$BASH_SOURCE"'
    echo Script-Dir-Relative : 'dirname $0'
    echo
    echo Calling-Dir : 'pwd'

Выход:

     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir
     Script-Dir : /tmp/src dir

     Script-Name : test.sh
     Script-Name : test.sh

     Script-Dir-Relative : ..
     Script-Dir-Relative : ..

     Calling-Dir : /tmp/src dir/other

Смотрите https://pastebin.com/J8KjxrPF