Возможно ли shift
присвоить значение параметру массива изнутри подоболочки?
Пример кода:
arr=(a b c)
(shift arr)
echo $arr
# prints: a b c
# should print: b c
решение1
В этом и заключается суть подоболочек — запустить часть кода в копии текущегосреда выполнения оболочки(подробности см. вспецификация POSIXдля sh
), чтобы сохранить исходную, поэтому весь смысл в том, что любые изменения переменных, сделанные в подоболочке, будут потеряны после завершения подоболочки.
Традиционно это делается путем ответвления оболочки процесса и запуска кода в дочернем процессе, пока родительский процесс ждет его завершения.
POSIX не требует этого, и ksh93, по крайней мере, реализует (...)
подоболочки, тщательно восстанавливая исходную среду после возврата подоболочки без разветвления процесса, если этого можно избежать (хотя иногда в некоторых особых случаях это не удается сделать должным образом).
zsh
делает fork процесса для этого, как и большинство других оболочек. Есть исключения для оптимизации, например, когда (...)
подоболочка является последней командой в 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
, интерпретировал подоболочку и даже выполнил последнюю 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
vs shift arr argv
) в зависимости от того, arr
объявлена ли она как переменная массива или нет. Использование shift 1 arr
делает более очевидным, что вам нужна именно первая.
решение2
Подоболочка — это отдельный процесс, он не может изменять родительский процесс.