在 bash 中何时使用 () 与 {}?

在 bash 中何时使用 () 与 {}?

(...)我正在学习使用 bash 编写 shell 脚本,我需要知道和之间的区别{...}。编写脚本时如何在两者之间进行选择?

答案1

如果你想让命令列表的副作用影响你的当前的shell,使用{...}
如果你想消除任何副作用,使用(...)

例如,我可能会使用子shell:

  • 想要修改$IFS一些命令,但不想$IFS对当前 shell 进行全局修改
  • cd但我不想改变$PWD当前 shell

值得注意的是,在函数定义中可以使用括号:

  • 正常用法:括号:函数体在当前 shell 中执行;函数完成后副作用仍然存在

    $ count_tmp() { cd /tmp; files=(*); echo "${#files[@]}"; }
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /tmp
    $ echo "${#files[@]}"
    11    
    
  • 不寻常的用法:括号:函数体在子 shell 中执行;当子 shell 退出时副作用消失

    $ cd ; unset files
    $ count_tmp() (cd /tmp; files=(*); echo "${#files[@]}")
    $ pwd; count_tmp; pwd
    /home/jackman
    11
    /home/jackman
    $ echo "${#files[@]}"
    0
    

文档

答案2

来自官方bash 文档

()

( list )

将命令列表放在括号之间会导致创建子 shell 环境,并且列表中的每个命令都会在该子 shell 中执行。由于列表是在子 shell 中执行的,因此变量赋值在子 shell 完成后不再有效。

{}

{ list; }

将命令列表放在花括号之间会导致列表在当前 shell 上下文中执行。不会创建子 shell。列表后面的分号(或换行符)是必需的。

答案3

'{}' 中的代码在当前线程/进程/环境中执行,更改会保留,更简洁地说,代码在当前范围内运行。
'()' 中的代码在 bash 的一个单独的子进程内运行,该子进程在执行后被丢弃。这个子进程通常被称为子 shell,可以被认为是一个新的、类似子的作用域。

举例来说,请考虑以下内容...

 ~ # { test_var=test ; }
 ~ # echo $test_var
 test
 ~ # ( test_var2=test2 )
 ~ # echo $test_var2

 ~ # 

请注意,在第一个带有“{}”的例子中,即使在结束“}”之后,变量仍然会被设置,而在带有“()”的例子中,变量不会在“()”的范围之外设置。

还要注意 '{}' 和 '()' 语法差异。“;”分隔符是总是‘{}’ 中的代码是必需的,但 ‘()’ 中的代码不是必需的。

答案4

()除了这里的其他答案之外,我想扩展一下和之间的主要权衡{}

  • 速度
  • 代码可读性
  • 线程污染

具体优缺点举例

以下 3 个函数几乎执行完全相同的操作:它们定义一个变量,然后清除它:

funct1(){ local myvar; myvar="boo"; }
  • 优点:非常快
  • 优点:不会覆盖同名的全局变量
  • 缺点:错误风险:此功能中的其他更改(如cd mydirectory/)在功能完成后仍然存在
  • 反对意见:可读性:要求变量以local myvar myvar2 ...
  • 缺点:错误风险:忘记启动local可能会导致覆盖全局变量
funct2(){ myvar="boo"; unset myvar; }
  • 优点:非常快(与 funct1 差不多,可能快到可以忽略不计)
  • 缺点:错误风险:总是使用相同名称覆盖全局变量
  • 缺点:错误风险:此功能中的其他更改(例如cd mydirectory/功能完成后仍然存在)
  • 反对意见:可读性:要求使用以下方式取消设置变量unset myvar myvar2 ...
  • 缺点:错误风险:忘记取消设置变量可能会导致全局变量具有非预期的值
funct3()(myvar="boo")
  • 反对意见:执行时间约为 funct1 或 funct2 的 40 到 45 倍
  • 优点:不会覆盖同名的全局变量
  • 优点:此功能中的其他更改(例如cd mydirectory/在功能完成后清除)
  • 优点:不需要显式的变量清理命令
  • 优点:简单且不易出错

基准

您可以使用此脚本来比较这 3 个函数。每个测试运行每个函数 1000 次,测试运行 5 次。之后输出每个函数的平均时间。

#!/bin/bash

funct1(){ local myvar; myvar="boo"; }
funct2(){ myvar="boo"; unset myvar; }
funct3()(myvar="boo")

typeset -A tests
tests=(
[funct1]=0
[funct2]=0
[funct3]=0
)

for n in {0..5}; do
  for f in funct1 funct2 funct3; do
    start=$(date +%s.%N)
    for i in {1..1000}; do $f; done
    dur=$(echo "$(date +%s.%N) - $start" | bc)
    tests[$f]="$(echo "$(printf "%.6f" "$dur") + ${tests[$f]}"|bc)"
  done
done

for i in $(printf "%s\n" "${!tests[@]}"|sort); do
    echo "$i average: $(printf "%.6f" "$(echo "${tests[$i]} / 5"|bc -l)")"
done

exit

运行该脚本的示例输出:

funct1 average: 0.014117
funct2 average: 0.012473
funct3 average: 0.558457

相关内容