スクリプト自体の中からBashスクリプトのソースディレクトリを取得するにはどうすればよいですか?

2008年09月13日に質問されました。  ·  閲覧回数 1.8M回  ·  ソース

Jiaaro picture
2008年09月13日

そのスクリプトで、 Bashスクリプトが配置されているディレクトリのパスを取得するにはどうすればよいですか?

別のアプリケーションのランチャーとしてBashスクリプトを使用したいと思います。 作業ディレクトリをBashスクリプトが配置されているディレクトリに変更して、次のようにそのディレクトリ内のファイルを操作できるようにします。

$ ./application

回答

dogbane picture
2008年10月29日
6879
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

は、スクリプトがどこから呼び出されているかに関係なく、スクリプトの完全なディレクトリ名を提供する便利なワンライナーです。

スクリプトの検索に使用されるパスの最後のコンポーネントがシンボリックリンクでない限り機能します(ディレクトリリンクは問題ありません)。 スクリプト自体へのリンクも解決する場合は、複数行のソリューションが必要です。

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"

この最後のものは、エイリアス、 sourcebash -c 、シンボリックリンクなどの任意の組み合わせで機能します。

注意:このスニペットを実行する前に別のディレクトリにcd移動すると、結果が正しくない可能性があります。

また、ユーザーがcdをスマートにオーバーライドして出力をstderrにリダイレクトした場合は、 $CDPATH落とし穴、およびstderr出力の副作用にupdate_terminal_cwd >&2を呼び出す場合などのエスケープシーケンスを含む)。 cdコマンドの最後に>/dev/null 2>&1を追加すると、両方の可能性が処理されます。

それがどのように機能するかを理解するには、このより冗長なフォームを実行してみてください。

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $TARGET == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

そしてそれは次のようなものを印刷します:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
matt b picture
2008年09月13日
939

dirname "$0"使用します:

#!/bin/bash
echo "The script you are running has basename `basename "$0"`, dirname `dirname "$0"`"
echo "The present working directory is `pwd`"

スクリプトが含まれているディレクトリからスクリプトを実行していない場合、 pwd単独で使用しても機能しません。

