为什么 (...) 中设置的变量在 bash 中会丢失?

为什么 (...) 中设置的变量在 bash 中会丢失?
$ p="pineapple" ; echo $p
pineapple
$ (p="peach" ; echo $p)
peach
$ echo $p
pineapple

为什么 shell 自从上次初始化后就不显示 peach 了?括号对这个结果起作用吗?

答案1

是的,这正是括号在类似 Bourne 和类似 csh 的 shell 中的作用:引入一个子 shell 环境(在大多数 shell 中,包括bash通过分叉子进程来实现),修改被限制在其中。

在该子 shell 环境中对 shell 状态的任何更改,无论是变量、选项、别名、函数、重定向、当前工作目录、信号配置...在离开时都会丢失(恢复)。

运行起来非常有用仅有的不同环境中的一些命令。

例如:

(cd /some/dir && cmd)

cmd在不同的目录中运行。

或者像这样的事情:

(IFS=:; set -o noglob; ls -l -- $PATH)

我们仅针对 的一次扩展调整 split+glob 运算符$PATH

如果您想对命令进行分组而不引入子 shell 环境,请使用{}关键字:

a=1
{ echo "I'm running in the main shell environemnt"; a=2;}
echo "$a"
(echo "I'm running in a subshell environment"; a=3)
echo "$a"

{请注意 a 后面和;之前的空格},因为它们是由普通字符组成的关键字,而()是 shell 语法中的特殊字符,此处用于(...)子 shell 构造。

另请注意,这(...)不是引入子 shell 环境的唯一构造。还有(在类似 Bourne/Korn 的 shell 中):

  • 某些形式的命令替换:$(subshell)`subshell`
  • 过程替换:<(subshell), >(subshell),=(subshell)
  • 管道组件:({ subshell; } | othercommand不是 AT&T ksh 或 zsh 中的最后一个组件)。
  • 异步命令:{ subshell; } &
  • 协同处理:{ subshell; } |&coproc { subshell; }

在 POSIX shell 语法中, 和{ ...; }(...)被称为复合命令。两者都可以分配给函数。将函数定义为:

func() (
  ...
)

代替

func() {
  ...
}

意味着一旦调用,该函数的主体就会在子 shell 环境中运行,这使得它的行为更像脚本(不同之处在于它仍然继承调用者的所有变量、别名、函数、选项...)。

fish外壳中,(...)是完全不同的东西。这是命令替换,但确实如此不是引入一个子shell环境:

fish> set a 1; echo $a (set a 2; echo $a) $a
1 2 2

它类似于${ ...; }ksh93 或 mksh shell 的命令替换形式,也不会引入子 shell 环境。

相关内容