Как лучше включить другие скрипты? - PullRequest
319 голосов
/ 10 октября 2008

Обычно вы включаете скрипт с помощью «source»

например:

main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

incl.sh:

echo "The included script"

Результат выполнения "./main.sh":

The included script
The main script

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

Какой хороший способ убедиться, что ваш сценарий может найти сценарий включения, особенно если, например, сценарий должен быть переносимым?

Ответы [ 20 ]

204 голосов
/ 10 октября 2008

Я стараюсь, чтобы все мои сценарии были связаны друг с другом. Таким образом, я могу использовать dirname:

#!/bin/sh

my_dir="$(dirname "$0")"

"$my_dir/other_script.sh"
164 голосов
/ 02 октября 2012

Я знаю, что опаздываю на вечеринку, но это должно работать независимо от того, как вы запускаете скрипт и использует исключительно встроенные функции:

DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"

. (точка) - псевдоним источника, $PWD - путь к рабочему каталогу, BASH_SOURCE - переменная массива, членами которой являются имена файлов источника, ${string%substring} отбирает самое короткое совпадение $ substring из задняя часть $ string

50 голосов
/ 10 октября 2008

Альтернатива:

scriptPath=$(dirname $0)

есть:

scriptPath=${0%/*}

.. преимущество заключается в отсутствии зависимости от dirname, которое не является встроенной командой (и не всегда доступно в эмуляторах)

37 голосов
/ 10 октября 2008

Если он находится в том же каталоге, вы можете использовать dirname $0:

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"
25 голосов
/ 23 сентября 2011

Я думаю, что лучший способ сделать это - использовать метод Криса Борана, НО вы должны вычислять MY_DIR следующим образом:

#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh

Цитировать справочные страницы для readlink:

readlink - display value of a symbolic link

...

  -f, --canonicalize
        canonicalize  by following every symlink in every component of the given 
        name recursively; all but the last component must exist

Я никогда не сталкивался с вариантом использования, в котором MY_DIR вычисляется неправильно. Если вы обращаетесь к своему сценарию через символическую ссылку в $PATH, он работает.

19 голосов
/ 14 июня 2009
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
18 голосов
/ 10 декабря 2015

Комбинация ответов на этот вопрос обеспечивает наиболее надежное решение.

У нас это работало в сценариях промышленного уровня с отличной поддержкой зависимостей и структуры каталогов:

#!/bin/bash

# Full path of the current script
THIS=`readlink -f "${BASH_SOURCE[0]}" 2>/dev/null||echo $0`

# The directory where current script resides
DIR=`dirname "${THIS}"`

# 'Dot' means 'source', i.e. 'include':
. "$DIR/compile.sh"

Метод поддерживает все это:

  • Пробелы в пути
  • Ссылки (через readlink)
  • ${BASH_SOURCE[0]} более устойчив, чем $0
12 голосов
/ 12 августа 2015

Это работает, даже если скрипт получен:

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
7 голосов
/ 10 октября 2008

Вам нужно указать расположение других скриптов, другого пути нет. Я бы порекомендовал настраиваемую переменную вверху вашего скрипта:

#!/bin/bash
installpath=/where/your/scripts/are

. $installpath/incl.sh

echo "The main script"

В качестве альтернативы вы можете настаивать на том, чтобы пользователь поддерживал переменную среды, указывающую, где находится ваша программа, например PROG_HOME или что-то подобное. Это может быть предоставлено пользователю автоматически путем создания сценария с этой информацией в /etc/profile.d/, который будет запрашиваться при каждом входе пользователя в систему.

6 голосов
/ 07 апреля 2018

1. Опрятный

Я изучил почти все предложения, и вот одно из них, которое сработало для меня:

script_root=$(dirname $(readlink -f $0))

Работает, даже если скрипт связан с каталогом $PATH.

Смотрите это в действии здесь: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent

2. Самый крутой

# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')

Это фактически из другого ответа на этой самой странице, но я тоже добавляю его в свой ответ!

2. Самый надежный

В качестве альтернативы, в тех редких случаях, когда они не работали, вот пуленепробиваемый подход:

# Copyright http://stackoverflow.com/a/7400673/257479
myreadlink() { [ ! -h "$1" ] && echo "$1" || (local link="$(expr "$(command ls -ld -- "$1")" : '.*-> \(.*\)$')"; cd $(dirname $1); myreadlink "$link" | sed "s|^\([^/].*\)\$|$(dirname $1)/\1|"); }
whereis() { echo $1 | sed "s|^\([^/].*/.*\)|$(pwd)/\1|;s|^\([^/]*\)$|$(which -- $1)|;s|^$|$1|"; } 
whereis_realpath() { local SCRIPT_PATH=$(whereis $1); myreadlink ${SCRIPT_PATH} | sed "s|^\([^/].*\)\$|$(dirname ${SCRIPT_PATH})/\1|"; } 

script_root=$(dirname $(whereis_realpath "$0"))

Вы можете увидеть это в действии в taskrunner источник: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner

Надеюсь, это поможет кому-то там:)

Также, пожалуйста, оставьте его в качестве комментария, если он вам не подходит, и укажите вашу операционную систему и эмулятор. Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...