Bash: file descriptor & exec

 

 

彻底搞懂shell的高级I/O重定向 - 骏马金龙 - 博客园 (cnblogs.com)

  1. 软件设计认为,程序应该有一个数据来源、数据出口和报告错误的地方。在Linux系统中,它们分别使用描述符0、1、2来表示,这3个描述符默认的目标文件(设备)分别是/dev/stdin、/dev/stdout、/dev/stderr,它们分别是各个终端字符设备的软链接。

    bash内部的exec命令允许我们操作文件描述符,如果在exec之后没有指定命令,则exec命令之后的重定向将更改当前shell的fd


     

  2. exec创建一个只读fd
    也可连接至 pipe socket
    exec 4< zc

     读完一次数据后,再读为空

    能读取追加的新内容

    其内部维护了文件指针的数据结构

    上面我们是省略写法,默认输入输入重定向的fd为0,因此

    cat <&4 == cat 0<&4


    cat 默认读取文件,可以改写其stdin

    我们可以直接覆盖原有fd,相当于删除,新建

    上面创建的是 r-x权限的 fd,向其写数据会发生什么?

    stderr没有发生写错误,原因尚不清楚

  3. exec 创建只写fd, 如果文件有内容会清空

    exec 4> zc

    读数据发生错误

    写数据没有问题

    fd没有 >> 追加操作, 只有文件有追加, fd内部有指针, 默认效果就是追加 

     

  4. 创建可读可写fd, 不会清空文件
    exec {fd#}<> file  # 从10开始自动分配
    exec 3<> file

    文件指针在最开始 

  5. 关闭fd
    exec fd<&-
    or
    exec fd>&-

     

     

    关闭当前shell 的stdout后, 遇到输出到stdout的产生错误

     

  6. 将当前shell stdout 重定向至 zc文件
    exec 1> zc

    将当前shell stdout 重定向至 zc文件 (慎用)

    exec 2> zc

     

  7. 转移fd,会关闭原fd, 与关闭一样 < > 都可以

    exec 3<> v
    exec 4<&3-
    exec 5>&4-

    复制的时候并不改变原fd rwx属性, 不能使用<>

  8. 复制fd, 比move 少了-

     

     

 

Example:

  1. exec {fd}<> file

     

     

     

  2. 利用变量关闭fd
    eval "exec $fd>&-"

    此处必须使用eval

  3. 利用fd读取文件
    #!/bin/env bash
    
    if [ $# -ne 1 -o ! -f "$1" ];then
            echo "Usage: `basename $0` <file>"
            exit 6
    fi
    
    exec {fd}<$1
    head -n+3 <&$fd eval "exec ${fd1}<&-"

     

  4. #!/bin/sh
    exec {fd}> mmm
    cat mmm
    echo uoiopp >&$fd
    eval "exec $fd<&-"

     

  5. 重定向脚本所有输出
    #!/bin/bash
    
    : redirect all output to file
    exec {fd}>&1  # duplicate stdout
    exec 1> mmm 2>&1
    df
    date +%c
    eval "exec 1>&$fd $fd<&-"

     

  6. 生成测试文件

     

     测试脚本

    #!/bin/bash
    
    if [ $# -lt 1 ];then
            echo "Usage:`basename $0` file"
            exit 5
    fi
    
    while read -r line;do
            echo $line
            read -p $'Press any key continue...\n' -n 1
    done < $1

     

     此时没有执行read命令,将指定的文件作为while循环的标准输入,read也继承了这个fd(标准输入)

    因此,read从重定向后的标准输入读取,而不是从默认的标准输入(键盘)读取


    改进思路,while后的read -u 从文件描述符中读取,命令中的read依旧从stdin读取

    #!/bin/bash
    
    if [ $# -lt 1 ];then
            echo "Usage:`basename $0` file"
            exit 5
    fi
    
    exec {fd}< $1
    
    echo $fd
    ls -l /proc/self/fd
    
    while read -u $fd line;do
            echo $line
            read -p $'Press any key continue...\n' -n 1
    done

     

     
    可以看到fd 11 的权限为 r-x

  7. #!/bin/bash
    
    exec 3< /etc/resolv.conf
    exec 4> mm
    
    ls -l /proc/self/fd
    
    read -u 3 a b
    echo $a $b
    
    echo uiop $a >&4
    echo vbnm $b >&4
    
    exec 3<&-
    exec 4>&-
    
    ls -l /proc/self/fd

     

  8. /dev/tcp/host/port
    {
      printf >&3 'GET / HTTP/1.0\r\n\r\n'
      cat 0<&3
    } 3<> /dev/tcp/baidu.com/80
    exec 3> /dev/tcp/x.com/80
    echo -e 'GET / HTTP/1.0\r\n\r\n' >&3
    cat <&3

     

     




 

posted @ 2020-12-10 16:26  ascertain  阅读(430)  评论(0编辑  收藏  举报