Обратитесь к файлу в том же каталоге скрипта, который находится в $ PATH

33

У меня есть файл сценария bash, который помещен в какой-то каталог, добавленный в $ PATH, чтобы я мог вызывать сценарий из любого каталога.

В том же каталоге, что и скрипт, находится другой текстовый файл. Интересно, как ссылаться на текстовый файл в скрипте?

Например, если скрипт просто выводит содержимое текстового файла, cat textfileработать не будет, поскольку при вызове скрипта из другого каталога текстовый файл не найден.

Тим
источник
Этот вопрос отвечает на вопрос о том, как надежно получить путь к файлам сценариев bash. Я добавил это к своему пути: stackoverflow.com/q/4774054/1695680
ThorSummoner

Ответы:

24

Они должны работать одинаково, если нет символических ссылок (в расширении пути или в самом скрипте):

  • MYDIR="$(dirname "$(realpath "$0")")"

  • MYDIR="$(dirname "$(which "$0")")"

  • Двухэтапная версия любого из вышеперечисленного:

    MYSELF="$(realpath "$0")"

    MYDIR="${MYSELF%/*}"

Если на пути к вашему сценарию есть символическая ссылка, вы whichполучите ответ, не включающий разрешение этой ссылки. Если realpathпо умолчанию не установлен в вашей системе, вы можете найти его здесь .

[РЕДАКТИРОВАТЬ]: Поскольку кажется, что не realpathимеет никаких преимуществ по сравнению с readlink -f предложенным Калебом , вероятно, лучше использовать последний. Мои временные тесты показывают, что это на самом деле быстрее.

rozcietrzewiacz
источник
Нет проблем. Кстати, откуда realpathв вашей системе? (Для тех, у кого его нет, вы можете использоватьreadlink -f
Caleb
@Caleb На самом деле я думал, что он принадлежит к ряду стандартных утилит GNU (coreutils), но теперь я вижу, что это отдельный пакет .
rozcietrzewiacz
@rozcietrzewiacz realpathвосходит к тому времени readlink -f(и даже readlinkIIRC), который был в GNU coreutils (было несколько подобных инструментов. В readlink -fконечном итоге он стал стандартом де-факто); realpathхранится только для совместимости со скриптами, которые все еще используют его.
Жиль "ТАК - перестань быть злым"
В чем преимущество $(dirname "$(which "$0")")перед тем, $(dirname $0)где whichбольше нет? Разве это не то же самое?
UlfR
readlink -fпохоже, не работает на Mac OS X 10.11.6, но realpathработает из коробки.
Grav
9

Мои системы не имеют, realpathкак предложено rozcietrzewiacz .

Вы можете сделать это с помощью readlinkкоманды. Преимущество использования этого по сравнению с синтаксическим анализом whichили другими решениями состоит в том, что даже если часть пути или имя исполняемого файла была символической ссылкой, вы сможете найти каталог, где находился фактический файл.

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

Ваш текстовый файл может быть затем прочитан в переменную, как это:

TEXTFILE="$(<$MYDIR/textfile)"
Калеб
источник
@rozcietrzewiacz: На самом деле я не имел в виду только ваше whichпредложение. Обычное решение для этого включает в себя либо просто, dirnameлибо комбинацию cdи pwdв подоболочке. Readlink имеет преимущество здесь. realpathв readlink -fлюбом случае, похоже, просто обертка .
Калеб
Я не знаю, чем realpathотличается от readlink -f. Я могу только видеть, что это дает мне те же результаты (в отличие от which).
rozcietrzewiacz
Имейте в виду, что readlink -f(из GNU coreutils) НЕ требуется, чтобы существовал последний элемент пути readlink -e, но он не поддерживается busybox readlink, что имитирует поведение -eих -fопции.
dragon788
8

$0в скрипте будет полный путь к скрипту, и он dirnameбудет принимать полный путь и даст вам только каталог, так что вы можете сделать это для cat textfile:

$ cat "$(dirname -- "$0")/textfile"
Михаил Мрозек
источник
Хотя кажется, что это работает без realpath $0, вы ошибаетесь, говоря, что « $0в скрипте будет полный путь к скрипту».
rozcietrzewiacz
@roz Каким образом?
Майкл Мрозек
1
$0это команда , как он был запущен, который может быть , например ../script.sh.
rozcietrzewiacz
Таким образом, фактически $(dirname "$0")возвращает относительный путь к сценарию, как часть вызванной команды, а не абсолютный путь. Это может привести к проблемам в скриптах, которые изменяют каталоги во время работы.
rozcietrzewiacz
@roz Ах, интересно. Так что я думаю, что это не вызовет проблем, так как он называет что-то на пути по имени, но это сломает другие вещи. Спасибо
Майкл Мрозек
4

Вы можете поместить это вверху вашего скрипта:

cd "${BASH_SOURCE%/*}" || exit

Внутренняя переменная BASH_SOURCE на самом деле является массивом путей. Если вы развернете его как простую строку, например «$ BASH_SOURCE», вы получите первый элемент, который является путем к текущей выполняемой функции или скрипту.

Источник: http://mywiki.wooledge.org/BashFAQ/028

Дэвид Кеннеди
источник
2

Я пробовал это, и RealPath не работал для меня. Решение, которое я выбрал:

SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

Который до сих пор хорошо работал. Я хотел бы знать, есть ли потенциальные проблемы с подходом.

Brettski
источник
1
Хорошо, но не следует по символическим ссылкам. Попробуйте это:SCRIPT_DIR="$( cd "$(dirname "$( readlink -f ${BASH_SOURCE[0]} )")" >/dev/null 2>&1 && pwd)"
OronNavon
1

Я использую:

#! /bin/sh -
dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P) || exit
dosomethingwith "${dir%/}/some-file"

Что POSIX и должен работать до тех пор , как имя папки из $0не заканчивается символом новой строки, не -и $CDPATHне установлен (и , возможно , несколько других случаев угловыми , если сценарий не смотрел, в $PATH).

Стефан Шазелас
источник
0

Я всегда использую, whichчтобы найти полный путь к исполняемому файлу из PATH. Например:

which python

Если вы объедините это с dirnameкомандой, то получите:

wp=`which python`
dn=`dirname $wp`
ls $dn
Майкл Диллон
источник