あなたが通常スクリプトをインクルードする方法は "source"です。
例えば:
main.sh:
#!/bin/bash
source incl.sh
echo "The main script"
sh:
echo "The included script"
"./main.sh"を実行した結果は次のとおりです。
The included script
The main script
...さて、あなたが別の場所からそのシェルスクリプトを実行しようとすると、それはあなたのパスになければインクルードを見つけることができません。
あなたのスクリプトがincludeスクリプトを見つけることができることを確実にするための良い方法は何ですか、特に、例えばスクリプトが移植可能である必要があるならば?
私は自分のスクリプトをすべて互いに相対的なものにする傾向があります。そうすれば、dirnameを使うことができます。
#!/bin/sh
my_dir="$(dirname "$0")"
"$my_dir/other_script.sh"
私はパーティーに遅刻していることを私は知っています、しかしこれはあなたがスクリプトを始めて、排他的にビルトインを使う方法に関係なく働くべきです:
DIR="${BASH_SOURCE%/*}"
if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi
. "$DIR/incl.sh"
. "$DIR/main.sh"
.
(dot)コマンドはsourceの別名、$PWD
は作業ディレクトリのパス、BASH_SOURCE
はメンバーがソースファイル名である配列変数、${string%substring}
は$ stringの後ろから$ substringの最短一致を削除します
に代わるもの:
scriptPath=$(dirname $0)
です:
scriptPath=${0%/*}
..組み込みコマンドではないdirnameに依存しないという利点(およびエミュレータで常に利用できるわけではない)
同じディレクトリにある場合はdirname $0
を使用できます。
#!/bin/bash
source $(dirname $0)/incl.sh
echo "The main script"
これを行う最良の方法はChris Boranの方法を使用することですが、MY_DIRを次のように計算する必要があります。
#!/bin/sh
MY_DIR=$(dirname $(readlink -f $0))
$MY_DIR/other_script.sh
Readlinkのmanページを引用するには:
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
の中のシンボリックリンクを通してあなたのスクリプトにアクセスすればそれはうまくいきます。
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
この質問に対する回答の組み合わせが最も堅牢な解決策を提供します。
依存関係とディレクトリ構造を非常によくサポートしているので、プロダクショングレードのスクリプトではうまくいきました。
#!/ bin/bash #現在のスクリプトのフルパス THIS = `readlink -f" $ {BASH_SOURCE [0]} "2>/dev/null | | echo $ 0` #現在のスクリプトが置かれているディレクトリ DIR = `dirname" $ {THIS} "` # 'は'源 'を意味します、すなわち、' include ': 。 "$ DIR/compile.sh"
このメソッドは、これらすべてをサポートしています。
readlink
経由)${BASH_SOURCE[0]}
は$0
よりも堅牢ですスクリプトが提供されている場合でもこれは機能します。
source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
他のスクリプトの場所を指定する必要がありますが、他にはありません。あなたのスクリプトの一番上に設定可能な変数をお勧めします。
#!/bin/bash
installpath=/where/your/scripts/are
. $installpath/incl.sh
echo "The main script"
あるいは、PROG_HOMEやsomesuchのように、プログラムのホームがどこにあるかを示す環境変数をユーザーが維持するように要求することもできます。これは/etc/profile.d/にその情報を持つスクリプトを作成することでユーザーに自動的に提供されます。そして、それはユーザーがログインするたびに供給されます。
私はほとんどすべての提案を調査しました、そしてここに私のために働いた最も良いものがあります:
script_root=$(dirname $(readlink -f $0))
スクリプトが$PATH
ディレクトリにシンボリックリンクされている場合でも機能します。
ここで実際にそれを参照してください。 https://github.com/pendashteh/hcagent/blob/master/bin/hcagent
# Copyright https://stackoverflow.com/a/13222994/257479
script_root=$(ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //')
これは実際にはこのページの別の答えから来ていますが、私はそれを私の答えにも追加しています!
あるいは、まれにそれらがうまくいかなかった場合、ここに箇条書き証明アプローチがあります:
# 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
これが誰かに役立つことを願っています:)
また、うまく動かなかった場合はコメントとして残して、あなたのオペレーティングシステムとエミュレータについて言及してください。ありがとうございます。
システム全体のさまざまなコンポーネントの場所を提供することを唯一の目的とするsetenvスクリプトを作成することをお勧めします。
その後、他のすべてのスクリプトがこのスクリプトを使用して、setenvスクリプトを使用してすべての場所がすべてのスクリプトで共通になるようにします。
これはcronjobを実行するときにとても便利です。 cronを実行すると最小限の環境が得られますが、すべてのcronスクリプトに最初にsetenvスクリプトを含めるようにすれば、cronjobを実行する環境を制御および同期することができます。
私たちは約2000 kSLOCのプロジェクトにわたる継続的インテグレーションに使用されていたそのようなテクニックを私たちのビルドモンキーに使用しました。
Steveの回答は間違いなく正しいテクニックですが、installpath変数がそのような宣言がすべて行われる別の環境スクリプト内にあるようにリファクタリングする必要があります。
それから、すべてのスクリプトがそのスクリプトを読み込み、installpathを変更する必要があります。あなたはそれをただ1つの場所で変更する必要があります。物事をもっと未来にします。神私はその言葉が嫌いです! ( - :
ところであなたの例で示されている方法でそれを使うとき本当にあなたは$ {installpath}を使って変数を参照するべきです:
. ${installpath}/incl.sh
中括弧を省略した場合、シェルによっては変数 "installpath/incl.sh"を拡張しようとします。
シェルスクリプトローダーがこれに対する私の解決策です。
1つのスクリプトを参照するために多くのスクリプトで何度も呼び出すことができるinclude()という名前の関数を提供しますが、そのスクリプトは1回しかロードされません。この関数は完全パスまたは部分パスを受け入れることができます(スクリプトは検索パスで検索されます)。無条件にスクリプトをロードするload()という名前の同様の関数も提供されています。
bash、ksh、pd、ksh、zsh、最適化スクリプトで動作一人一人のために。ash、dash、heirloom shなど、元のshと一般的に互換性のある他のシェルシェルが提供できる機能に応じて自動的に機能を最適化するユニバーサルスクリプト。
[例]
start.sh
これはオプションのスタータースクリプトです。ここにスタートアップメソッドを配置することは単に便利であり、代わりにメインスクリプトに配置することができます。スクリプトをコンパイルする場合も、このスクリプトは必要ありません。
#!/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 ----'
output:
---- b.sh ----
---- a.sh ----
---- main.sh ----
一番良いのは、それを基にしたスクリプトをコンパイルして、利用可能なコンパイラで単一のスクリプトを作成することです。
これを使用するプロジェクトは次のとおりです。 http://sourceforge.net/p/playshell/code/ci/master/tree/ 。スクリプトをコンパイルしてもしなくても移植可能に実行できます。単一のスクリプトを生成するためのコンパイルも発生する可能性があり、インストール中に役立ちます。
私はまた、実装スクリプトがどのように機能するのかについての簡単な考えを持ちたいと思うかもしれない保守的なパーティーのためのより簡単なプロトタイプを作成しました: https://sourceforge.net/p/loader/code/ci/base/tree/ loader-include-prototype.bash 。コードが小さく、Bash 4.0以降で実行することを意図していて、eval
を使用しない場合は、誰でもメインのスクリプトにコードを含めることができます。
個人的にすべてのライブラリをlib
フォルダに置き、それらをロードするためにimport
関数を使用してください。
フォルダ構造
script.sh
の内容
# 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
}
このインポート機能はあなたのスクリプトの始めにあるべきであり、それからあなたはこのようにあなたのライブラリを簡単にインポートすることができます:
import "utils"
import "requirements"
各ライブラリの先頭に1行を追加します(つまりutils.sh)。
IMPORTED="$BASH_SOURCE"
utils.sh
からrequirements.sh
とscript.sh
内の関数にアクセスできるようになりました
TODO:単一のsh
ファイルを構築するためのリンカを書く
Sourceまたは$ 0を使用しても、スクリプトの実際のパスはわかりません。実際のパスを取得するためにスクリプトのプロセスIDを使用できます
ls -l /proc/$$/fd |
grep "255 ->" |
sed -e 's/^.\+-> //'
私はこのスクリプトを使っています、そしてそれはいつも私によく役立ってきました:)
私はすべての起動スクリプトを.bashrc.dディレクトリに置きました。これは/etc/profile.dなどのような場所で一般的な手法です。
while read file; do source "${file}"; done <<HERE
$(find ${HOME}/.bashrc.d -type f)
HERE
グロビングを使用したソリューションの問題...
for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done
...ファイルリストが長すぎる可能性があります。のようなアプローチ...
find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done
...は実行されますが、必要に応じて環境を変更することはありません。
もちろん、それぞれ独自に、しかし私は以下のブロックはかなりしっかりしていると思います。これにはディレクトリを見つけるための「最善の」方法と、別のbashスクリプトを呼び出すための「最善の」方法が含まれると思います。
scriptdir=`dirname "$BASH_SOURCE"`
source $scriptdir/incl.sh
echo "The main script"
したがって、これが他のスクリプトを含めるための「最善の」方法かもしれません。これは がbashスクリプトにそれが格納されている場所を指示するという別の「最良の」答えに基づいています
これは確実に機能するはずです。
source_relative() {
local dir="${BASH_SOURCE%/*}"
[[ -z "$dir" ]] && dir="$PWD"
source "$dir/$1"
}
source_relative incl.sh
我々のincl.shとmain.shが格納されているフォルダを見つける必要があります。これでmain.shを変更するだけです。
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"
man hier
スクリプトインクルードに適した場所は/usr/local/lib/
です
/ usr/local/lib
ローカルにインストールされたプログラムに関連付けられているファイル。
個人的には、インクルードには/usr/local/lib/bash/includes
を好みます。そのようにライブラリを含めるための bash-helper libがあります。
#!/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