web-dev-qa-db-ja.com

他のスクリプトを含めるにはどうすればよいでしょうか。

あなたが通常スクリプトをインクルードする方法は "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スクリプトを見つけることができることを確実にするための良い方法は何ですか、特に、例えばスクリプトが移植可能である必要があるならば?

319
Aaron H.

私は自分のスクリプトをすべて互いに相対的なものにする傾向があります。そうすれば、dirnameを使うことができます。

#!/bin/sh

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

"$my_dir/other_script.sh"
204
Chris Boran

私はパーティーに遅刻していることを私は知っています、しかしこれはあなたがスクリプトを始めて、排他的にビルトインを使う方法に関係なく働くべきです:

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の最短一致を削除します

164
sacii

に代わるもの:

scriptPath=$(dirname $0)

です:

scriptPath=${0%/*}

..組み込みコマンドではないdirnameに依存しないという利点(およびエミュレータで常に利用できるわけではない)

50
tardate

同じディレクトリにある場合はdirname $0を使用できます。

#!/bin/bash

source $(dirname $0)/incl.sh

echo "The main script"
37
dsm

これを行う最良の方法は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の中のシンボリックリンクを通してあなたのスクリプトにアクセスすればそれはうまくいきます。

25
Mat131
SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/incl.sh"
19
Max

この質問に対する回答の組み合わせが最も堅牢な解決策を提供します。

依存関係とディレクトリ構造を非常によくサポートしているので、プロダクショングレードのスクリプトではうまくいきました。

#!/ 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よりも堅牢です
18
Brian Haak

スクリプトが提供されている場合でもこれは機能します。

source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh"
12

他のスクリプトの場所を指定する必要がありますが、他にはありません。あなたのスクリプトの一番上に設定可能な変数をお勧めします。

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

. $installpath/incl.sh

echo "The main script"

あるいは、PROG_HOMEやsomesuchのように、プログラムのホームがどこにあるかを示す環境変数をユーザーが維持するように要求することもできます。これは/etc/profile.d/にその情報を持つスクリプトを作成することでユーザーに自動的に提供されます。そして、それはユーザーがログインするたびに供給されます。

7
Steve Baker

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

これが誰かに役立つことを願っています:)

また、うまく動かなかった場合はコメントとして残して、あなたのオペレーティングシステムとエミュレータについて言及してください。ありがとうございます。

6
Alexar

システム全体のさまざまなコンポーネントの場所を提供することを唯一の目的とするsetenvスクリプトを作成することをお勧めします。

その後、他のすべてのスクリプトがこのスクリプトを使用して、setenvスクリプトを使用してすべての場所がすべてのスクリプトで共通になるようにします。

これはcronjobを実行するときにとても便利です。 cronを実行すると最小限の環境が得られますが、すべてのcronスクリプトに最初にsetenvスクリプトを含めるようにすれば、cronjobを実行する環境を制御および同期することができます。

私たちは約2000 kSLOCのプロジェクトにわたる継続的インテグレーションに使用されていたそのようなテクニックを私たちのビルドモンキーに使用しました。

5
Rob Wells

Steveの回答は間違いなく正しいテクニックですが、installpath変数がそのような宣言がすべて行われる別の環境スクリプト内にあるようにリファクタリングする必要があります。

それから、すべてのスクリプトがそのスクリプトを読み込み、installpathを変更する必要があります。あなたはそれをただ1つの場所で変更する必要があります。物事をもっと未来にします。神私はその言葉が嫌いです! ( - :

ところであなたの例で示されている方法でそれを使うとき本当にあなたは$ {installpath}を使って変数を参照するべきです:

. ${installpath}/incl.sh

中括弧を省略した場合、シェルによっては変数 "installpath/incl.sh"を拡張しようとします。

3
Rob Wells

シェルスクリプトローダーがこれに対する私の解決策です。

1つのスクリプトを参照するために多くのスクリプトで何度も呼び出すことができるinclude()という名前の関数を提供しますが、そのスクリプトは1回しかロードされません。この関数は完全パスまたは部分パスを受け入れることができます(スクリプトは検索パスで検索されます)。無条件にスクリプトをロードするload()という名前の同様の関数も提供されています。

bashkshpd、kshzsh、最適化スクリプトで動作一人一人のために。ashdashheirloom 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を使用しない場合は、誰でもメインのスクリプトにコードを含めることができます。

2
konsolebox

個人的にすべてのライブラリをlibフォルダに置き、それらをロードするためにimport関数を使用してください。

フォルダ構造

enter image description here

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.shscript.sh内の関数にアクセスできるようになりました

TODO:単一のshファイルを構築するためのリンカを書く

2
Xaqron

Sourceまたは$ 0を使用しても、スクリプトの実際のパスはわかりません。実際のパスを取得するためにスクリプトのプロセスIDを使用できます

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

私はこのスクリプトを使っています、そしてそれはいつも私によく役立ってきました:)

1
francoisrv

私はすべての起動スクリプトを.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

...は実行されますが、必要に応じて環境を変更することはありません。

1
phreed

もちろん、それぞれ独自に、しかし私は以下のブロックはかなりしっかりしていると思います。これにはディレクトリを見つけるための「最善の」方法と、別のbashスクリプトを呼び出すための「最善の」方法が含まれると思います。

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

echo "The main script"

したがって、これが他のスクリプトを含めるための「最善の」方法かもしれません。これは がbashスクリプトにそれが格納されている場所を指示するという別の「最良の」答えに基づいています

1
modulitos

これは確実に機能するはずです。

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

source_relative incl.sh
1
PSkocik

我々の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"
0
fastrizwaan

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

bash-helpers includes status output

0