Зверніться до файлу під тим же каталогом сценарію, який можна знайти в $ PATH


33

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

Є ще один текстовий файл у тому ж каталозі, що і сценарій. Цікаво, як посилатися на текстовий файл у сценарії?

Наприклад, якщо сценарій призначений лише для виведення вмісту текстового файлу, cat textfileне вийде, оскільки при виклику сценарію з іншого каталогу текстовий файл не знайдений.



Це питання відповідає як отримати Баш файли сценарію шлях надійно, я додав , що на моєму шляху: stackoverflow.com/q/4774054/1695680
ThorSummoner

Відповіді:


24

Вони повинні працювати так само, доки немає символьних посилань (у розширенні контуру або в самому сценарії):

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

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

  • Двоетапна версія будь-якого з перерахованих вище:

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

    MYDIR="${MYSELF%/*}"

Якщо на шляху до вашого сценарію є симпосилання, тоді ви whichотримаєте відповідь, не включаючи роздільну здатність цього посилання. Якщо система realpathне встановлена ​​за замовчуванням у вашій системі, її можна знайти тут .

[EDIT]: Оскільки, здається, realpathнемає переваги перед readlink -f запропонованим Калебом , можливо, краще використовувати останній. Мої тести на терміни показують, що це насправді швидше.


Без проблем. До речі, звідки realpathберуться у вашій системі. (Для інших, хто цього не має, ви можете використовуватиreadlink -f
Caleb

@Caleb Насправді я думав, що він належить до набору стандартних утиліт GNU (coreutils), але я можу побачити, що це окремий пакет .
rozcietrzewiacz

@rozcietrzewiacz realpathдатується раніше readlink -f(і навіть readlink, IIRC) був у GNU coreutils (існувало декілька подібних інструментів. З readlink -fчасом він став фактичним стандартом); realpathзберігається лише для сумісності зі сценаріями, які все ще використовують його.
Жил 'ТАК - перестань бути злим'

Яка перевага $(dirname "$(which "$0")")над тим, $(dirname $0)де цього whichнемає більше? Хіба це не те саме?
UlfR

readlink -fСхоже, він не працює на Mac OS X 10.11.6, але realpathпрацює поза коробкою.
Грав

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:

$ cat "$(dirname -- "$0")/textfile"

Хоча це, здається, працює і без цього realpath $0, ви помиляєтесь, сказавши, що " $0в сценарії буде повний шлях до сценарію".
rozcietrzewiacz

@roz Яким чином?
Майкл Мрозек

1
$0це команда, як вона була запущена, яка може бути, наприклад ../script.sh.
rozcietrzewiacz

Отже, насправді $(dirname "$0")повертає відносний шлях до сценарію, як частина викликаної команди - не абсолютний шлях. Це може призвести до проблем у скриптах, які змінюють каталоги під час роботи.
rozcietrzewiacz

@roz Ах, цікаво. Тож я думаю, це не спричинило б тут проблем, оскільки він називає щось на шляху по імені, але це порушить інші речі. Спасибі
Михайло Мрозек

4

Ви можете розмістити це вгорі свого сценарію:

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

Внутрішня змінна bash BASH_SOURCE - це фактично масив імен шляхів. Якщо розгорнути його як простий рядок, наприклад, "$ BASH_SOURCE", ви отримаєте перший елемент, який є іменем шляху виконуваної в даний час функції або сценарію.

Джерело: http://mywiki.wooledge.org/BashFAQ/028


2

Я намагався це зробити, і realpath не працював для мене. Я вирішив:

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

Що добре працювало до цих пір. Мені хотілося б знати, чи є якісь потенційні проблеми з підходом.


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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.