從子shell內部修改數組參數

從子shell內部修改數組參數

是否可以shift從子 shell 內部為數組參數賦值或賦值?

範例程式碼:

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

答案1

這是子 shell 的全部要點,在目前的副本中執行部分程式碼shell執行環境(詳情見POSIX 規範for sh),以便保留原始變量,因此在子 shell 中對變數所做的任何更改都將在子 shell 終止後遺失。

傳統上,這是透過 shell 分叉一個進程並在父進程等待其終止時在子進程中運行程式碼來完成的。

POSIX 沒有強制要求這樣做,並且 ksh93 至少(...)透過在返回子shell 時仔細恢復原始環境來實現子shell,而無需分叉進程(如果可以避免的話)(儘管有時在某些極端情況下無法正確執行此操作)。

zsh像大多數其他 shell 一樣,會為此分叉一個進程。最佳化也有例外,例如當(...)子 shell 是腳本中的最後一個命令時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

它與上面執行的 21085 進程相同zsh,解釋了子 shell,甚至執行了最後一個ps命令。

設定 a 足以trap使該最佳化無效,重點是只有當 zsh 可以保證子 shell 返回後 shell 不會執行任何內容時,才會執行此操作。

為了使子進程能夠更改父進程中變數的值,它需要執行諸如附加gdb到該進程並向其中註入程式碼以更改該進程中的內部記憶體結構之類的操作。

如果您想要取得子 shell 定義的陣列的值,則需要子 shell 將其定義傳遞給父 shell。例如,可以透過以下方式:

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 arrvs shift arr argv),取決於是否arr聲明為數組變數。使用shift 1 arr可以更明顯地表明它是您想要的前者。

答案2

子shell是一個不同的進程,它不能修改父進程。

相關內容