Як встановити поточний робочий каталог в каталог сценарію?


569

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

Поведінка за замовчуванням полягає в тому, що поточний робочий каталог у скрипті - це оболонка, з якої я його запускаю, але я не хочу цього поведінки.


Чи обмірковували ви розміщувати скрипт для обгортки десь на зразок / usr / bin, щоб ввести його у відповідний каталог (hardcoded), а потім виконати свій скрипт?
Dagg Nabbit

2
Для чого потрібен каталог сценарію? Можливо, є кращий спосіб вирішити основну проблему.
bstpierre

12
Я просто хотів би зазначити, що поведінка, яку ви називаєте "очевидно небажаною", насправді цілком необхідна - якщо я запускаю, myscript path/to/fileя очікую, що сценарій оцінить шлях / файл / файл відносно мого поточного каталогу, не залежно від каталогу, у якому відбувається сценарій Крім того, що б ви сталися зі сценарієм, з яким запускається ssh remotehost bash < ./myscriptFAQ BASH?
Гордон Девіссон


3
cd "${BASH_SOURCE%/*}" || exit
каре

Відповіді:


655
#!/bin/bash
cd "$(dirname "$0")"

7
dirname повертається "." при використанні bash під Windows. Отже, відповідь Павла краща.
Tvaroh

7
Також повертається "." в Mac OSX
Бен Клейтон

4
Варто зазначити, що все може зламатися, якщо частина символічного посилання складається з $0. Наприклад, у вашому сценарії ви можете розраховувати на те, що ../../він посилатиметься на два рівні над розташуванням сценарію, але це не обов'язково, якщо символічні посилання відтворюються.
Брайан Гордон

20
Якщо ви назвали скрипт як ./script, .- це правильний каталог, і зміна на .нього також виявиться в самому каталозі, де scriptзнаходиться, тобто в поточному робочому каталозі.
ndim

6
Якщо ви запускаєте скрипт із поточного каталогу так bash script.sh, то значення $0є script.sh. Єдиний спосіб, коли cdкоманда буде "працювати" для вас, це тому, що вам не байдуже невдалі команди. Якби ви використовували set -o errexit(aka:) set -eдля того, щоб ваш скрипт не видав минулі невдалі команди, це НЕ буде працювати, оскільки cd script.shце помилка. Надійний [баш-специфічний] спосібcd "$(dirname ${BASH_SOURCE[0]})"
Бруно Броноський

322

Також працює наступне:

cd "${0%/*}"

Синтаксис докладно описаний у цій відповіді StackOverflow.


17
Пояснення , як це працює: stackoverflow.com/questions/6393551 / ...
kenorb

25
Не забудьте укласти лапки, якщо шлях містить пробіли. тобто cd "$ {0% / *}"
H.Rabiee

17
Це не вдається, якщо сценарій викликається з bash script.sh- $0просто буде назва файлу
Chris Watts

17
Я б сказав, що ця відповідь занадто лаконічна. Ви ледве нічого не дізнаєтесь про синтаксис. Деякі описи про те, як це прочитати, було б корисно.
матч

2
Цей трюк не породжує передпластинку, тому це добре для швидких виродків.
текнопаул

184

Спробуйте наступні прості однолінійки:


Для всіх UNIX / OSX / Linux

dir=$(cd -P -- "$(dirname -- "$0")" && pwd -P)

Баш

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

Примітка: подвійний тире (-) використовується в командах для позначення кінця параметрів команд, тому файли, що містять тире або інші спеціальні символи, не порушують команду.

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


Для Linux, Mac та інших * BSD:

cd "$(dirname "$(realpath "$0")")";

Примітка. realpathПотрібно встановити за замовчуванням найпопулярніший дистрибутив Linux (наприклад, Ubuntu), але в деяких він може бути відсутнім, тому вам доведеться його встановити.

Примітка. Якщо ви використовуєте Bash, використовуйте ${BASH_SOURCE[0]}на користь $0, інакше шлях може перерватися при пошуку джерела ( source/ .).

Інакше ви можете спробувати щось подібне (він буде використовувати перший існуючий інструмент):

cd "$(dirname "$(readlink -f "$0" || realpath "$0")")"

Для ОС Linux:

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

Використання GNU readlink на * BSD / Mac:

cd "$(dirname "$(greadlink -f "$0")")"

Примітка: Вам потрібно coreutilsвстановити (наприклад, 1. Встановити Homebrew , 2. brew install coreutils).


В баш

У bash ви можете використовувати розширення параметрів, щоб досягти цього, наприклад:

cd "${0%/*}"

але він не працює, якщо сценарій запускається з одного каталогу.

Крім того, ви можете визначити наступну функцію в bash:

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

