21 апреля 2011 г.

Как узнать полный путь до запущенного bash-скрипта

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

Можно, конечно, поступить просто и в самом скрипте в переменной жёстко прописать полный путь до каталога, в котором этот скрипт лежит. Получится что-то вроде этого:
#!/bin/bash

DIRECTORY="/home/user/my_scripts"
cd $DIRECTORY

# запуск "соседних" скриптов
./script1.sh
./script2.sh
Вполне рабочий вариант и две обозначенные выше проблемы будут решены, но если надо будет переместить скрипт в другой каталог, то и значение переменной придётся руками поменять в самом скрипте. Не очень удобно.

Для красивого решения проблемы нужно знать всего три вещи:
  1. Путь до выполняющегося скрипта можно узнать с помощью $0, но проблема в том, что он относительный, т.е. если вы запустите скрипт как ./script.sh, то и $0 будет содержать ./script.sh
  2. Команда readlink с параметром -e решит сразу две проблемы: во-первых она вернет полный путь до файла скрипта, если вы воспользовались для запуска символической ссылкой (даже если это была цепочка симлинков), а во-вторых преобразует относительный путь, если такой получен с помощью $0, в абсолютный
  3. Чтобы избавиться от имени файла скрипта в конце абсолютного пути, нужно воспользоваться командой dirname
    Пример:
    #!/bin/bash
    
    # полный путь до скрипта
    ABSOLUTE_FILENAME=`readlink -e "$0"`
    # каталог в котором лежит скрипт
    DIRECTORY=`dirname "$ABSOLUTE_FILENAME"`
    
    # запуск "соседних" скриптов
    $DIRECTORY/script1.sh
    $DIRECTORY/script2.sh
    

    13 комментариев:

    1. А если не воспользовались для запуска символической ссылкой?
      Да и к чему городить огород.
      Проще так:
      DIRSCRIPT=$(pwd) # Текущая директория
      echo "${DIRSCRIPT}"

      ОтветитьУдалить
      Ответы
      1. Поправка.. Короче, разобрался. Все это туфта и то и другое.
        Вот универсальное отличное решение, особенно, если учитывать, что во фряхе нет "readlink"..

        DIRROOT=$(cd $(dirname $0) && pwd)
        echo "${DIRROOT}"

        И запускайте скрипт как хотите..

        Удалить
      2. > А если не воспользовались для запуска символической ссылкой?
        Отработает всё совершенно корректно

        А вы уверены, что называть туфтой решение в котором вы так и не разобрались хорошая идея?

        > во фряхе нет "readlink"
        Где я писал что это должно работать во фрибсд? Прочитайте название блога и сообщения.
        Я не использую фрибсд, но позволю себе поинтересоваться, а какой интерпретатор команд вы используете в ней? На сколько мне известно, по умолчанию там tcsh или csh, а совсем не bash.

        > И запускайте скрипт как хотите
        Да что вы? Оукей. Тогда создайте символьную ссылку на свой скрипт в другом каталоге (не в том в котором лежит скрипт), войдите в этот каталог и выполните симлинк. Сильно удивитесь. Ваше решение вернет путь до каталога, в котором находится симлинк, вместо каталога в котором находится сам скрипт. Надеюсь доступно объяснил.

        А еще поясните, пожалуйста, чем решение из двух команд, приведенное мной, "огороднее" вашего решения из трёх команд? Кроме того, переход в каталог для того что бы узнать путь до него немножко попахивает костылем, вам так не кажется? Но это я уже придираюсь, конечно, т. к. в баше без костылей никуда.

        Успехов.

        Удалить
    2. спасибо
      для себя немного упростил
      cd $(dirname $(readlink -e $0))
      далее уже работаю с внутренней структурой

      ОтветитьУдалить
      Ответы
      1. Пожалуйста! Разумно. Я написал в несколько шагов для того, чтобы было проще пояснить каждый из них в комментариях.

        Удалить
      2. http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in и http://stackoverflow.com/questions/19313056/what-platform-independent-way-to-find-directory-of-shell-executable-in-shell-scr

        Удалить
    3. Огромное спасибо! Очень нужный скрипт, теперь пользую везде )

      ОтветитьУдалить
    4. Спасибо, очень полезно.
      Обычно использовал:
      script_dir=$(dirname $0)
      сd ${script_dir}/..
      Но если пулять скрипт откуда-то из другого места, как это обычно и происходит, да еще и по ссылке, то нифига это не спасало. А предложенный вариант (упрощенный, как выше написал аноним) очень удобен и работает.
      Еще раз спасибо!

      ОтветитьУдалить
    5. Не работает.

      Debian 8 jessie stable
      Скрипт лежит в папке /usr/local/etc/test/script.sh
      Символьная ссылка находится в /usr/local/bin/script
      Запускаю из домашней директории

      Вывод
      echo $0 # выведет "script"
      echo $ABSOLUTE_FILENAME # выведет "."
      echo $DIRECTORY # выведет ... ничего

      ЧЯДНТ?

      ОтветитьУдалить
      Ответы
      1. Работает.

        eqlbin:~ # cat /etc/test/script.sh
        #!/bin/bash

        ABSOLUTE_FILENAME=`readlink -e "$0"`
        DIRECTORY=`dirname "$ABSOLUTE_FILENAME"`

        echo ABSOLUTE_FILENAME=$ABSOLUTE_FILENAME
        echo DIRECTORY: $DIRECTORY

        eqlbin:~ # sh /usr/local/bin/script
        ABSOLUTE_FILENAME=/etc/test/script.sh
        DIRECTORY: /etc/test

        ЧЯДНТ?

        Какой вопрос - такой ответ

        Удалить
    6. Спасибо! Ваша конструкция пригодилось при запуске скрипта по ссш на удаленной машине. По умолчанию директория была: ~. А она возвращает именно абсолютный путь к скрипту.

      ОтветитьУдалить