是否可以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 arr
vs shift arr argv
),具体取决于是否arr
声明为数组变量。使用shift 1 arr
可以更明显地表明它是您想要的前者。
答案2
子shell是一个不同的进程,它不能修改父进程。