Jak najlepiej dołączyć Inne skrypty?

Sposób, w jaki zwykle dołączasz skrypt, to "źródło"

Eg:

Main.sh:

#!/bin/bash

source incl.sh

echo "The main script"

Incl.sh:

echo "The included script"

Wyjście wykonania". /main.sh " jest:

The included script
The main script

... Jeśli spróbujesz wykonać skrypt powłoki z innej lokalizacji, nie będzie on mógł znaleźć include, chyba że znajduje się on w twojej ścieżce.

Jaki jest dobry sposób, aby twój skrypt mógł znaleźć skrypt include, zwłaszcza jeśli na przykład skrypt musi być przenośny?

 372
Author: codeforester, 2008-10-10

21 answers

Staram się, aby moje Skrypty były względem siebie. W ten sposób mogę użyć dirname:

#!/bin/sh

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

"$my_dir/other_script.sh"
 237
Author: Chris Boran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-31 09:21:58

Wiem, że jestem spóźniony na imprezę, ale to powinno działać bez względu na to, jak uruchamiasz skrypt i używasz builtins wyłącznie:

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

. (kropka) polecenie jest aliasem do source, $PWD jest ścieżką do katalogu roboczego, BASH_SOURCE jest zmienną array, której członkowie są źródłowymi nazwami plików, ${string%substring} usuwa najkrótsze dopasowanie $ substring z tyłu $string

 194
Author: sacii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-07 00:47:37

Alternatywa dla:

scriptPath=$(dirname $0)

Jest:

