从子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是一个不同的进程,它不能修改父进程。

相关内容