[[email protected] ~]$ pwd
/home/matt
[[email protected] ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[[email protected] ~]$ cd /tmp
[[email protected] tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp
phatblat picture
2009年09月27日
540

dirnameコマンドは最も基本的なもので、 $0 (スクリプト名)変数からファイル名までのパスを解析するだけです。

dirname "$0"

ただし、 matt bが指摘しているように、返されるパスは、スクリプトの呼び出し方法によって異なります。 pwdは、スクリプトが存在するディレクトリではなく、現在のディレクトリが何であるかを示すだけなので、ジョブを実行しません。さらに、スクリプトへのシンボリックリンクが実行されると、実際のスクリプトではなく、リンクが存在する場所への(おそらく相対的な)パス。

readlinkコマンドについて言及している人もいますが、最も簡単な方法として、次のコマンドを使用できます。

dirname "$(readlink -f "$0")"

readlinkは、スクリプトパスをファイルシステムのルートからの絶対パスに解決します。 したがって、シングルドットまたはダブルドット、チルダ、シンボリックリンクを含むパスは、フルパスに解決されます。

これらのそれぞれを示すスクリプトは次のとおりです。 whatdir.sh

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"

相対パスを使用して、このスクリプトをホームディレクトリで実行します。

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat

繰り返しますが、スクリプトへのフルパスを使用します。

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

現在ディレクトリを変更しています:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat

そして最後に、シンボリックリンクを使用してスクリプトを実行します。

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat
user25866 picture
2008年10月08日
185
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}"
if ([ -h "${SCRIPT_PATH}" ]); then
  while([ -h "${SCRIPT_PATH}" ]); do cd `dirname "$SCRIPT_PATH"`; 
  SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

を含むすべてのバージョンで動作します

  • 複数の深さのソフトリンクを介して呼び出された場合、
  • ファイルするとき
  • コマンド「 source 」(別名. (ドット)演算子)によってスクリプトが呼び出されたとき。
  • arg $0が呼び出し元から変更されたとき。
  • "./script"
  • "/full/path/to/script"
  • "/some/path/../../another/path/script"
  • "./some/folder/script"

また、場合bashスクリプト自体は、あなたがそれに従うと、スクリプトリンク-へのフルパスを返すようにしたい相対シンボリックリンクです:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null

SCRIPT_PATHは、どのように呼び出されても、フルパスで指定されます。
スクリプトの開始時にこれを見つけてください。

このコメントとコードはコピーレフトであり、GPL2.0以降またはCC-SA 3.0(CreativeCommons継承)以降で選択可能なライセンスです。 (c)2008。無断複写・転載を禁じます。 いかなる種類の保証もありません。 あなたは警告されました。
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

Mr Shark picture
2008年09月13日
112

$BASH_SOURCE使用できます:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

これはBash拡張機能であるため、 #!/bin/shではなく#!/bin/bashを使用する必要があることに注意してください。

Fabien picture
2008年12月03日
111

簡潔な答え:

`dirname $0`

または(できれば):

$(dirname "$0")
Simon Rigét picture
2016年02月13日
82

これはそれを行う必要があります:

DIR="$(dirname "$(readlink -f "$0")")"

これは、パス内のシンボリックリンクとスペースで機能します。

dirnameおよびreadlinkのマニュアルページを参照してください。

コメントトラックから、MacOSでは動作しないようです。 それがなぜなのか私にはわかりません。 助言がありますか?

Thamme Gowda picture
2018年11月07日
73

受け入れられた回答にワンライナーをコピーして貼り付けるために、このページに何度もアクセスするのはうんざりです。 それに関する問題は、理解して覚えるのが簡単ではないということです。

覚えやすいスクリプトは次のとおりです。

DIR="$(dirname "${BASH_SOURCE[0]}")"  # get the directory name
DIR="$(realpath "${DIR}")"    # resolve its full path if need be
SpoonMeiser picture
2008年09月16日
64

pwdを使用して現在の作業ディレクトリを検索し、 dirnameを使用して特定のファイルのディレクトリを検索できます(実行されたコマンドは$0なので、 dirname $0は、現在のスクリプトのディレクトリを提供するはずです)。

ただし、 dirnameは、ファイル名のディレクトリ部分を正確に示します。これは、現在の作業ディレクトリに関連している可能性が高いです。 スクリプトが何らかの理由でディレクトリを変更する必要がある場合、 dirnameからの出力は無意味になります。

私は次のことを提案します:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"

このようにして、相対ディレクトリではなく絶対ディレクトリを取得します。

スクリプトは別のbashインスタンスで実行されるため、後で作業ディレクトリを復元する必要はありませんが、何らかの理由でスクリプトを元に戻したい場合は、 pwd値を簡単に割り当てることができます。将来使用するために、ディレクトリを変更する前に変数に

ただ

cd `dirname $0`

質問の特定のシナリオを解決します。一般的に、より便利な絶対パスがあることがわかります。

Jim picture
2008年09月13日
39

これは他の人が考えているほど簡単ではないと思います。 pwdは機能しません。これは、現在のディレクトリが必ずしもスクリプトのあるディレクトリであるとは限らないためです。 $0も常に情報があるとは限りません。 スクリプトを呼び出すには、次の3つの方法を検討してください。

./script

/usr/bin/script

script

1番目と3番目の方法では、 $0はフルパス情報がありません。 2番目と3番目では、 pwdは機能しません。 3番目の方法でディレクトリを取得する唯一の方法は、パスを実行して、正しい一致のファイルを見つけることです。 基本的に、コードはOSが行うことをやり直す必要があります。

求めていることを実行する1つの方法は、 /usr/shareディレクトリ内のデータをハードコーディングし、そのフルパスで参照することです。 とにかくデータは/usr/binディレクトリにあるべきではないので、これはおそらくやるべきことです。