scriptPath=${0%/*}

.. zaletą jest brak zależności od dirname, która nie jest wbudowanym poleceniem (i nie zawsze jest dostępna w emulatorach)

 52
Author: tardate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 18:18:55

Jeśli znajduje się w tym samym katalogu możesz użyć dirname $0:

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"
 43
Author: dsm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 17:17:55

Myślę, że najlepszym sposobem na to jest użycie sposobu Chrisa Borana, ale powinieneś obliczyć MY_DIR w ten sposób:

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

Aby zacytować strony podręcznika dla 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
Nigdy nie spotkałem się z przypadkiem użycia, w którym {[2] } nie jest poprawnie obliczony. Jeśli uzyskasz dostęp do skryptu przez dowiązanie symboliczne w $PATH, działa.
 28
Author: Mat131,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Połączenie odpowiedzi na to pytanie zapewnia najbardziej solidne rozwiązanie.

Pracował dla nas w skryptach klasy produkcyjnej z wielkim wsparciem zależności i struktury katalogów:

#!/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"

Metoda obsługuje wszystkie z nich:

  • spacje w ścieżce
  • Linki (via readlink)
  • ${BASH_SOURCE[0]} jest bardziej wytrzymały niż $0
 22
Author: Brian Cannard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-01 14:58:16
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
 20
Author: Max Arnold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-14 13:35:36

To działa nawet jeśli skrypt jest pozyskiwany:

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
 16
Author: Alessandro Pezzato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-12 09:08:28

1. Neatest

Zbadałem prawie każdą sugestię i oto najbardziej zgrabna, która dla mnie zadziałała:

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

Działa nawet wtedy, gdy skrypt jest dowiązany symbolicznie do katalogu $PATH.

Zobacz go w akcji tutaj: https://github.com/pendashteh/hcagent/blob/master/bin/hcagent

2. Najfajniejsze

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

To właściwie z innej odpowiedzi na tej stronie, ale dodaję ją również do mojej odpowiedzi!

2. Najbardziej niezawodny

Alternatywnie, w rzadkich przypadkach, że te nie zadziałały, oto podejście bullet proof:

# 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"))

Możesz zobaczyć go w akcji w taskrunner Źródło: https://github.com/pendashteh/taskrunner/blob/master/bin/taskrunner

Mam nadzieję, że to komuś pomoże :)

Również, proszę zostawić to jako komentarz, jeśli jeden nie działa dla Ciebie i wspomnieć o systemie operacyjnym i emulator. Dzięki!

 11
Author: Alexar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Musisz określić lokalizację innych skryptów, nie ma innego sposobu na obejście tego. Polecam konfigurowalną zmienną u góry skryptu:

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

. $installpath/incl.sh

echo "The main script"

Alternatywnie, możesz nalegać, aby użytkownik utrzymywał zmienną środowiskową wskazującą, gdzie znajduje się twój program home, jak PROG_HOME lub somesuch. Można to podać użytkownikowi automatycznie, tworząc skrypt z tą informacją w /etc / profile.d/ , które będą pozyskiwane za każdym razem, gdy użytkownik zaloguje się.

 8
Author: Steve Baker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 17:32:52

Sugerowałbym utworzenie skryptu setenv, którego jedynym celem jest dostarczenie lokalizacji dla różnych komponentów w całym systemie.

Wszystkie Inne skrypty będą źródłem tego skryptu tak, że wszystkie lokalizacje są wspólne dla wszystkich skryptów korzystających ze skryptu setenv.

Jest to bardzo przydatne podczas uruchamiania cronjobs. Dostajesz Minimalne środowisko podczas uruchamiania crona, ale jeśli wszystkie skrypty crona zawierają najpierw skrypt setenv, możesz kontrolować i synchronizować środowisko, w którym chcesz wykonać cronjobs.

Zastosowaliśmy taką technikę na naszej małpie konstrukcyjnej, która była używana do ciągłej integracji w całym projekcie około 2000 kSLOC.

 6
Author: Rob Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-10 17:18:51

ODPOWIEDŹ Steve ' a jest zdecydowanie prawidłową techniką, ale powinna być zrefakturowana tak, aby zmienna installpath znajdowała się w osobnym skrypcie środowiskowym, w którym wszystkie takie deklaracje są dokonywane.

Wtedy wszystkie skrypty źródłowe tego skryptu i powinny się zmienić installpath, wystarczy zmienić go tylko w jednym miejscu. Sprawia, że rzeczy są bardziej przyszłościowe. Boże, nienawidzę tego słowa! (-:

BTW naprawdę powinieneś odnieść się do zmiennej używając ${installpath}, gdy używasz jej w sposób pokazany w Twoim przykład:

. ${installpath}/incl.sh

Jeśli szelki zostaną pominięte, niektóre powłoki spróbują rozwinąć zmienną "installpath/incl.sh"!

 3
Author: Rob Wells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-11 11:35:12

Shell Script Loader to moje rozwiązanie.

Zapewnia funkcję o nazwie include (), która może być wywołana wiele razy w wielu skryptach w celu odwołania się do jednego skryptu, ale ładuje skrypt tylko raz. Funkcja może przyjmować pełne ścieżki lub częściowe ścieżki (skrypt jest przeszukiwany w ścieżce wyszukiwania). Dostarczona jest również podobna funkcja load (), która bezwarunkowo ładuje Skrypty.

To działa dla bash, ksh, pd ksh i zsh z zoptymalizowane skrypty dla każdego z nich; i inne powłoki, które są ogólnie kompatybilne z oryginalnym SH jak ash, dash, heirloom sh , itp., poprzez uniwersalny skrypt, który automatycznie optymalizuje swoje funkcje w zależności od funkcji, jakie może zapewnić powłoka.

[Fowarded przykład]

Start.sh

Jest to opcjonalny skrypt startowy. Umieszczenie tutaj metod uruchamiania jest tylko wygodą i może być umieszczony w skrypcie głównym. Ten skrypt nie jest również potrzebny, jeśli skrypty mają być skompilowane.

#!/bin/sh

# load loader.sh
. loader.sh

# include directories to search path
loader_addpath /usr/lib/sh deps source

# load main script
load main.sh

Main.sh

include a.sh
include b.sh

echo '---- main.sh ----'

# remove loader from shellspace since
# we no longer need it
loader_finish

# main procedures go from here

# ...

A.sh

include main.sh
include a.sh
include b.sh

echo '---- a.sh ----'

B.sh

include main.sh
include a.sh
include b.sh

echo '---- b.sh ----'

Wyjście:

---- b.sh ----
---- a.sh ----
---- main.sh ----

Najlepsze jest to, że skrypty bazujące na nim mogą być również skompilowane w jeden skrypt z dostępnym kompilatorem.

Oto projekt, który go używa: http://sourceforge.net/p/playshell/code/ci/master/tree / . It może działać przenośnie z kompilacją skryptów lub bez niej. Kompilacja w celu wytworzenia pojedynczego skryptu może się również zdarzyć i jest pomocna podczas instalacji.

Stworzyłem również prostszy prototyp dla każdej Partii Konserwatywnej, która może chcieć mieć krótkie pojęcie o tym, jak działa skrypt implementacyjny: https://sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype.bash . jest mały i każdy może po prostu umieścić kod w swoim skrypcie głównym, jeśli chce, jeśli jego kod jest przeznaczony do uruchomienia z Bash 4.0 lub nowszym, a także nie używa eval.

 3
Author: konsolebox,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-28 17:29:48

Osobiście umieść wszystkie biblioteki w folderze lib i użyj funkcji import, aby je załadować.

struktura folderów

Tutaj wpisz opis obrazka

script.sh spis treści

# Imports '.sh' files from 'lib' directory
function import()
{
  local file="./lib/$1.sh"
  local error="\e[31mError: \e[0mCannot find \e[1m$1\e[0m library at: \e[2m$file\e[0m"
  if [ -f "$file" ]; then
     source "$file"
    if [ -z $IMPORTED ]; then
      echo -e $error
      exit 1
    fi
  else
    echo -e $error
    exit 1
  fi
}

Zauważ, że ta funkcja importu powinna znajdować się na początku skryptu, a następnie możesz łatwo zaimportować swoje biblioteki w następujący sposób:

import "utils"
import "requirements"

Dodaj pojedynczą linię u góry każdej biblioteki (np. utils.sh):

IMPORTED="$BASH_SOURCE"

Teraz masz dostęp do funkcji wewnątrz utils.sh i requirements.sh od script.sh

TODO: napisz linker, aby zbudować pojedynczy sh plik

 2
Author: Xaqron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-10 09:36:27

Użycie source lub $0 nie da ci prawdziwej ścieżki skryptu. Możesz użyć ID procesu skryptu, aby pobrać jego prawdziwą ścieżkę

ls -l       /proc/$$/fd           | 
grep        "255 ->"            |
sed -e      's/^.\+-> //'

Używam tego skryptu i zawsze mi dobrze służył:)

 1
Author: francoisrv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-04 22:26:10

Umieściłem wszystkie moje skrypty startowe w .bashrc.katalog D. Jest to powszechna technika w takich miejscach jak /etc / profile.d, itp.

while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE

Problem z rozwiązaniem za pomocą globbingu...

for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done

...możesz mieć listę plików, która jest "zbyt długa". Takie podejście...

find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done

...działa, ale nie zmienia środowiska zgodnie z życzeniem.

 1
Author: phreed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-13 22:33:19

Oczywiście, dla każdego ich własnych, ale myślę, że blok poniżej jest dość solidny. Uważam, że wiąże się to z "najlepszym" sposobem znalezienia katalogu i "najlepszym" sposobem wywołania innego skryptu bash:

scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh

echo "The main script"

Więc może to być "najlepszy" sposób na dołączenie innych skryptów. Jest to oparte na innej "najlepszej" odpowiedzi, która mówi skryptowi bash, gdzie jest przechowywany

 1
Author: modulitos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:45

To powinno działać niezawodnie:

source_relative() {
 local dir="${BASH_SOURCE%/*}"
 [[ -z "$dir" ]] && dir="$PWD"
 source "$dir/$1"
}

source_relative incl.sh
 1
Author: PSkocik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-04 21:22:43

Musimy tylko znaleźć folder, w którym nasz incl.sh oraz main.sh jest przechowywany; wystarczy zmienić swoje main.sh z tym:

Main.sh

#!/bin/bash

SCRIPT_NAME=$(basename $0)
SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")"
source $SCRIPT_DIR/incl.sh

echo "The main script"
 0
Author: fastrizwaan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-16 13:35:51

Według man hier odpowiednie miejsce dla Script includes to /usr/local/lib/

/usr / local / lib

Pliki związane z lokalnie zainstalowanymi programami.

Osobiście wolę /usr/local/lib/bash/includes dla includes. Istnieje bash-helper lib do włączania bibliotek w ten sposób:

#!/bin/bash

. /usr/local/lib/bash/includes/bash-helpers.sh

include api-client || exit 1                   # include shared functions
include mysql-status/query-builder || exit 1   # include script functions

# include script functions with status message
include mysql-status/process-checker; status 'process-checker' $? || exit 1
include mysql-status/nonexists; status 'nonexists' $? || exit 1

Bash-helpers zawiera wyjście stanu

 0
Author: Alexander Yancharuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-04 13:01:05

Możesz również użyć:

PWD=$(pwd)
source "$PWD/inc.sh"
 -5
Author: Django,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-19 16:13:32