フルパスを知る必要があるBashスクリプトがあります。私は、相対パスやファンキーなパスを使わずに、互換性のある方法を見つけようとしています。私はsh、cshなどではなく、Bashのみをサポートする必要があります。
私がこれまでに見つけたもの:
内からBashスクリプトのソースディレクトリを取得するための一般的な回答は、dirname $0
を使用してスクリプトのパスを取得することです。これは、相対パス(.
など)を返す可能性があります。これは、スクリプト内のディレクトリを変更し、そのパスが引き続きスクリプトのディレクトリを指すようにする場合に問題になります。それでも、dirname
はパズルの一部になります。
OS XでのBashスクリプトの絶対パスに対する受け入れられた回答(OS X固有、しかし回答は関係なく動作します)は、$0
が相対的に見えるかどうかをテストし、もしそうであれば、$PWD
をその前に追加するかどうかをテストする関数を提供します。しかし、結果にはまだ相対ビットが含まれている可能性があります(全体としては絶対的ですが)。たとえば、スクリプトがディレクトリ/usr/bin
内のt
で、/usr
に入っていて実行にbin/../bin/t
と入力した場合(はい、複雑です)、スクリプトのディレクトリパスとして/usr/bin/../bin
で終わります。どれが動作します、しかし...
このページのreadlink
による解決方法 、これは次のようになります:
# Absolute path to this script. /home/user/bin/foo.sh
SCRIPT=$(readlink -f $0)
# Absolute path this script is in. /home/user/bin
SCRIPTPATH=`dirname $SCRIPT`
しかしreadlink
はPOSIXではなく、どうやらこの解決法はGNUのreadlink
に依存しています。ここでBSDは何らかの理由で機能しません(私はBSD風のシステムにアクセスしてチェックしていません)。
それで、それをするさまざまな方法が、彼らはすべて彼らの警告を持っています。
もっと良い方法は何ですか? 「より良い」とは、
これが私が思いついたものです(編集:+ sfstewman 、 levigroker 、 Kyle Strand 、および Rob Kennedy )。 「より良い」基準:
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )"
そのSCRIPTPATH
行は特に回り道のようですが、スペースやシンボリックリンクを適切に処理するためにはSCRIPTPATH=`pwd`
ではなく必要です。
また、アクセス可能なファイルシステム内のファイルから来ていないスクリプトを実行するなどの難解な状況(これは完全に可能です)は、そこには対処されていません(または私が見たその他の答えのいずれでも)。 ).
realpath
コマンドがここで言及されていないことに驚きました。私の理解するところでは、それは広く移植可能/移植されているということです。
あなたの最初の解決策は以下のようになります。
SCRIPT=`realpath $0`
SCRIPTPATH=`dirname $SCRIPT`
シンボリックリンクを好みに合わせて未解決のままにするには、
SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`
Bashで完全な正規パスを取得するために見つけた最も簡単な方法は、cd
とpwd
を使用することです。
ABSOLUTE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/$(basename "${BASH_SOURCE[0]}")"
${BASH_SOURCE[0]}
の代わりに$0
を使用しても、スクリプトが<name>
またはsource <name>
のどちらとして呼び出されたかにかかわらず、同じ動作になります。
私は今日この問題をもう一度見直さなければならず、スクリプト自体の中からBashスクリプトのソースディレクトリを取得する:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
リンクされた回答にはもっと多くの変形があります。スクリプト自体がシンボリックリンクの場合。
Readlinkで-f
オプションを使用していないので、BSD/Mac OS Xで動作するはずです。
.
ドット演算子によって呼び出された場合)foo->dir1/dir2/bar bar->./../doe doe->script
私はこのコードがうまくいかないコーナーケースを探しています 。私にお知らせください。
pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
while([ -h "${SCRIPT_PATH}" ]); do
cd "`dirname "${SCRIPT_PATH}"`"
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
done
cd "`dirname "${SCRIPT_PATH}"`" > /dev/null
SCRIPT_PATH="`pwd`";
popd > /dev/null
echo "srcipt=[${SCRIPT_PATH}]"
echo "pwd =[`pwd`]"
スクリプト はディスクのどこかにある必要があります 。それがネットワーク上にあるようにしましょう。 PIPEからこのスクリプトを実行しようとするとうまくいきません
wget -o /dev/null -O - http://Host.domain/dir/script.sh |bash
技術的に言えば、それは未定義です。実際には、これを検出するための正しい方法はありません。 (コプロセスは親の環境にアクセスできません。)
つかいます:
SCRIPT_PATH=$(dirname `which $0`)
which
は、渡された引数がシェルプロンプトで入力されたときに実行されたであろう実行可能ファイルのフルパスを標準出力に表示します($ 0が含むものです)
dirname
は、ディレクトリ名以外のサフィックスをファイル名から削除します。
したがって、パスが指定されているかどうかにかかわらず、スクリプトのフルパスになります。
私のLinuxシステムではデフォルトでrealpathがインストールされていないので、私には次のように動作します。
SCRIPT="$(readlink --canonicalize-existing "$0")"
SCRIPTPATH="$(dirname "$SCRIPT")"
$SCRIPT
はスクリプトへの実際のファイルパスを含み、$SCRIPTPATH
はスクリプトを含むディレクトリへの実際のパスを含みます。
これを使う前に この答え のコメントを読んでください。
読みやすい?以下は代替案です。シンボリックリンクは無視されます
#!/bin/bash
currentDir=$(
cd $(dirname "$0")
pwd
)
echo -n "current "
pwd
echo script $currentDir
私は数年前に上記の答えを投稿して以来、私は自分のプラクティスを適切にシンボリックリンクを処理するこのLinux特有のパラダイムを使用するように進化させました:
Origin=$(dirname $(readlink -f $0))
この質問に非常に遅く答えるが、私は使用します:
SCRIPT=$( readlink -m $( type -p $0 )) # Full path to script
BASE_DIR=`dirname ${SCRIPT}` # Directory script is run in
NAME=`basename ${SCRIPT}` # Actual name of script even if linked
私たちはGitHubに自社製品 realpath-lib を配置して、無料で障害のないコミュニティでの使用を目指しています。
恥知らずなプラグしかしこのBashライブラリであなたはできる:
get_realpath <absolute|relative|symlink|local file>
この関数はライブラリの中核です。
function get_realpath() {
if [[ -f "$1" ]]
then
# file *must* exist
if cd "$(echo "${1%/*}")" &>/dev/null
then
# file *may* not be local
# exception is ./file.ext
# try 'cd .; cd -;' *works!*
local tmppwd="$PWD"
cd - &>/dev/null
else
# file *must* be local
local tmppwd="$PWD"
fi
else
# file *cannot* exist
return 1 # failure
fi
# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success
}
外部の依存関係は必要ありません。Bash4+だけです。 get_dirname
、get_filename
、get_stemname
および validate_path validate_realpath
。それは無料で、きれいで、単純でそしてよく文書化されています、それでそれは学習目的のためにも使われることができます、そして間違いなく改善されることができます。プラットフォームを越えて試してみてください。
更新:いくつかのレビューとテストの後、上記の関数を同じ結果(dirnameを使わずに、純粋なBashのみ)を実現するものに置き換えましたが、より効率的です:
function get_realpath() {
[[ ! -f "$1" ]] && return 1 # failure : file does not exist.
[[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
return 0 # success
}
これには、物理システムへのシンボリックリンクを解決する機能を提供する環境設定no_symlinks
も含まれます。デフォルトではシンボリックリンクをそのまま残します。
単に:
BASEDIR=$(readlink -f $0 | xargs dirname)
派手な演算子は必要ありません。
Bourne Shell (sh
)準拠の方法:
SCRIPT_HOME=`dirname $0 | while read a; do cd $a && pwd && break; done`
次の変数を定義しようとします。
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
あるいは、Bashで以下の機能を試すことができます。
realpath () {
[[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}
この関数は1つの引数を取ります。引数がすでに絶対パスを持っている場合は、それをそのまま出力します。それ以外の場合は、$PWD
変数+ファイル名引数(./
プレフィックスなし)を出力します。
関連する
承認された解決策は(私にとっては)「ソース対応」ではないという不都合があります。
"source ../../yourScript
"から呼び出した場合、$0
は "bash
"になります。
次の関数(bash> = 3.0の場合)から正しいパスが得られますが、スクリプトが呼び出されることがあります(直接またはsource
を介して、絶対パスまたは相対パスで)。
(「正しいパス」とは、 呼び出されるスクリプトの絶対パス 、別のパスから直接呼び出される場合でも、直接または「source
」で呼び出される場合でも)
#!/bin/bash
echo $0 executed
function bashscriptpath() {
local _sp=$1
local ascript="$0"
local asp="$(dirname $0)"
#echo "b1 asp '$asp', b1 ascript '$ascript'"
if [[ "$asp" == "." && "$ascript" != "bash" && "$ascript" != "./.bashrc" ]] ; then asp="${BASH_SOURCE[0]%/*}"
Elif [[ "$asp" == "." && "$ascript" == "./.bashrc" ]] ; then asp=$(pwd)
else
if [[ "$ascript" == "bash" ]] ; then
ascript=${BASH_SOURCE[0]}
asp="$(dirname $ascript)"
fi
#echo "b2 asp '$asp', b2 ascript '$ascript'"
if [[ "${ascript#/}" != "$ascript" ]]; then asp=$asp ;
Elif [[ "${ascript#../}" != "$ascript" ]]; then
asp=$(pwd)
while [[ "${ascript#../}" != "$ascript" ]]; do
asp=${asp%/*}
ascript=${ascript#../}
done
Elif [[ "${ascript#*/}" != "$ascript" ]]; then
if [[ "$asp" == "." ]] ; then asp=$(pwd) ; else asp="$(pwd)/${asp}"; fi
fi
fi
eval $_sp="'$asp'"
}
bashscriptpath H
export H=${H}
重要なのは、 "source
"の場合を検出し、実際のスクリプトを取り戻すために${BASH_SOURCE[0]}
を使用することです。
この問題についてもう一度考えてみましょう。このスレッド内で参照されているものとして、Origin here を持つ非常に一般的な解決策があります。
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
私はdirnameを使用しているため、この解決策を避けました。特にセキュリティ上の理由からスクリプトをロックする必要がある場合は、プラットフォーム間で問題が生じる可能性があります。しかし、純粋なBashの代替手段として、どのように使用しますか。
DIR="$( cd "$( echo "${BASH_SOURCE[0]%/*}" )" && pwd )"
これは選択肢でしょうか。
おそらく、次の質問に対する一般的な回答が役立つことがあります。
MacでGNUのreadlink -fの動作を取得する方法を教えてください。
$ PWDと$ 0を連結したときに得られる名前を正規化したいだけなのであれば($ 0は絶対的ではないと仮定して)、abs_dir=${abs_dir//\/.\//\/}
などの行に沿った一連の正規表現の置き換えを使用します。
はい、私はそれが恐ろしいように見えますが、それはうまくいくでしょう、そして純粋なBashです。
Bashを使用する場合、外部コマンドへの呼び出しを必要としないので、これが最も便利な方法だと思います。
THIS_PATH="${BASH_SOURCE[0]}";
THIS_DIR=$(dirname $THIS_PATH)
これを試して:
cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
まさにそのために、純粋にテキスト形式で、純粋にBashで処理を行うスクリプトを少しハッキングしました。すべてのEdgeケースをキャッチできたらと思います。
私が他の答えで言及した${var//pat/repl}
は、最短一致のみを置き換えることはできないため機能しません。これは、/foo/../
をたとえば/*/../
は、単一のエントリではなく、その前にあるすべてのものを取得します。そして、これらのパターンは実際には正規表現ではないので、どのように機能させることができるのかわかりません。だから、ここで私が思いついた、うまく複雑なソリューションをお楽しみください。 ;)
ところで、未処理のEdgeケースが見つかった場合はお知らせください。
#!/bin/bash
canonicalize_path() {
local path="$1"
OIFS="$IFS"
IFS=$'/'
read -a parts < <(echo "$path")
IFS="$OIFS"
local i=${#parts[@]}
local j=0
local back=0
local -a rev_Canon
while (($i > 0)); do
((i--))
case "${parts[$i]}" in
""|.) ;;
..) ((back++));;
*) if (($back > 0)); then
((back--))
else
rev_Canon[j]="${parts[$i]}"
((j++))
fi;;
esac
done
while (($j > 0)); do
((j--))
echo -n "/${rev_Canon[$j]}"
done
echo
}
canonicalize_path "/.././..////../foo/./bar//foo/bar/.././bar/../foo/bar/./../..//../foo///bar/"
ワンライナー
`dirname $(realpath $0)`
私はしばらくの間(OS Xではなく)次のアプローチを成功裏に使用してきました、そしてそれはシェル組み込みのみを使用して、私が見た限りで 'source foobar.sh'ケースを取り扱います。
以下の(急いでまとめた)サンプルコードの問題の1つは、関数が$ PWDを使用していることです。これは、関数呼び出し時には正しくない場合と正しくない場合があります。だからそれを処理する必要があります。
#!/bin/bash
function canonical_path() {
# Handle relative vs absolute path
[ ${1:0:1} == '/' ] && x=$1 || x=$PWD/$1
# Change to dirname of x
cd ${x%/*}
# Combine new pwd with basename of x
echo $(pwd -P)/${x##*/}
cd $OLDPWD
}
echo $(canonical_path "${BASH_SOURCE[0]}")
type [
type cd
type echo
type pwd