[解決済み] POSIXのshでスクリプトディレクトリを取得するには?
質問
bashスクリプトで以下のようなコードを書いています。今私はそれをPOSIX shで使いたいと思っています。どのように私はそれを変換することができますか?
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
どのように解決するのですか?
POSIX-shellの(
sh
) の対応する
$BASH_SOURCE
は
$0
.
背景情報については、下を参照してください。
注意事項
: 決定的に違うのは
である場合、スクリプトは
ソース
(に読み込まれます)。
現在の
というシェルに
.
)
を指定すると
のスニペットは
ではなく
は正しく動作しません。
の説明を以下に示します。
を変更したことに注意してください。
DIR
を
dir
であるため、以下のスニペットでは
はすべて大文字の変数名を使用しないほうがよいからです。
というのは、環境変数や特殊なシェル変数との衝突を避けるために、すべて大文字の変数名を使わない方が良いからです。
また
CDPATH=
の代わりにプレフィックス
> /dev/null
に置き換えられる。
$CDPATH
にはヌル文字列が設定されており、これにより
cd
は決して
エコー
何もしない
最も単純なケースでは、これで( OPのコマンドに相当する ):
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
もしあなたが
を解決したい場合は
結果
ディレクトリのパスを最終的なターゲットに解決します。
シンボリックリンク
を追加する。
-P
に
pwd
コマンドに変換します。
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
警告
: これは
ではありません。
を見つけるのと同じです。
を見つけるのと同じです。
:
例えば、あなたのスクリプトが
foo
にシンボリックリンクされているとします。
/usr/local/bin/foo
の中で
$PATH
の中にありますが、その本当のパスは
/foodir/bin/foo
.
上記はまだ
/usr/local/bin
を報告します。なぜならシンボリックリンクの解決 (
-P
) が適用されるからです。
ディレクトリ
,
/usr/local/bin
というように、スクリプトそのものではなく
スクリプト自身の真の生成ディレクトリを見つけるために を調べる必要があります。 スクリプトの へのパスを であるかどうかを確認します。 シンボリックリンク であるかどうかを調べ、もしそうなら、最終的なターゲットファイルまで(一連の)シンボリックリンクをたどり、そのディレクトリパスを ターゲット ファイルの正規のパスからディレクトリのパスを抽出します。
GNUの
readlink -f
(もっと良い
readlink -e
) は、あなたのためにそれを行うことができますが
readlink
は POSIX ユーティリティではありません。
macOS を含む BSD プラットフォームには
readlink
ユーティリティもありますが、macOS ではこのユーティリティは
-f
の機能をサポートしていません。とはいえ
がいかに簡単な作業になるかを示すために
もし
readlink -f
が利用可能です。
:
dir=$(dirname "$(readlink -f -- "$0")")
.
実は、そこには no を解決するための POSIX ユーティリティがあります。 ファイル シンボリックリンク . これを回避する方法はありますが、面倒ですし、完全に堅牢というわけではありません。
は
を次のようにします。
POSIX準拠のシェル関数
は、GNU の
readlink -e
が行う
であり
合理的に堅牢なソリューション
その
は2つの稀なエッジケースにおいてのみ失敗します。
:
- を埋め込んだパス 改行 (非常に稀)
-
リテラル文字列を含むファイル名
->
(これも稀です)
という名前のこの関数で
rreadlink
と定義されています。
が定義されている場合、次のようにスクリプトの本当のディレクトリのパスを決定します。
:
dir=$(dirname -- "$(rreadlink "$0")")
注意
: もしあなたが、(POSIX以外の)
readlink
ユーティリティが存在すると仮定するならば - macOS, FreeBSD, Linux をカバーすることになります - 似たような、しかしより単純な解決法が、以下のものにあります。
この回答
を参照してください。
rreadlink()
ソースコード
-
場所
以前
を呼び出す
をスクリプトで呼び出します。
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.
target=$1 fname= targetDir= CDPATH=
# Try to make the execution environment as predictable as possible:
# All commands below are invoked via `command`, so we must make sure that `command`
# itself is not redefined as an alias or shell function.
# (Note that command is too inconsistent across shells, so we don't use it.)
# `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
# an external utility version of it (e.g, Ubuntu).
# `command` bypasses aliases and shell functions and also finds builtins
# in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
# to happen.
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
while :; do # Resolve potential symlinks until the ultimate target is found.
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
if [ -L "$fname" ]; then
# Extract [next] target path, which may be defined
# *relative* to the symlink's own directory.
# Note: We parse `ls -l` output to find the symlink target
# which is the only POSIX-compliant, albeit somewhat fragile, way.
target=$(command ls -l "$fname")
target=${target#* -> }
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target's canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
# Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
# AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
堅牢で予測可能なように、この関数では
command
を使用して、シェル組み込み関数や外部ユーティリティのみが呼び出されるようにします(エイリアスや関数の形式でのオーバーロードは無視します)。
それは
は以下のシェルの最近のバージョンでテストされています。
bash
,
dash
,
ksh
,
zsh
.
処理方法 ソース付き の呼び出しをどのように扱うか。
tl;dr :
使用方法 POSIXの機能のみ :
-
あなたは
できない
スクリプトのパスを決定する
の中で
をソースとする
呼び出し
(ただし
zsh
として動作することは通常ありません。sh
). -
あなたは
できる
を検出します。
スクリプトがソースされている場合のみ、スクリプトがソースされているかどうかを検出できます。
直接
シェルから
(シェルプロファイル/初期化ファイルのような)
チェーン
のソース) を比較することにより
$0
をシェルの実行ファイル名/パスと比較します (ただしzsh
では、前述のように$0
は本当に現在のスクリプトのパスである)。対照的に (ただしzsh
を除く)、スクリプトのソースが 他のスクリプト であり、それ自体が 直接呼び出される を含む。 その スクリプトのパスを$0
. -
これらの問題を解決するために
bash
,ksh
そしてzsh
は 非標準 機能 その する は、ソース付きのシナリオであっても実際のスクリプトのパスを決定し、スクリプトがソースされているかどうかも検出できるようにします。bash
,$BASH_SOURCE
常に には、ソースされているかどうかに関わらず、 実行中のスクリプトのパスが含まれます。[[ $0 != "$BASH_SOURCE" ]]
はスクリプトがソースされているかどうかをテストするために使用することができます。
なぜこれができないかを示すために からのコマンドを解析してみましょう。 ウォルターAさんの回答 :
# NOT recommended - see discussion below.
DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
-
(余談が2つ。
-
使用方法
-P
を二回使うのは冗長です。pwd
. -
コマンドは、サイレンシングが欠落している
cd
の潜在的な標準出力が、もし$CDPATH
がたまたま設定されていた場合)。
-
使用方法
-
command -v -- "$0"
-
command -v -- "$0"
は、もう一つのシナリオをカバーするように設計されています。 がソースされている場合です。 スクリプトが 対話型 シェルから$0
には通常 単なるファイル名 を含んでいます。sh
) で、この場合dirname
は単に.
を返すだけです (これはdirname
のない引数が与えられると必ずそうなるからです。 パス コンポーネントがない場合)。command -v -- "$0"
を通して、そのシェルの絶対パスを返します。$PATH
ルックアップ (/bin/sh
). ただし ログイン シェルは、いくつかのプラットフォーム (例: OSX) では、ファイル名の前に-
で$0
(-sh
) である場合、この場合command -v -- "$0"
は意図したとおりに動作しません( 空の文字列 ). -
逆に
command -v -- "$0"
は2つの誤動作をすることがあります。 非 -ソースのシナリオ で、シェルの実行可能なsh
は 直接 は、スクリプトを引数として呼び出されます。-
もしスクリプト自身が
ではなく
を実行可能にしてください。
command -v -- "$0"
を返すかもしれません。 空の文字列 として動作する特定のシェルに依存します。sh
として動作するシェルに依存します。bash
,ksh
そしてzsh
は空文字列を返します。dash
エコー$0
は の POSIX 仕様。command
はcommand -v
に適用された場合 ファイルシステムのパス を返します。 実行可能ファイル - というのはbash
,ksh
そしてzsh
の目的によって暗示されていると主張することができます。command
;不思議なことにdash
は、通常最も準拠したPOSIX市民ですが、ここでは標準から逸脱しています。対照的にksh
は、ここでは唯一のモデル市民であり、実行可能なファイルのみを報告するものであるため と を付けて報告します。 絶対 (正規化されていないとはいえ) パスを指定します。 -
もしスクリプトが
が
が実行可能であり、かつ
$PATH
であり、呼び出しはその 単なるファイル名 (を使用します(例.sh myScript
),command -v -- "$0"
はまた 空の文字列 ただしdash
.
-
もしスクリプト自身が
ではなく
を実行可能にしてください。
-
スクリプトのディレクトリが
はできません。
を決定することができません。
ソース
- なぜなら
$0
ではその情報を含まないからです (ただしzsh
として動作することは通常ありません。sh
として動作する) - この問題に対する良い解決策はありません。-
を返す
シェル実行ファイルの
ディレクトリのパスを返すことは、限られた用途にしか使えません。
スクリプトの
ディレクトリではありません。
テスト
を決定するために
かどうか
を判断します。
-
より信頼性の高い方法は、単純にテストすることです。
$0
を直接テストすることです。[ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ]
-
より信頼性の高い方法は、単純にテストすることです。
-
しかし、スクリプトのソースが次のものである場合は、それも機能しません。
別の
スクリプト (それ自身は
が直接
が呼び出される)、なぜなら
$0
は単に ソース スクリプトのパスが含まれます。
-
を返す
シェル実行ファイルの
ディレクトリのパスを返すことは、限られた用途にしか使えません。
スクリプトの
ディレクトリではありません。
テスト
を決定するために
かどうか
を判断します。
-
の有用性が限定的であることを考えると
command -v -- "$0"
の有用性が限られていることと、2つの を壊さないことです。 -を壊してしまうという事実があります。 使わないに一票 で、これは私たちに残ります。- すべて なし -ソースシナリオ は をカバーしています。
-
で
ソース付き
の呼び出しでは
スクリプトのパスを決定することはできません。
であり、せいぜい限られた状況下では
であるかどうかを検出することができます。
を検出できる程度です。
-
ソーシングされたとき
直接
である場合 (シェルプロファイル/初期化ファイルから)。
$dir
を含むことになります。.
を含むか、シェルの実行ファイルが単なるファイル名として起動された場合、(dirname
を単なるファイル名 常に を返します。.
) を返し、それ以外の場合はシェル実行ファイルのディレクトリパスを返します。.
は、カレントディレクトリからの非ソース起動と確実に区別することができません。 -
からソースされた場合
他のスクリプト
(からソースされている場合(それ自体もソースされていない)。
$0
には その スクリプトのパスが含まれており、ソースとなるスクリプトはそれが事実であるかどうかを知る方法がありません。
-
ソーシングされたとき
直接
である場合 (シェルプロファイル/初期化ファイルから)。
-
背景情報です。
POSIX
が定義している
の動作を定義しています。
$0
に対して、シェル
スクリプト
ここで
.
本来は
$0
はスクリプトファイルのパスを反映させる必要があります。
指定された
を意味する。
-
に依存してはいけません。
$0
を含む 絶対 パス . -
$0
には 絶対 パス の場合のみです。-
あなた
明示的に
を指定します。
絶対
のパスを指定します。
-
~/bin/myScript
(スクリプト自体が実行可能であると仮定して) -
sh ~/bin/myScript
-
-
で実行可能なスクリプトを呼び出した場合
単なるファイル名
というように、実行可能であることが必要です。
と
であることが必要です。
$PATH
を変換しています。myScript
を絶対パスに変換して実行します; 例えば-
myScript # executes /home/jdoe/bin/myScript, for instance
-
-
あなた
明示的に
を指定します。
絶対
のパスを指定します。
-
それ以外の場合
$0
はスクリプトを反映します。 パス 指定された :-
明示的に
sh
をスクリプトで呼び出す場合、これは 単なるファイル名 (例,sh myScript
) または 相対パス (例えばsh ./myScript
) -
実行スクリプトを起動する場合
直接
であれば、これは
相対的
のパス(例.
./myScript
- があることに注意してください。 単なるファイル名 を指定すると、スクリプト の中で$PATH
).
-
明示的に
実際には
bash
,
dash
,
ksh
そして
zsh
はすべてこの挙動を示します。
これに対して
の値は POSIX では義務付けられていません。
$0
である場合
ソーシング
スクリプト
(特別な組み込みユーティリティである
.
("dot")) を使っているので
に依存することはできません。
とか、実際には
の動作はシェルによって異なります。
-
したがって、やみくもに
$0
を使用し、標準的な動作を期待することはできません。-
実際には
bash
,dash
そしてksh
去る$0
手つかず というのは、スクリプトをソーシングするときに$0
には 呼び出し元の$0
の値、より正確には$0
の値、より正確には、それ自身がソースされていないコールチェーン内の最も最近の呼び出し元の値です; 従って$0
のパスはシェルの実行ファイルを指すかもしれません。 別の (直接起動される) スクリプトのパスを指します。 -
これに対して
zsh
は、一人反対派として、実は をしています。 を報告しています。 現在の スクリプトのパスを$0
. 逆に$0
は、スクリプトがソースされているかどうかの表示を行いません。 -
要するに、POSIXの機能だけを使って、手元のスクリプトがソースされているかどうか、手元のスクリプトのパスが何であるか、そして
$0
と現在のスクリプトのパスの関係はどうなっているのか。
-
実際には
-
もし、この状況に対応する必要があるのなら
にアクセスし、特定のシェルを識別する必要があります。
特定の
非標準の機能
:
-
bash
,ksh
そしてzsh
はすべて 自身の を提供し、実行中のスクリプトのパスを取得する方法を提供します。
-
完全を期すために
の値は
$0
で
その他
コンテキスト
:
-
シェル内部
機能
POSIXでは、以下のように規定されています。
$0
が残る 変更なし したがって、それがどのような値であったとしても の外側 を持つ場合、その値は 内側 を含むようになります。-
実際には
bash
,dash
そしてksh
はそのように動作します。 -
また
zsh
は一人反対派で 関数の という名前で報告します。
-
実際には
-
を受け付けたシェルで
コマンド文字列を
-c
オプションで を指定すると、起動時に 最初のオペランド (非オプション引数) を設定します。$0
;例えば-
sh -c 'echo \$0: $0 \$1: $1' foo one # -> '$0: foo $1: one'
-
bash
,dash
,ksh
そしてzsh
はすべてそのような振る舞いをします。
-
-
その他
シェルで
ではなく
を実行することで
スクリプトファイル
,
$0
はシェルの 親プロセス が渡した - 通常、これは シェルの の名前かパスです (例:sh
または/bin/sh
); これを含む。-
an
対話型
シェル
-
洞窟
一部のプラットフォーム、特に OSX では、常に
ログイン
シェルを作成します。
を前置する
-
の中に入れる前に、シェル名に$0
を追加することで、_login シェルであることをシェルに知らせることができます。したがって、デフォルトでは、このシェルは$0
が報告されます。-bash
でなくbash
ではなく、OSX 上の対話型シェルで使用されます。
-
洞窟
一部のプラットフォーム、特に OSX では、常に
ログイン
シェルを作成します。
を前置する
-
を読み込むシェルです。
コマンドを
標準入力
-
は、stdin を介してスクリプトファイルをシェルにパイプする場合にも当てはまります (例,
sh < myScript
)
-
は、stdin を介してスクリプトファイルをシェルにパイプする場合にも当てはまります (例,
-
bash
,dash
,ksh
とzsh
はすべてそのように振る舞います。
-
an
対話型
シェル
関連
-
[解決済み] Bashスクリプトのソースディレクトリをスクリプト自体から取得するにはどうすればよいですか?
-
[解決済み] Bashシェルスクリプトでディレクトリが存在するかどうかを確認するにはどうすればよいですか?
-
[解決済み] Bashで通常のファイルが存在しないかどうかを判断する方法を教えてください。
-
[解決済み] Bashで文字列が部分文字列を含むかどうかをチェックする方法
-
[解決済み] シェルで、「2>&1」はどういう意味ですか?
-
[解決済み] Bashスクリプトからプログラムが存在するかどうかを確認するにはどうすればよいですか?
-
[解決済み] Bashでコマンドの出力に変数を設定するにはどうすればよいですか?
-
[解決済み] Bash関数にパラメータを渡す
-
[解決済み] Git のコマンドライン・インターフェイスの言語を変更するにはどうしたらよいですか?
-
[解決済み] Bashの引数で引用符を維持するには?重複
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Bashスクリプトのソースディレクトリをスクリプト自体から取得するにはどうすればよいですか?
-
[解決済み] Unixシェルスクリプトは、スクリプトファイルが存在するディレクトリを見つけることができますか?
-
[解決済み】スクリプトがソースされているかどうかを検出する方法
-
[解決済み] $0とBASH_SOURCEの選択
-
[解決済み] 現在の作業ディレクトリではなく、ファイルの場所に基づく相対パス [重複]。
-
[解決済み] コマンドライン・ターミナルでの乗算
-
[解決済み] Bashで$PATH変数からパスを削除する最もエレガントな方法は何ですか?
-
[解決済み] Ansibleで新しいユーザーとパスワードを作成する
-
[解決済み] このbashのフォーク爆弾はどのように機能するのでしょうか?重複
-
[解決済み] bashでalt + numericを押すと(arg [numeric])と出ますが、これは何でしょう?