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
                                                                                            • 第二十二章. 限制模式的Shell
                                                                                              • 第二十三章. 进程替换
                                                                                                • 24 函数

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

                                                                                                                      第二十三章. 进程替换

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

                                                                                                                      # 第二十三章. 进程替换

                                                                                                                      用管道open in new window 将一个命令的 标准输出 输送到另一个命令的 标准输入 是个强大的技术。但是如果你需要用管道输送多个命令的 标准输出 怎么办?这时候 进程替换 就派上用场了。

                                                                                                                      进程替换 把一个(或多个)进程open in new window 的输出送到另一个进程的 标准输入。

                                                                                                                      样板 命令列表要用括号括起来

                                                                                                                      >(command_list)
                                                                                                                      <(command_list)
                                                                                                                      
                                                                                                                      1
                                                                                                                      2

                                                                                                                      进程替换使用 /dev/fd/<n> 文件发送括号内进程的结果到另一个进程。[1]

                                                                                                                      "<"或">"与括号之间没有空格,加上空格或报错。

                                                                                                                      bash$ echo >(true)
                                                                                                                      /dev/fd/63
                                                                                                                      
                                                                                                                      bash$ echo <(true)
                                                                                                                      /dev/fd/63
                                                                                                                      
                                                                                                                      bash$ echo >(true) <(true)
                                                                                                                      /dev/fd/63 /dev/fd/62
                                                                                                                      
                                                                                                                      bash$ wc <(cat /usr/share/dict/linux.words)
                                                                                                                       483523  483523 4992010 /dev/fd/63
                                                                                                                      
                                                                                                                      bash$ grep script /usr/share/dict/linux.words | wc
                                                                                                                          262     262    3601
                                                                                                                      
                                                                                                                      bash$ wc <(grep script /usr/share/dict/linux.words)
                                                                                                                          262     262    3601 /dev/fd/63
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16
                                                                                                                      17

                                                                                                                      Bash用两个文件描述符创建管道,--fIn 和 fOut-- 。trueopen in new window 的标准输入连接 fOut(dup2(fOut, 0)),然后Bash 传递一个 /dev/fd/fIn 参数给 echo 。在不使用 /dev/fd/<n> 的系统里,Bash可以用临时文件(感谢 S.C. 指出这点)。

                                                                                                                      进程替换可以比较两个不同命令的输出,或者同一个命令使用不同选项的输出。

                                                                                                                      bash$ comm <(ls -l) <(ls -al)
                                                                                                                      total 12
                                                                                                                      -rw-rw-r--    1 bozo bozo       78 Mar 10 12:58 File0
                                                                                                                      -rw-rw-r--    1 bozo bozo       42 Mar 10 12:58 File2
                                                                                                                      -rw-rw-r--    1 bozo bozo      103 Mar 10 12:58 t2.sh
                                                                                                                              total 20
                                                                                                                              drwxrwxrwx    2 bozo bozo     4096 Mar 10 18:10 .
                                                                                                                              drwx------   72 bozo bozo     4096 Mar 10 17:58 ..
                                                                                                                              -rw-rw-r--    1 bozo bozo       78 Mar 10 12:58 File0
                                                                                                                              -rw-rw-r--    1 bozo bozo       42 Mar 10 12:58 File2
                                                                                                                              -rw-rw-r--    1 bozo bozo      103 Mar 10 12:58 t2.sh
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11

                                                                                                                      进程替换可以比较两个目录的内容——来检查哪些文件在这个目录而不在那个目录。

                                                                                                                      diff <(ls $first_directory) <(ls $second_directory)
                                                                                                                      
                                                                                                                      1

                                                                                                                      进程替换的一些其他用法:

                                                                                                                      read -a list < <( od -Ad -w24 -t u2 /dev/urandom )
                                                                                                                      #  从 /dev/urandom 读取一个随机数列表
                                                                                                                      #+ 用 "od" 处理
                                                                                                                      #+ 输送到 "read" 的标准输入. . .
                                                                                                                      #  来自 "insertion-sort.bash" 示例脚本。
                                                                                                                      #  致谢:JuanJo Ciarlante。
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      PORT=6881   # bittorrent(BT端口)
                                                                                                                      
                                                                                                                      #  扫描端口,确保没有恶意行为
                                                                                                                      netcat -l $PORT | tee>(md5sum ->mydata-orig.md5) |
                                                                                                                      gzip | tee>(md5sum - | sed 's/-$/mydata.lz2/'>mydata-gz.md5)>mydata.gz
                                                                                                                      
                                                                                                                      #  检查解压缩结果:
                                                                                                                        gzip -d<mydata.gz | md5sum -c mydata-orig.md5)
                                                                                                                      #  对原件的MD5校验用来检查标准输入,并且探测压缩当中出现的问题。
                                                                                                                      
                                                                                                                      #  Bill Davidsen 贡献了这个例子
                                                                                                                      #+ (ABS指南作者做了轻微修改)。
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      cat <(ls -l)
                                                                                                                      # 等价于	ls -l | cat
                                                                                                                      
                                                                                                                      sort -k 9 <(ls -l /bin) <(ls -l /usr/bin) <(ls -l /usr/X11R6/bin)
                                                                                                                      #  列出 3 个主要 'bin' 目录的文件,按照文件名排序。
                                                                                                                      #  注意,有三个(数一下)单独的命令输送给了 'sort'。
                                                                                                                      
                                                                                                                      diff <(command1) <(command2)    # 比较命令输出结果的不同之处。
                                                                                                                      
                                                                                                                      tar cf >(bzip2 -c > file.tar.bz2) $directory_name
                                                                                                                      
                                                                                                                      #  调用 "tar cf /dev/fd/?? $directory_name",然后 "bzip2 -c > file.tar.bz2"。
                                                                                                                      #
                                                                                                                      #  因为 /dev/fd/<n> 系统特性
                                                                                                                      #  不需要在两个命令之间使用管道符
                                                                                                                      #
                                                                                                                      #  这个可以模拟
                                                                                                                      #
                                                                                                                      bzip2 -c < pipe > file.tar.bz2&
                                                                                                                      tar cf pipe $directory_name
                                                                                                                      rm pipe
                                                                                                                      #	或者
                                                                                                                      exec 3>&1
                                                                                                                      tar cf /dev/fd/4 $directory_name 4>&1 >&3 3>&- | bzip2 -c > file.tar.bz2 3>&-
                                                                                                                      exec 3>&-
                                                                                                                      
                                                                                                                      # 致谢:Stéphane Chazelas
                                                                                                                      
                                                                                                                      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

                                                                                                                      在子shell中 echo 命令用管道输送给 while-read 循环open in new window时会出现问题,下面是避免的方法:

                                                                                                                      例23-1 不用 fork 的代码块重定向。

                                                                                                                      #!/bin/bash
                                                                                                                      
                                                                                                                      #  wr-ps.bash: 使用进程替换的 while-read 循环。
                                                                                                                      
                                                                                                                      #  示例由 Tomas Pospisek 贡献。
                                                                                                                      # (ABS指南作者做了大量改动。)
                                                                                                                      
                                                                                                                      echo
                                                                                                                      
                                                                                                                      echo "random input" | while read i
                                                                                                                      do
                                                                                                                        global=3D": Not available outside the loop."
                                                                                                                        # ... 因为在子 shell 中运行。
                                                                                                                      done
                                                                                                                      
                                                                                                                      echo "\$global (从子进程之外) = $global"
                                                                                                                      # $global (从子进程之外) =
                                                                                                                      
                                                                                                                      echo; echo "--"; echo
                                                                                                                      
                                                                                                                      while read i
                                                                                                                      do
                                                                                                                        echo $i
                                                                                                                        global=3D": Available outside the loop."
                                                                                                                        # ... 因为没有在子 shell 中运行。
                                                                                                                      done < <( echo "random input" )
                                                                                                                      #    ^ ^
                                                                                                                      
                                                                                                                      echo "\$global (使用进程替换) = $global"
                                                                                                                      #  随机输入
                                                                                                                      #  $global (使用进程替换)= 3D: Available outside the loop.
                                                                                                                      
                                                                                                                      
                                                                                                                      echo; echo "##########"; echo
                                                                                                                      
                                                                                                                      
                                                                                                                      
                                                                                                                      # 同样道理 . . .
                                                                                                                      
                                                                                                                      declare -a inloop
                                                                                                                      index=0
                                                                                                                      cat $0 | while read line
                                                                                                                      do
                                                                                                                        inloop[$index]="$line"
                                                                                                                        ((index++))
                                                                                                                        # 在子 shell 中运行,所以 ...
                                                                                                                      done
                                                                                                                      echo "OUTPUT = "
                                                                                                                      echo ${inloop[*]}           # ... 什么也没有显示。
                                                                                                                      
                                                                                                                      
                                                                                                                      echo; echo "--"; echo
                                                                                                                      
                                                                                                                      
                                                                                                                      declare -a outloop
                                                                                                                      index=0
                                                                                                                      while read line
                                                                                                                      do
                                                                                                                        outloop[$index]="$line"
                                                                                                                        ((index++))
                                                                                                                        # 没有在子 shell 中运行,所以 ...
                                                                                                                      done < <( cat $0 )
                                                                                                                      echo "OUTPUT = "
                                                                                                                      echo ${outloop[*]}          # ... 整个脚本的结果显示出来。
                                                                                                                      
                                                                                                                      exit $?
                                                                                                                      
                                                                                                                      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
                                                                                                                      66

                                                                                                                      下面是个类似的例子。

                                                                                                                      例 23-2. 重定向进程替换的输出到一个循环内

                                                                                                                      #!/bin/bash
                                                                                                                      # psub.bash
                                                                                                                      #  受 Diego Molina 启发(感谢!)。
                                                                                                                      
                                                                                                                      declare -a array0
                                                                                                                      while read
                                                                                                                      do
                                                                                                                        array0[${#array0[@]}]="$REPLY"
                                                                                                                      done < <( sed -e 's/bash/CRASH-BANG!/' $0 | grep bin | awk '{print $1}' )
                                                                                                                      #  由进程替换来设置'read'默认变量($REPLY)。
                                                                                                                      #+ 然后将变量复制到一个数组。
                                                                                                                      
                                                                                                                      echo "${array0[@]}"
                                                                                                                      
                                                                                                                      exit $?
                                                                                                                      
                                                                                                                      # ====================================== #
                                                                                                                      # 运行结果:
                                                                                                                      bash psub.bash
                                                                                                                      
                                                                                                                      #!/bin/CRASH-BANG! done #!/bin/CRASH-BANG!
                                                                                                                      
                                                                                                                      1
                                                                                                                      2
                                                                                                                      3
                                                                                                                      4
                                                                                                                      5
                                                                                                                      6
                                                                                                                      7
                                                                                                                      8
                                                                                                                      9
                                                                                                                      10
                                                                                                                      11
                                                                                                                      12
                                                                                                                      13
                                                                                                                      14
                                                                                                                      15
                                                                                                                      16
                                                                                                                      17
                                                                                                                      18
                                                                                                                      19
                                                                                                                      20
                                                                                                                      21

                                                                                                                      一个读者发来一个有趣的进程替换例子,如下:

                                                                                                                      # SuSE 发行版中提取的脚本片段:
                                                                                                                      
                                                                                                                      # --------------------------------------------------------------#
                                                                                                                      while read  des what mask iface; do
                                                                                                                      # 一些命令 ...
                                                                                                                      done < <(route -n)  
                                                                                                                      #    ^ ^  第一个 < 是重定向,第二个是进程替换。
                                                                                                                      
                                                                                                                      #  为了测试,我们让它来做点儿事情。
                                                                                                                      while read  des what mask iface; do
                                                                                                                        echo $des $what $mask $iface
                                                                                                                      done < <(route -n)  
                                                                                                                      
                                                                                                                      # 输出内容:
                                                                                                                      # Kernel IP routing table
                                                                                                                      # Destination Gateway Genmask Flags Metric Ref Use Iface
                                                                                                                      # 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
                                                                                                                      # --------------------------------------------------------------#
                                                                                                                      
                                                                                                                      #  正如 Stéphane Chazelas 指出的,
                                                                                                                      #+ 一个更容易理解的等价代码如下:
                                                                                                                      route -n |
                                                                                                                        while read des what mask iface; do   # 通过管道输出设置的变量
                                                                                                                          echo $des $what $mask $iface
                                                                                                                        done  #  这段代码的结果更上面的相同。
                                                                                                                              #  但是,Ulrich Gayer 指出 . . .
                                                                                                                              #+ 这段简化版等价代码在 while 循环里用了子 shell,
                                                                                                                              #+ 因此当管道终止时变量都消失了。
                                                                                                                      
                                                                                                                      # --------------------------------------------------------------#
                                                                                                                      
                                                                                                                      #  然而,Filip Moritz 说上面的两个例子有一个微妙的区别,
                                                                                                                      #+ 见下面的代码
                                                                                                                      
                                                                                                                      (
                                                                                                                      route -n | while read x; do ((y++)); done
                                                                                                                      echo $y # $y is still unset
                                                                                                                      
                                                                                                                      while read x; do ((y++)); done < <(route -n)
                                                                                                                      echo $y # $y has the number of lines of output of route -n
                                                                                                                      )
                                                                                                                      
                                                                                                                      #  更通俗地说(译者注:原文本行少了注释符)
                                                                                                                      (
                                                                                                                      : | x=x
                                                                                                                      # 似乎启动了子 shell ,就像
                                                                                                                      : | ( x=x )
                                                                                                                      # 而
                                                                                                                      x=x < <(:)
                                                                                                                      # 并没有。
                                                                                                                      )
                                                                                                                      #  这个方法在解析 csv 和类似格式时很有用。
                                                                                                                      #  也就是在效果上,原始 SuSE 系统的代码片段就是做这个用的。
                                                                                                                      
                                                                                                                      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

                                                                                                                      注解 [1] 这个与命名管道(使用临时文件)的效果相同,而且事实上,进程替换也曾经用过命名管道。

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