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 arr
とshift arr argv
)を持つため、あいまいです。 を使用すると、前者が必要であることがより明確になります。arr
shift 1 arr
答え2
サブシェルは別のプロセスであり、親プロセスを変更することはできません。