サブシェルプロセスのないサブシェルの例

サブシェルプロセスのないサブシェルの例

私は最近、「サブシェル」が「サブシェルプロセス」と同じではないことに気づきました(たとえば参照)。「サブシェル」と「サブプロセス」の正確な違いは何ですか?とPOSIXの定義サブシェルそしてサブプロセス)。

これを確実にするために、サブシェルを作成せずにサブシェルが作成されたことを表示(証明)するコマンドを探しています。

現在私が試しているすべては、作成時にサブシェルを生成するようです。

$ echo $BASHPID; (pwd; cd ..; echo $BASHPID; pwd); pwd      # `( ...)` executed in a subshell
                                                            # and in a child-shell process

$ >&2 ps | ps       # Theoretically executed in two subshells and apparently without child-shells
                    # but I cannot be sure due to the outcome of the next example

$ $ >&2 echo $BASHPID | ps      # `ps` doesn't display a child-shell for the execution of `echo`
953790                          # but `echo $BASHPID` shows a new process that is necessarily
    PID TTY         TIME CMD    # a child-shell since echo is a built-in 
 948538 pts/2   00:00:00 bash
 953791 pts/2   00:00:00 ps

私はサブシェルがあるとしても必ずしもサブシェルがあるという意味ではないことを証明する方法を探しています。

ヘビーストライク 5.0.17

答え1

Bash シェルでは、サブシェルはサブプロセスを分岐して実装されるため、サブプロセスで実行されていないサブシェルはシェルに表示されません。

ksh93は、可能であればサブシェル分岐をスキップする私が知っている唯一のシェルです(この最適化はまだバグが多く、AT&Tがそれを作成したチームを解体した後にそれを維持しようとする後続の人々によって削除されると考えられています)。

たとえば、次のような場合:

 strace ksh93 -c 'pwd; (cd /; umask 0; pwd; exit 2); pwd'

ksh93はプロセスを分岐せずに、次のようなことをするのを見ることができます。

openat(AT_FDCWD, ".", O_RDONLY|O_PATH)  = 3
fcntl(3, F_DUPFD, 10)                   = 10
close(3)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
[...]

現在のディレクトリをfd 10に保存します。それから:

chdir("/")                              = 0
umask(000)                              = 002

これにより、サブシェルの現在のディレクトリとumaskが変更されます。サブシェルが終了すると(システムコールが呼び出されexit 2ない):_exit()

fchdir(10)                              = 0
close(10)                               = 0

現在の作業ディレクトリを復元するには、次の手順を実行します。

umask(002)                              = 000

ウマスクを復元します。

一部のシェル(FreeBSDなど)は、sh次のような非常に特定の状況でフォークをスキップできます。

var=$(printf %04d "$n")

(ここには組み込みのものprintfがあり、環境は変わりません。)

パイプラインでは、すべてのコンポーネントを同時に実行する必要があるため、ksh93でも別々のプロセスで実行する必要があります。

では、bashすべて子プロセスで実行されます。 AT&T ksh や zsh または with bash -O lastpipe(非対話型の場合) では、一番右のプロセスはそうではありません (もちろん、外部コマンド (例: ) を実行するには子プロセスをフォークする必要がありますps)。

bashbashインタプリタパイプラインコンポーネントであるサブシェルがその前のサブプロセスで直接実行されるため、追加のプロセスはps >&2 | ps表示されません。たとえば、(ps)psps

n=0; /bin/true "$((n=1))" | /bin/echo "$((n=2))"; echo "$n"

2bash と zsh/ksh93 で と を見ることができます。サブプロセスで実行され、以前に完了したサブシェルプロセスで直接実行されます。 bashの(and)と同じですが、//ではメインシェルプロセスで完了し、サブプロセスは外部プロセスを実行するためにフォークのみを実行します。パイプラインの一部として実行されていない場合と同様に、ユーティリティです。022/bin/true/bin/echo/bin/truen=1/bin/echon=2zshkshbash -O lastpipen=2/bin/echo "$((n=2))"

bashzsh/とは対照的にksh)はbash追加のプロセスを表示します(: anything; ps)。最適化は、サブシェルに外部コマンドが1つしかない場合にのみ実行されます。以下をexec使用して手動で最適化する必要があります(: anything; exec ps)

にも適用されます{ ps; } | cat

答え2

私はあなたが「サブシェル」と「サブプロセス」(サブプロセスとも呼ばれる)を誤解していると思います。

サブシェルは、ほぼ現在のシェルが呼び出されたときに取得されますfork()。フォークはサブプロセスを生成するので、サブシェルが生成されます。はい子プロセス

Bashのマニュアルページには、bashが「サブシェルでコマンドを実行する」と記載していますが、ここで明確でない点は、外部プロセス(例えばps)を実行し、exec()サブシェルから呼び出され、実行中のbashを次の実行可能ファイルに置き換えますということです。新しいコマンドサブシェル。 bashでwhenを使用する場合など、一部の場合は()サブシェルが開始(および終了し)、それらの間のコマンドは独自のサブシェル/サブプロセスで開始できます。

サブシェルがサブプロセスとは異なる唯一の方法は、サブプロセスが同じ実行可能ファイルの他のインスタンスである場合とそうでない場合があります。

関連情報