web-dev-qa-db-ja.com

ドットスペースとドットスラッシュを使用したファイルの実行

既存のコードライブラリを操作しようとしていますが、問題が発生しました。要するに、私はシェルスクリプト(これをAと呼びましょう)を実行します。その最初の行為は別のスクリプト(B)を呼び出すことです。スクリプトBは現在のディレクトリにあります(使用しているプログラムの要件)。ソフトウェアのマニュアルはbashを参照していますが、Aのコメントは、それがkshで開発されたことを示唆しています。私はこれまでbashで運営してきました。

A内では、Bを実行する行は単純です。

_. B
_

「ドットスペース」構文を使用してプログラムを呼び出します。 Sudoのような異常なことは何もしません。

ドットスペース構文なしでAを呼び出すと、次のようになります。

_./A
_

ファイルBが見つからないと常にエラーが発生します。 pwdlswhoami、_echo $Shell_、および_echo $PATH_行をAに追加してデバッグし、Bは実際にはそこにあり、スクリプトはコマンドプロンプトで実行しているのと同じ_$Shell_で実行されており、スクリプトは私と同じユーザーであり、スクリプトの検索パスは同じです_$PATH_私と同じように。また、次のことも確認しました。

_. B
_

コマンドラインでは、問題なく動作します。しかし、A内の構文を次のように変更すると次のようになります。

_./B
_

代わりに、Aが正常に実行されます。

同様に、ドットスペース構文でAを実行すると、_. B_と_./B_の両方が機能します。

要約
_./A_は、Aに_./B_構文が含まれている場合にのみ機能します。
_. A_は、_./B_または_. B_構文のいずれかでAに対して機能します。

ドットスペース(つまり、_. A_)構文の使用は、サブシェルにフォークせずに実行されることを理解していますが、ファイルが明らかにそこにあることを考えると、これが私が観察している動作にどのようにつながるかはわかりません。構文や親/子プロセスワークスペースのニュアンスについて私が見逃しているものはありますか?魔法?

PDATE1kshを使用しているときに、スクリプトがbashで開発された可能性があることを示す情報を追加しました。
PDATE2:_$PATH_が同じであることを確認するためのチェックを追加しました。

PDATE:スクリプトはksh用に作成されたと言っていますが、bashで実行されています。 Kensterの回答に応えて、コマンドラインで_bash -posix_を実行してから_. B_を実行すると失敗することがわかりました。これは、コマンドラインとスクリプトの環境の違いは、後者がPOSIX準拠モードでbashを実行しているのに対し、コマンドラインはそうではないことを示しています。もう少し詳しく見てみると、これはbashmanページにあります。

Shとして呼び出されると、bashはスタートアップファイルが読み取られた後にposixモードに入ります。

ShebangAは確かに_#!/bin/sh_です。

要約すると、ドットスペース構文なしでAを実行すると、Shebangが_#!/bin/sh_であるため、POSIX準拠モードである独自のサブシェルにフォークします(たとえば、 、_#!/bin/bash_。これは、コマンドライン環境とスクリプトランタイム環境の重大な違いであり、ABを見つけることができなくなります。

22
TTT

コマンドパスがどのように機能し、いつ使用されるかから始めましょう。次のようなコマンドを実行すると、次のようになります。

ls /tmp

ここでのlsには/文字が含まれていないため、シェルはコマンドパス(PATH環境変数の値)内のディレクトリでlsという名前のファイルを検索します。見つかった場合は、そのファイルを実行します。 lsの場合、通常は/binまたは/usr/binにあり、これらのディレクトリは両方とも通常はパスにあります。

コマンドWordで/を使用してコマンドを発行すると、次のようになります。

/bin/ls /tmp

シェルはコマンドパスを検索しません。特にファイル/bin/lsを探し、それを実行します。

./Aの実行は、名前に/を含むコマンドの実行例です。シェルはコマンドパスを検索しません。特に./Aという名前のファイルを探し、それを実行します。 「。」は現在の作業ディレクトリの省略形であるため、./Aは現在の作業ディレクトリにあるはずのファイルを指します。ファイルが存在する場合は、他のコマンドと同じように実行されます。例えば:

cd /bin
./ls

/bin/lsを実行すると機能します。

. Aの実行は、ソーシングファイルの例です。ソースとなるファイルは、シェルコマンドを含むテキストファイルである必要があります。これは、新しいプロセスを開始することなく、現在のシェルによって実行されます。ソースとなるファイルは、コマンドが見つかるのと同じ方法で見つかります。ファイルの名前に/が含まれている場合、シェルは指定された特定のファイルを読み取ります。ファイルの名前に/が含まれていない場合、シェルはコマンドパスでそのファイルを探します。

. A        # Looks for A using the command path, so might source /bin/A for example
. ./A      # Specifically sources ./A

そのため、スクリプトは. Bを実行しようとしますが、現在のディレクトリにBという名前のファイルがある場合でも、Bが存在しないと主張できません。上で説明したように、Bには/文字が含まれていなかったため、シェルはコマンドパスでBを検索していました。コマンドを検索するとき、シェルは現在のディレクトリを自動的に検索しません。現在のディレクトリがコマンドパスの一部である場合にのみ、そのディレクトリを検索します。

つまり、「。」がないため、. Bはおそらく失敗しています。 (現在のディレクトリ)コマンドパスにあり、Bをソースしようとしているスクリプトは「。」と想定しています。あなたの道の一部です。私の意見では、これはスクリプトのバグです。多くの人が「。」なしで走ります。それらのパスにあり、スクリプトはそれに依存するべきではありません。

編集:

kshを使用しているときに、スクリプトはbashを使用すると言います。 KshはPOSIX標準に従います-実際、KSHはPOSIX標準の基礎でした-そして私が説明したように常にコマンドパスを検索します。Bashには「POSIXモード」と呼ばれるフラグがあり、その方法を制御します。厳密にはPOSIX標準に準拠しています。POSIXモード(一般的に使用されている方法)でない場合、bashは、コマンドパスにファイルが見つからない場合、ソースとなるファイルの現在のディレクトリをチェックします。

そのbashインスタンス内でbash -posixを実行して. Bを実行した場合、それが機能しないことがわかるはずです。

29
Kenster