Ця функція бере 1 аргумент. Якщо аргумент вже має абсолютний шлях, надрукуйте його таким, яким він є, інакше надрукуйте $PWDзмінну + аргумент імені файлу (без ./префікса).

або ось версія, взята з .bashrcфайлу Debian :

function realpath()
{
    f=$@
    if [ -d "$f" ]; then
        base=""
        dir="$f"
    else
        base="/$(basename "$f")"
        dir=$(dirname "$f")
    fi
    dir=$(cd "$dir" && /bin/pwd)
    echo "$dir$base"
}

Пов'язані:

Дивись також:

Як я можу отримати поведінку зчитування GNU -f на Mac?


4
набагато краща відповідь, ніж популярні, оскільки вона вирішує систематичні посилання та облік різних ОС. Дякую!
Денніс

Дякую за цю відповідь. Верхня cpчастина працювала на моєму Mac ... але що робить перемикач у команді @kenorb?
TobyG

3
Подвійний тире ( --) використовується в командах для позначення кінця параметрів команд, тому файли, що містять тире або інші спеціальні символи, не порушують команду. Спробуйте, наприклад, створити файл через touch "-test"і touch -- -test, а потім видаліть файл через rm "-test"та rm -- -test, і побачите різницю.
kenorb

1
Я можу зняти свою пропозицію, якщо зможу: realpath застарілий unix.stackexchange.com/questions/136494/…
lsh

1
@lsh У ньому говориться, що лише realpathпакет Debian застарілий, а не GNU realpath. Якщо ви вважаєте, що це не зрозуміло, ви можете запропонувати змінити.
kenorb

73
cd "$(dirname "${BASH_SOURCE[0]}")"

Це легко. Це працює.


1
Це має бути прийнятою відповіддю. Працює на OSX та Linux Ubuntu.
Девід Ріссато Крус

5
Це чудово працює, коли сценарій B отримується з іншого сценарію A і вам потрібно використовувати шляхи відносно сценарію B. Дякую.
Енріко

5
Це також працює в Cygwin для тих, хто з нас невдалий, щоб торкнутися Windows.
Бруно Броноський

3
Або cd "${BASH_SOURCE%/*}" || exit
каре

Працює над macOS Mojave
Juan Boero

6

Прийнята відповідь добре працює для сценаріїв, які не були пов’язані ніде, наприклад, в $PATH.

#!/bin/bash
cd "$(dirname "$0")"

Однак якщо сценарій запускається через симпосилання,

ln -sv ~/project/script.sh ~/bin/; 
~/bin/script.sh

Це буде вводити в ~/bin/каталог, а не в ~/project/каталог, який, ймовірно, порушить ваш сценарій, якщо метою cdбуде включити залежності відносно~/project/

Безпечна відповідь символьного посилання наведена нижче:

#!/bin/bash
cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"

readlink -f потрібен для вирішення абсолютного шляху потенційно пов'язаного файлу.

Цитати необхідні для підтримки файлових шляхів, які потенційно можуть містити пробіли (погана практика, але не можна вважати, що це не буде так)


4

Цей сценарій, здається, працює для мене:

#!/bin/bash
mypath=`realpath $0`
cd `dirname $mypath`
pwd

Командний рядок pwd повторює розташування скрипту як поточного робочого каталогу, незалежно від того, звідки я його запускаю.


3
realpathнавряд чи буде встановлений скрізь. Що може не мати значення, залежно від ситуації з ОП.
bstpierre

У моїй системі немає, realpathале вона має, readlinkяка, схоже, схожа.
Призупинено до подальшого повідомлення.

2
У котрому dist не встановлено realpath за замовчуванням? Ubuntu має його.
kenorb


1
cd "`dirname $(readlink -f ${0})`"

Чи можете ви пояснити свою відповідь, будь ласка?
Зулу

1
Хоча цей код може допомогти вирішити проблему, він не пояснює, чому та / або як він відповідає на питання. Забезпечення цього додаткового контексту значно покращило б його довгострокове навчальне значення. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення, включаючи, які обмеження та припущення застосовуються.
Toby Speight


-6

Якщо вам просто потрібно надрукувати наявний робочий каталог, ви можете слідувати цьому.

$ vim test

#!/bin/bash
pwd
:wq to save the test file.

Надати дозвіл на виконання:

chmod u+x test

Потім виконайте сценарій, ./testтоді ви зможете побачити поточну робочу директорію.


2
Питання полягало у тому, як забезпечити запуск сценарію у власному каталозі, у тому числі якщо це не робочий каталог. Тож це не відповідь. У будь-якому випадку, навіщо робити це замість просто ... бігу pwd? Мені здається, багато мені марно натискає клавішу.
підкреслюйте_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.