上部のコメントセクションからヘルプとバージョン情報を印刷できるように、現在のスクリプトをgrepしたいと思います。
私はこのようなことを考えていました:
_grep '^#h ' -- "$0" | sed -e 's/#h //'
_
しかし、スクリプトがPATHにあるディレクトリにあり、ディレクトリを明示的に指定せずに呼び出された場合はどうなるのだろうと思いました。
特殊変数の説明を検索したところ、_$0
_の次の説明が見つかりました。
現在のシェルまたはプログラムの名前
現在のスクリプトのファイル名
スクリプト自体の名前
実行されたときのコマンド
これらのいずれも、スクリプトがディレクトリなしで呼び出された場合に_$0
_の値にディレクトリが含まれるかどうかを明確にしません。最後の1つは、実際にはそうではないことを意味します。
マイシステムでのテスト(Bash 4.1)
/ usr/local/binにscriptnameという名前の実行可能ファイルを1行_echo $0
_で作成し、別の場所から呼び出しました。
これらは私の結果です:
_> cd /usr/local/bin/test
> ../scriptname
../scriptname
> cd /usr/local/bin
> ./scriptname
./scriptname
> cd /usr/local
> bin/scriptname
bin/scriptname
> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname
> scriptname
/usr/local/bin/scriptname
_
これらのテストでは、_$0
_の値は常に、スクリプトが呼び出された方法とまったく同じですexcept(パスコンポーネントなしで呼び出された場合)。その場合、_$0
_の値は絶対パスです。したがって、別のコマンドに渡しても安全なようです。
しかしそれから私は Stack Overflow についてのコメントに出くわしました、それは私を混乱させました。答えは、現在のスクリプトのディレクトリを取得するために$(dirname $0)
を使用することを提案しています。コメント(7回賛成)は、「スクリプトがパスにある場合は機能しません」と述べています。
質問
$0
_がディレクトリを含まない状況はありますか?最も一般的なケースでは、$0
には、絶対パスまたはスクリプトへの相対パスが含まれるため、
script_path=$(readlink -e -- "$0")
(readlink
コマンドがあり、それが-e
をサポートしていると仮定して)は、通常、スクリプトへの正規の絶対パスを取得するのに十分な方法です。
$0
は、インタープリターに渡されるスクリプトを指定する引数から割り当てられます。
たとえば、次の場所にあります。
the-Shell -Shell-options the/script its args
$0
はthe/script
を取得します。
実行すると:
the/script its args
シェルは次のことを行います:
exec("the/script", ["the/script", "its", "args"])
たとえば、スクリプトに#! /bin/sh -
she-bangが含まれている場合、システムはそれを次のように変換します。
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])
(シバンが含まれていない場合、またはより一般的にはシステムがENOEXECエラーを返した場合、同じことを行うシェルです)
一部のシステムのsetuid/setgidスクリプトには例外があり、システムは一部のfd
x
でスクリプトを開いて代わりに実行します。
exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])
競合状態を回避するため(この場合、$0
には/dev/fd/x
が含まれます)。
ここで、/dev/fd/x
isがそのスクリプトへのパスであると主張するかもしれません。ただし、$0
から読み取る場合は、入力を使用するときにスクリプトが壊れます。
ここで、呼び出されたスクリプトコマンド名にスラッシュが含まれていない場合は、違いがあります。に:
the-script its args
シェルはthe-script
で$PATH
を検索します。 $PATH
には、一部のディレクトリへの絶対パスまたは相対パス(空の文字列を含む)を含めることができます。たとえば、$PATH
に/bin:/usr/bin:
が含まれ、the-script
が現在のディレクトリで見つかった場合、シェルは次のことを行います。
exec("the-script", ["the-script", "its", "args"])
これは次のようになります:
exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]
または、/usr/bin
にある場合:
exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
"-", "/usr/bin/the-script", "its", "args")
上記のsetuidコーナーケースを除くすべてのケースで、$0
にはスクリプトへのパス(絶対パスまたは相対パス)が含まれます。
これで、スクリプトは次のように呼び出すこともできます。
the-interpreter the-script its args
上記のthe-script
にスラッシュ文字が含まれていない場合、動作はシェルによってわずかに異なります。
古いAT&T ksh
実装は実際に$PATH
で無条件にスクリプトを検索していました(これは実際にはバグであり、setuidスクリプトのセキュリティホールでした)。したがって、$0
は実際にnotへのパスを含みます$PATH
ルックアップが実際に現在のディレクトリでthe-script
を見つけた場合を除いて、スクリプト。
新しいAT&T ksh
は、現在のディレクトリでthe-script
が読み取り可能であれば、それを解釈しようとします。そうでない場合は、読み取り可能なおよび実行可能ファイルthe-script
内の$PATH
を検索します。
bash
の場合、the-script
が現在のディレクトリにあるかどうか(壊れたシンボリックリンクではないかどうか)をチェックし、ない場合は、the-script
で読み取り可能な(必ずしも実行可能ではない)$PATH
を検索します。
zsh
in sh
エミュレーションはbash
と同様ですが、the-script
が現在のディレクトリで壊れたシンボリックリンクである場合、the-script
を$PATH
で検索せず、代わりにエラー。
他のすべてのBourneのようなシェルは、the-script
で$PATH
を検索しません。
とにかく、これらすべてのシェルについて、$0
に/
が含まれておらず、読み取りもできないことがわかった場合は、おそらく$PATH
で検索されています。次に、$PATH
内のファイルは実行可能である可能性が高いので、command -v -- "$0"
を使用してそのパスを見つけることはおそらく安全な概算です(ただし、$0
がShellビルトインまたはキーワード(ほとんどのシェル)の名前でもある場合、これは機能しません) )。
したがって、そのケースを本当にカバーしたい場合は、次のように書くことができます。
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}""; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
(""
に追加された$PATH
は、$IFS
がseparatorではなくdelimiterとして機能するシェルで末尾の空の要素を保持するためです)。
現在、スクリプトを呼び出すためのより難解な方法があります。次のことができます:
the-Shell < the-script
または:
cat the-script | the-Shell
その場合、$0
はインタープリターが受け取った最初の引数(argv[0]
)になります(the-Shell
より上ですが、通常はベース名またはそのインタープリターへの1つのパスのいずれでもかまいません)。
$0
の値に基づいてその状況にあることを検出することは信頼できません。 ps -o args= -p "$$"
の出力を見て、手掛かりを得ることができます。パイプの場合、スクリプトへのパスに戻るための実際の方法はありません。
次のこともできます:
the-Shell -c '. the-script' blah blih
次に、zsh
(およびBourne Shellの古い実装)を除いて、$0
はblah
になります。繰り返しになりますが、これらのシェルでスクリプトのパスを取得するのは困難です。
または:
the-Shell -c "$(cat the-script)" blah blih
等.
正しい$progname
があることを確認するには、次のようにして特定の文字列を検索できます。
progname=$0
[ -r "$progname" ] || progname=$(
IFS=:; set -f
for i in ${PATH-$(getconf PATH)}:; do
case $i in
"") p=$progname;;
*/) p=$i$progname;;
*) p=$i/$progname
esac
[ -r "$p" ] && exec printf '%s\n' "$p"
done
exit 1
) && progname=$(readlink -e -- "$progname") ||
progname=unknown
[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
progname=unknown
しかし、繰り返しますが、努力する価値はないと思います。
ディレクトリがnotに含まれる2つの状況を次に示します。
> bash scriptname
scriptname
> bash <scriptname
bash
どちらの場合も、現在のディレクトリはscriptnameが配置されたディレクトリでなければなりません。
最初のケースでは、$0
の値をgrep
に渡すことができます。これは、FILE引数が現在のディレクトリに関連していると想定しているためです。
2番目のケースでは、ヘルプとバージョン情報が特定のコマンドラインオプションへの応答としてのみ出力される場合は、問題にはなりません。ヘルプやバージョン情報を出力するために、なぜそのようにスクリプトを呼び出すのかわかりません。
警告
スクリプトが現在のディレクトリを変更する場合、相対パスを使用する必要はありません。
スクリプトがソースである場合、$0
の値は通常、ソーススクリプトではなく呼び出し元スクリプトになります。
ほとんどの(すべての)シェルで-c
オプションを使用する場合、任意の引数ゼロを指定できます。例えば:
sh -c 'echo $0' argv0
man bash
から(これは私のman sh
よりも説明が優れているために選択されました-使用方法は同じです):
-c
-cオプションが存在する場合、コマンドは最初の非オプション引数command_stringから読み取られます。 command_stringの後に引数がある場合、それらは$ 0から始まる定位置パラメーターに割り当てられます。
注:他の人は_$0
_の仕組みをすでに説明しているので、すべてスキップします
私は通常、この問題全体を回避して、単純にコマンド_readlink -f $0
_を使用します。これにより、引数として与えたすべてのパスが常に返されます。
私が最初にここにいるとしましょう:
_$ pwd
/home/saml/tst/119929/adir
_
ディレクトリとファイルを作成します。
_$ mkdir adir
$ touch afile
$ cd adir/
_
readlink
を自慢し始めましょう:
_$ readlink -f ../adir
/home/saml/tst/119929/adir
$ readlink -f ../
/home/saml/tst/119929
$ readlink -f ../afile
/home/saml/tst/119929/afile
$ readlink -f .
/home/saml/tst/119929/adir
_
これで、readlink
を介して_$0
_に問い合わせたときに一貫した結果が返されるので、単純にdirname $(readlink -f $0)
を使用してスクリプトへの絶対パスを取得できます-または-basename $(readlink -f $0)
スクリプトの実際の名前を取得します。
私が使用する同様の何かのために:
rPath="$(dirname $(realpath $0))"
echo $rPath
rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath
rPath
は常に同じ値です。
私のman
ページは言う:
$0
:name
のShell
またはShell script
。
これは、現在のシェルのargv[0]
または、現在解釈しているシェルの最初のnonoperandコマンドライン引数に変換されるようです呼び出されたときに供給されます。 sh ./somescript
は$0 $ENV
variableをsh
しかし、Shell
は独自の新しいプロセスであり、新しい$ENV
。で呼び出されます
このように、sh ./somescript.sh
は. ./somescript.sh
とは異なります current環境および$0
はすでに設定されています。
$0
を/proc/$$/status
と比較することでこれを確認できます。
echo 'script="/proc/$$/status"
echo $0
cat "$script"' \
> ./script.sh
sh ./script.sh ; . ./script.sh
@toxalot、訂正ありがとうございます。私は何かを学びました。
上部のコメントセクションからヘルプとバージョン情報を印刷できるように、現在のスクリプトをgrepしたいと思います。
$0
にはスクリプト名が含まれますが、スクリプトの呼び出し方法に基づいてプレフィックス付きのパスが含まれる場合がありますが、私は常に${0##*/}
を使用して、$0
から先頭のパスを削除するヘルプ出力にスクリプト名を出力しました。
抜粋 高度なBashスクリプトガイド-セクション10.2パラメータの置換
${var#Pattern}
$var
のフロントエンドに一致する$Pattern
の最短部分を$var
から削除します。
${var##Pattern}
$var
のフロントエンドと一致する$Pattern
の最も長い部分を$var
から削除します。
したがって、$0
に一致する*/
の最も長い部分は、パスのプレフィックス全体であり、スクリプト名のみを返します。