サブシェル内から配列パラメータを変更する

サブシェル内から配列パラメータを変更する

shiftサブシェル内から配列パラメータに値を割り当てることは可能ですか?

コード例:

arr=(a b c)
(shift arr)
echo $arr
# prints: a b c
# should print: b c

答え1

サブシェルの目的は、現在のシェルのコピーでコードの一部を実行することです。シェル実行環境(詳細はPOSIX仕様)はsh、元の変数を保持するため、サブシェル内で行われた変数への変更は、サブシェルの終了後に失われるという点が重要です。

従来、これはシェルがプロセスをフォークし、親が終了を待機している間に子でコードを実行することによって行われます。

POSIX ではそれが必須ではなく、ksh93 では、(...)サブシェルが返されたときにプロセスをフォークせずに元の環境を慎重に復元することでサブシェルを実装しています (ただし、一部の特殊なケースでは適切に復元できないこともあります)。

zsh他のほとんどのシェルと同様に、そのためにプロセスをフォークします。サブシェルがスクリプト(...)の最後のコマンドである場合など、最適化には例外があります。zsh -c

$ zsh -c 'zmodload zsh/system; echo $$; (echo $sysparams[pid]; ps; ps)'
21085
21085
    PID TTY          TIME CMD
   1839 pts/4    00:00:00 zsh
  21085 pts/4    00:00:00 zsh
  21086 pts/4    00:00:00 ps
    PID TTY          TIME CMD
   1839 pts/4    00:00:00 zsh
  21085 pts/4    00:00:00 ps

zshこれは、実行され、サブシェルを解釈し、最後のコマンドを実行した上記の 21085 プロセスと同じですps

その最適化を無効にするにはを設定するだけで十分ですtrap。ポイントは、サブシェルが戻った後にシェルによって何も実行されないことを zsh が保証できる場合にのみ実行されるということです。

子プロセスが親プロセスの変数の値を変更できるようにするには、gdbそのプロセスにアタッチして、そのプロセスの内部メモリ構造を変更するコードを挿入するなどの操作を行う必要があります。

サブシェルで定義された配列の値を取得したい場合は、サブシェルがその定義を親に渡す必要があります。たとえば、次のようにします。

eval "$(
  # also a subshell using $(...)
  arr=( a b c )
  typeset -p arr
)"

次にtypeset出力しますtypeset -a arr=( a b c )(またはtypeset -g -a arr=( a b c )関数内で呼び出された場合)。これがeval実行されると、親に同じ変数が作成されます。

ちなみに、代わりに:

shift arr

私は使うだろう:

shift 1 arr

または:

arr[1]=()

このshift arrコードは、が配列変数として宣言されているかどうかによって異なる意味 (shift 1 arrshift arr argv)を持つため、あいまいです。 を使用すると、前者が必要であることがより明確になります。arrshift 1 arr

答え2

サブシェルは別のプロセスであり、親プロセスを変更することはできません。

関連情報