Получить путь к текущему сценарию при выполнении по символической ссылке

59

У меня есть утилита, состоящая из пары каталогов с некоторыми bash-скриптами и вспомогательными файлами, которые будут развернуты на нескольких машинах, возможно, в разных каталогах на каждой машине. Сценарии должны иметь возможность ссылаться на пути относительно себя, поэтому я должен иметь возможность получить путь к файлу, который выполняется в данный момент.

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

Пример общей структуры каталогов:

/
 |-usr/local/lib
 |  |-foo
 |  |  |-bin
 |  |  |  |-script.sh
 |  |  |-res
 |  |  |  |-resource_file.txt
 |-home/mike/bin
 |  |-link_to_script (symlink to /usr/local/lib/foo/bin/script.sh)

Как я могу надежно ссылаться /usr/local/lib/foo/res/resource_file.txtиз script.shтого , что вызывается /usr/local/lib/foo/bin/script.shили ~mike/bin/link_to_script?

Майк Дек
источник

Ответы:

80

Попробуйте это как универсальное решение:

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

В конкретном случае следующих символических ссылок вы также можете сделать это:

DIR="$(dirname "$(readlink -f "$0")")"
Калеб
источник
Работает отлично и намного проще, чем то, что я пробовал. Спасибо!
Майк Пал
@MikeDeck Наслаждайтесь. Обратите внимание, что я исправил некоторые проблемы с цитированием с момента первой публикации ... вы можете использовать опцию из последнего редактирования, если у вас есть пробелы или другие дурацкие символы в вашем пути когда-нибудь. Даже если это не имеет значения для этого проекта, вы можете скопировать код из ваших собственных сценариев в будущем, и тогда вам понадобится лучший вариант!
Калеб
1
Я должен уточнить, второй работает отлично. Первое решение, которое вы дали, не работает для символических ссылок в моей системе.
Майк Пал
4
@MikeDeck: первое решение работает для типичного случая, когда различные каталоги являются символическими ссылками в качестве части пути, но не поможет, если последний двоичный файл является символической ссылкой. Об этом позаботится второй.
Калеб
1
Не работает, если файл сценария sourced (например $ source ./sript.sh). Это работает в обоих случаях:DIR="$(cd "$(dirname "$BASH_SOURCE")" && pwd)"
FractalSpace
7

одна линия:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
diyism
источник
2
Ответы в одну строку обычно не самые полезные. Пожалуйста, рассмотрите возможность расширения своего ответа, включив в него пояснения, ссылки или документацию, которые дополнительно поддерживают предложенное вами решение.
HalosGhost
1
на самом деле, вам не нужно этого делать, версия оригинального ответа readlink работает просто отлично, если символическая ссылка вообще не используется
THESorcerer
3

readlink -f не работает для меня, у меня есть эта ошибка:

readlink: illegal option -- f
usage: readlink [-n] [file ...]

Но это прекрасно работает:

DIR="$(dirname "$(readlink "$0")")"
echo $DIR
Кевин Кэмпион
источник
3
Тогда вы находитесь на Mac, и это должно ответить на ваш вопрос .
Исаак
Да, я работаю на MacO @isaac, но у меня нет вопросов. Я разместил команду, которая работает на моей стороне.
Кевин Кэмпион
0

Одно небольшое дополнение к приведенному выше сценарию. Опция -P для pwd следует за символическими ссылками

DIR="$(cd "$(dirname "$0")" && pwd -P)"
user2108420
источник
7
Это работает только тогда, когда символическая ссылка является частью структуры каталога. Не работает, когда сам файл связан.
Оливер Гонджа