Skip to content
SRE运维进阶之路SRE运维进阶之路
github icon
    • 第一部分 初见shell

      • 第一章 为什么使用shell编程
        • 第二章 和Sha-Bang(#!)一起出发

          • 2.1 调用一个脚本
            • 2.2 牛刀小试
          • 第二部分 shell基础

            • 第三章 特殊字符
              • 第四章 变量与参数

                • 4.1 变量替换
                  • 4.2 变量赋值
                    • 4.3 Bash变量是弱类型的
                      • /code/shell/part2/04_4_special_variable_types
                      • 第五章 引用

                        • 5.1 引用变量
                          • 5.2 转义
                          • 第六章 退出与退出状态
                            • 第七章 测试

                              • 7.1 测试结构
                                • 7.2 文件测试操作
                                  • 7.3 其他比较操作
                                    • 7.4 嵌套 if/then 条件测试
                                      • 7.5 牛刀小试
                                      • 第八章 运算符相关话题

                                        • 8.1 运算符
                                          • 8.2 数字常量
                                            • 8.3 双圆括号结构
                                              • 8.4 运算符优先级
                                            • 第三部分 shell进阶

                                              • 第九章 换个角度看变量

                                                • 9.1 内部变量
                                                  • 9.2 变量类型标注:declare 与 typeset
                                                    • 9.2.1 declare 的另类用法
                                                      • 9.3 $RANDOM:生成随机数
                                                      • 第十章 变量处理

                                                        • 10.1 字符串处理
                                                          • 10.1.1 使用 awk 处理字符串
                                                            • 10.1.2 参考资料
                                                              • 10.2 参数替换
                                                              • 第十一章 循环与分支

                                                                • 11.1 循环
                                                                  • 11.2 嵌套循环
                                                                    • 11.3 循环控制
                                                                      • 11.4 测试与分支
                                                                      • 第十二章 命令替换
                                                                        • 第十三章 算术扩展
                                                                        • 第四部分 命令
                                                                          • 第五部分 高级话题

                                                                            • 18 正则表达式

                                                                              • 18.1 正则表达式简介
                                                                                • 18.2文件名替换
                                                                                  • 18.3 正则表达式对照表
                                                                                  • 19 嵌入文档
                                                                                    • 20 I/O 重定向

                                                                                      • 20.1 使用 exec
                                                                                        • 20.2 重定向代码块
                                                                                          • 20.3 应用程序
                                                                                          • 第二十一章 子shell
                                                                                            • Notes
                                                                                            • 第二十二章. 限制模式的Shell
                                                                                              • 第二十三章. 进程替换
                                                                                                • 24 函数

                                                                                                  • 24.1 复杂函数和函数复杂性
                                                                                                    • 24.2 局部变量
                                                                                                      • 24.3 不使用局部变量的递归
                                                                                                      • 25. 别名
                                                                                                        • 26. 列表结构
                                                                                                          • 27 数组
                                                                                                            • 30 网络编程
                                                                                                              • 33 选项
                                                                                                                • 第34章 陷阱
                                                                                                                  • 第36章 杂项
                                                                                                                    • echo命令
                                                                                                                    • 第六部分 Google Shell 风格指南
                                                                                                                    • 前端学习笔记

                                                                                                                      第二十一章 子shell

                                                                                                                      author iconLinuxStorycalendar icon2021年5月11日category icon
                                                                                                                      • Linux
                                                                                                                      tag icon
                                                                                                                      • Bash
                                                                                                                      timer icon大约 6 分钟

                                                                                                                      此页内容
                                                                                                                      • Notes

                                                                                                                      # 第二十一章 子shell

                                                                                                                      运行一个shell脚本会启动一个新的进程,即子shell。

                                                                                                                      定义: 一个子shell是由一个shell(或shell脚本)触发的子进程open in new window。

                                                                                                                      一个子shell是命令处理器(-- 在终端或者xtrem窗口给出提示符的shell)的一个独立的例子。正如你的命令在命令行提示符处被理解执行一样,一个脚本批处理open in new window一组命令。每一个shell脚本运行实际上是父open in new windowshell的一个支线进程(子进程)。

                                                                                                                      一个shell脚本可以自己启动多个子进程。这些子进程使得脚本进行并行处理,实际上是多个支线任务同时进行。

                                                                                                                      #!/bin/bash
                                                                                                                      # subshell-test.sh
                                                                                                                      
                                                                                                                      (
                                                                                                                      # 在圆括号内,因此是一个子shell . . .
                                                                                                                      while [ 1 ]   # 无限循环.
                                                                                                                      do
                                                                                                                        echo "Subshell running . . ."
                                                                                                                      done
                                                                                                                      )
                                                                                                                      
                                                                                                                      #  脚本会永远运行,或者至少直到由Ctl-C终止。
                                                                                                                      
                                                                                                                      exit $?  # 脚本结束 (但是永远无法到达这里)。
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      现在,运行这个脚本:
                                                                                                                      sh subshell-test.sh
                                                                                                                      
                                                                                                                      另外,在脚本运行的同时, 从另一个xterm运行:
                                                                                                                      ps -ef | grep subshell-test.sh
                                                                                                                      
                                                                                                                      UID       PID   PPID  C STIME TTY      TIME     CMD
                                                                                                                      500       2698  2502  0 14:26 pts/4    00:00:00 sh subshell-test.sh
                                                                                                                      500       2699  2698 21 14:26 pts/4    00:00:24 sh subshell-test.sh
                                                                                                                      
                                                                                                                                ^^^^
                                                                                                                      
                                                                                                                      分析:
                                                                                                                      PID 2698, 脚本, 启动 PID 2699, 子shell.
                                                                                                                      
                                                                                                                      注释: “UID ...”这一列可以通过“grep”命令筛去,但是由于说明的目的而显示在这里。
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16
                                                                                                                      17
                                                                                                                      18
                                                                                                                      19
                                                                                                                      20
                                                                                                                      21
                                                                                                                      22
                                                                                                                      23
                                                                                                                      24
                                                                                                                      25
                                                                                                                      26
                                                                                                                      27
                                                                                                                      28
                                                                                                                      29
                                                                                                                      30
                                                                                                                      31
                                                                                                                      32
                                                                                                                      33

                                                                                                                      一般来说,脚本的一个外部命令open in new window会使得子进程产生分叉open in new window,[1] 但是一个Bash内建命令不会如此。

                                                                                                                      在圆括号内的命令列

                                                                                                                      (命令1; 命令1; 命令3; ...)

                                                                                                                      放在圆括号内的一列命令作为子shell运行。
                                                                                                                      

                                                                                                                      子shell的变量不能被这个子shell内代码区块之外的部分看见。这些变量不能被父进程open in new window中调用,也不能被启动次子shell的shell调用。这些变量实际上是子进程的局部变量open in new window。

                                                                                                                      例21-1.子shell的变量范围

                                                                                                                      #!/bin/bash
                                                                                                                      # subshell.sh
                                                                                                                      
                                                                                                                      echo
                                                                                                                      
                                                                                                                      echo "We are outside the subshell."
                                                                                                                      echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
                                                                                                                      # Bash, 版本3,增加新变量                 $BASH_SUBSHELL 。
                                                                                                                      echo; echo
                                                                                                                      
                                                                                                                      outer_variable=Outer
                                                                                                                      global_variable=
                                                                                                                      #  定义全局变量来”存储“子shell变量值。
                                                                                                                      
                                                                                                                      (
                                                                                                                      echo "We are inside the subshell."
                                                                                                                      echo "Subshell level INSIDE subshell = $BASH_SUBSHELL"
                                                                                                                      inner_variable=Inner
                                                                                                                      
                                                                                                                      echo "From inside subshell, \"inner_variable\" = $inner_variable"
                                                                                                                      echo "From inside subshell, \"outer\" = $outer_variable"
                                                                                                                      
                                                                                                                      global_variable="$inner_variable"   #  这会允许”输出“ 一个子shell变量吗?
                                                                                                                      )
                                                                                                                      
                                                                                                                      echo; echo
                                                                                                                      echo "We are outside the subshell."
                                                                                                                      echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
                                                                                                                      echo
                                                                                                                      
                                                                                                                      if [ -z "$inner_variable" ]
                                                                                                                      then
                                                                                                                        echo "inner_variable undefined in main body of shell"
                                                                                                                      else
                                                                                                                        echo "inner_variable defined in main body of shell"
                                                                                                                      fi
                                                                                                                      
                                                                                                                      echo "From main body of shell, \"inner_variable\" = $inner_variable"
                                                                                                                      #  $inner_variable 会显示为空白 (未初始化) 
                                                                                                                      #+ 因为定义在子shell的变量是“局部变量”。
                                                                                                                      #  有办法改正这一点吗?
                                                                                                                      echo "global_variable = "$global_variable""  # 为什么这不行?
                                                                                                                      
                                                                                                                      echo
                                                                                                                      
                                                                                                                      # =======================================================================
                                                                                                                      
                                                                                                                      # 另外 ...
                                                                                                                      
                                                                                                                      echo "-----------------"; echo
                                                                                                                      
                                                                                                                      var=41                                                 # 全局变量。
                                                                                                                      
                                                                                                                      ( let "var+=1"; echo "\$var INSIDE subshell = $var" )  # 42
                                                                                                                      
                                                                                                                      echo "\$var OUTSIDE subshell = $var"                   # 41
                                                                                                                      # 子shell内的变量操作,即使是对全局变量,不影响变量在子shell外的值!
                                                                                                                      
                                                                                                                      
                                                                                                                      exit 0
                                                                                                                      
                                                                                                                      #  问题:
                                                                                                                      #  --------
                                                                                                                      #  一旦执行一个子shell,
                                                                                                                      #+ 是否有办法再次进入这个子shell以便修改或调用子shell的变量? 
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16
                                                                                                                      17
                                                                                                                      18
                                                                                                                      19
                                                                                                                      20
                                                                                                                      21
                                                                                                                      22
                                                                                                                      23
                                                                                                                      24
                                                                                                                      25
                                                                                                                      26
                                                                                                                      27
                                                                                                                      28
                                                                                                                      29
                                                                                                                      30
                                                                                                                      31
                                                                                                                      32
                                                                                                                      33
                                                                                                                      34
                                                                                                                      35
                                                                                                                      36
                                                                                                                      37
                                                                                                                      38
                                                                                                                      39
                                                                                                                      40
                                                                                                                      41
                                                                                                                      42
                                                                                                                      43
                                                                                                                      44
                                                                                                                      45
                                                                                                                      46
                                                                                                                      47
                                                                                                                      48
                                                                                                                      49
                                                                                                                      50
                                                                                                                      51
                                                                                                                      52
                                                                                                                      53
                                                                                                                      54
                                                                                                                      55
                                                                                                                      56
                                                                                                                      57
                                                                                                                      58
                                                                                                                      59
                                                                                                                      60
                                                                                                                      61
                                                                                                                      62
                                                                                                                      63
                                                                                                                      64
                                                                                                                      65

                                                                                                                      同样参看 $BASHPIDopen in new window 和 例34-2open in new window。

                                                                                                                      定义: 变量的范围是指其有意义的上下文内容,在此变量值可以被引用。比如说,局部变量open in new window的范围只在函数、代码区块或子shell内的相应定义范围内,而全局变量的范围则是其出现的整个脚本区域。

                                                                                                                      内部变量 $BASH_SUBSHELLopen in new window 指出一个子shell的嵌套层级时,而变量 $SHLVLopen in new window 指示在子shell内不变的层级。

                                                                                                                      echo " \$BASH_SUBSHELL outside subshell       = $BASH_SUBSHELL"           # 0
                                                                                                                        ( echo " \$BASH_SUBSHELL inside subshell        = $BASH_SUBSHELL" )     # 1
                                                                                                                        ( ( echo " \$BASH_SUBSHELL inside nested subshell = $BASH_SUBSHELL" ) ) # 2
                                                                                                                      # ^ ^                          ***  嵌套   ***                        ^ ^
                                                                                                                      
                                                                                                                      echo
                                                                                                                      
                                                                                                                      echo " \$SHLVL outside subshell = $SHLVL"       # 3
                                                                                                                      ( echo " \$SHLVL inside subshell  = $SHLVL" )   # 3 (不变!)
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9

                                                                                                                      子shell内的路径改变不会带入到父shell中。

                                                                                                                      例21-2. 列出用户信息

                                                                                                                      #!/bin/bash
                                                                                                                      # allprofs.sh: 打印所有用户信息.
                                                                                                                      
                                                                                                                      # 此脚本作者 Heiner Steven,由文件作者修改。
                                                                                                                      
                                                                                                                      FILE=.bashrc  #  包含用户信息的文件是".profile"的原始脚本。
                                                                                                                      
                                                                                                                      for home in `awk -F: '{print $6}' /etc/passwd`
                                                                                                                      do
                                                                                                                        [ -d "$home" ] || continue    # 如果没有home目录,到下一个。
                                                                                                                        [ -r "$home" ] || continue    # 如果没有读取权限,到下一个。
                                                                                                                        (cd $home; [ -e $FILE ] && less $FILE)
                                                                                                                      done
                                                                                                                      
                                                                                                                      # 脚本终止时, 不需要使用命令'cd'回到初始目录,因为'cd $home'只在子shell发生。
                                                                                                                      
                                                                                                                      exit 0
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16
                                                                                                                      17

                                                                                                                      一个子shell可以用来为一个命令组设定一个“特定环境”。

                                                                                                                      命令1
                                                                                                                      命令2
                                                                                                                      命令3
                                                                                                                      (
                                                                                                                        IFS=:
                                                                                                                        PATH=/bin
                                                                                                                        unset TERMINFO
                                                                                                                        set -C
                                                                                                                        shift 5
                                                                                                                        命令4
                                                                                                                        命令5
                                                                                                                        exit 3 # 只退出子shell!
                                                                                                                      )
                                                                                                                      # 父shell不受影响, 且环境保留。
                                                                                                                      命令6
                                                                                                                      命令7
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16

                                                                                                                      从这里可以看出,命令 exitopen in new window 只终止正在运行的子shell,并不终止父shell或脚本。

                                                                                                                      这样的“特定环境”的一个应用是检查一个变量是否被定义。

                                                                                                                      if (set -u; : $variable) 2> /dev/null
                                                                                                                      then
                                                                                                                        echo "Variable is set."
                                                                                                                      fi     #  变量已在当前脚本被设定, 
                                                                                                                             #+ 或者变量是一个Bash内部变量,
                                                                                                                             #+ 或者变量在环境变量中(在export命令后)。
                                                                                                                      
                                                                                                                      # 也可以写成  [[ ${variable-x} != x || ${variable-y} != y ]]
                                                                                                                      # 或者       [[ ${variable-x} != x$variable ]]
                                                                                                                      # 或者       [[ ${variable+x} = x ]]
                                                                                                                      # 或者       [[ ${variable-x} != x ]]
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11

                                                                                                                      另一个应用是检查一个锁定文件。

                                                                                                                      if (set -C; : > lock_file) 2> /dev/null
                                                                                                                      then
                                                                                                                        :   # lock_file不存在:没有用户运行此脚本
                                                                                                                      else
                                                                                                                        echo "Another user is already running that script."
                                                                                                                      exit 65
                                                                                                                      fi
                                                                                                                      
                                                                                                                      #  代码段作者 Stéphane Chazelas,
                                                                                                                      #+ 修改者 Paulo Marcel Coelho Aragao。
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10

                                                                                                                      多个进程可以在不同子shell内并行执行。这样就可以将一个复杂的任务分解成多个子部分同时处理。

                                                                                                                      例21-3. 在子shell中运行并行进程

                                                                                                                      	(cat list1 list2 list3 | sort | uniq > list123) &
                                                                                                                      	(cat list4 list5 list6 | sort | uniq > list456) &
                                                                                                                      	# 同时合并和排列两组列表。
                                                                                                                      	# 在后台运行以确保并行执行。
                                                                                                                      	#
                                                                                                                      	# 同样效果如下
                                                                                                                      	#   cat list1 list2 list3 | sort | uniq > list123 &
                                                                                                                      	#   cat list4 list5 list6 | sort | uniq > list456 &
                                                                                                                      	
                                                                                                                      	wait   # 在子shell结束前不执行之后命令。
                                                                                                                      	
                                                                                                                      	diff list123 list456
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12

                                                                                                                      向子shell的I/O重定向使用管道算符"|",正如 ls -al | (命令)

                                                                                                                      在花括号间的代码块不会启动一个子shell。

                                                                                                                      { 命令1; 命令2; 命令3; ...命令N; }

                                                                                                                      var1=23
                                                                                                                      echo "$var1"   # 23
                                                                                                                      
                                                                                                                      { var1=76; }
                                                                                                                      echo "$var1"   # 76
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5

                                                                                                                      # Notes


                                                                                                                      1. 和 execopen in new window 命令一起触发的外部命令(通常)不会分叉一个子进程 / 子shell ↩︎

                                                                                                                      edit icon编辑此页open in new window
                                                                                                                      上次编辑于: 2022/4/27 15:33:00
                                                                                                                      贡献者: clay-wangzhi
                                                                                                                      上一页
                                                                                                                      20 I/O 重定向
                                                                                                                      下一页
                                                                                                                      第二十二章. 限制模式的Shell
                                                                                                                      备案号:冀ICP备2021007336号
                                                                                                                      Copyright © 2023 LinuxStory