Linux:八种重定向详解(万字长文警告)

瑞星  金牌会员 | 2024-10-18 11:32:27 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 802|帖子 802|积分 2406

相干阅读Linux
https://blog.csdn.net/weixin_45791458/category_12234591.html?spm=1001.2014.3001.5482

        本文将讨论Linux中的重定向相干问题,在阅读本文前,强烈发起先学习文件描述符的相干内容Linux:文件描述符详解。
         重定向分为两类:第一类是全局重定向,它对后续在Bash中创建的全部子历程都见效(因为文件描述符的继承);第二类是下令重定向,它只对单个下令见效。
全局重定向

输入重定向

  1. exec [number|varname]< filename
  2. # 如果不使用exec,则无法影响当前Bash进程
  3. # [number|varname]和<之间不能有空格
复制代码
        输入重定向会起首将文件名举行拓展(如参数扩展和下令替换),如果利用number,会在文件描述符number上以读模式打开该文件(实际上,系统调用open打开一个文件返回的文件描述符是最小可用的整数值,这是不能直接控制的,但可以用系统调用dup、dup2或fcntl举行复制);如果利用varname,则会自动分配一个大于等于10的文件描述符并将其赋值给变量varname;如果未指定number或varname,则默认为标准输入(文件描述符0)。
        例1展示了指定数字分配文件描述符和自动分配文件描述符的过程。
  1. 例1
  2. [zhangchen@EDA Desktop]$ ps
  3.    PID TTY          TIME CMD
  4. 34005 pts/0    00:00:00 bash
  5. 34070 pts/0    00:00:00 ps
  6. [zhangchen@EDA Desktop]$ ls -al /proc/34005/fd # 查询bash进程的文件描述符
  7. total 0
  8. dr-x------. 2 zhangchen zhangchen  0 Sep 24 14:08 .
  9. dr-xr-xr-x. 9 zhangchen zhangchen  0 Sep 24 14:08 ..
  10. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 0 -> /dev/pts/0
  11. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 1 -> /dev/pts/0
  12. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 2 -> /dev/pts/0
  13. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:09 255 -> /dev/pts/0
  14. [zhangchen@EDA Desktop]$ exec 5< test1 # 指定数字分配文件描述符
  15. [zhangchen@EDA Desktop]$ ls -al /proc/34005/fd
  16. total 0
  17. dr-x------. 2 zhangchen zhangchen  0 Sep 24 14:08 .
  18. dr-xr-xr-x. 9 zhangchen zhangchen  0 Sep 24 14:08 ..
  19. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 0 -> /dev/pts/0
  20. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 1 -> /dev/pts/0
  21. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 2 -> /dev/pts/0
  22. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:09 255 -> /dev/pts/0
  23. l-wx------. 1 zhangchen zhangchen 64 Sep 24 14:08 5 -> /home/zhangchen/Desktop/test1
  24. [zhangchen@EDA Desktop]$ exec {fd}< test2 # 自动分配文件描述符
  25. [zhangchen@EDA Desktop]$ echo $fd
  26. 10
  27. [zhangchen@EDA Desktop]$ ls -al /proc/34005/fd
  28. total 0
  29. dr-x------. 2 zhangchen zhangchen  0 Sep 24 14:08 .
  30. dr-xr-xr-x. 9 zhangchen zhangchen  0 Sep 24 14:08 ..
  31. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 0 -> /dev/pts/0
  32. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 1 -> /dev/pts/0
  33. l-wx------. 1 zhangchen zhangchen 64 Sep 24 14:08 10 -> /home/zhangchen/Desktop/test2
  34. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:08 2 -> /dev/pts/0
  35. lrwx------. 1 zhangchen zhangchen 64 Sep 24 14:09 255 -> /dev/pts/0
  36. l-wx------. 1 zhangchen zhangchen 64 Sep 24 14:08 5 -> /home/zhangchen/Desktop/test1
复制代码
        例2展示了输入重定向过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。
  1. # 例2
  2. # 文件:redirection.sh
  3. exec 5< test1
  4. exec {fd}< test2
  5. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  6. *****
  7. open("test1", O_RDONLY)                 = 3
  8. fcntl(5, F_GETFD)                      = -1 EBADF (Bad file descriptor)
  9. dup2(3, 5)                              = 5
  10. close(3)                                = 0
  11. rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
  12. open("test2", O_RDONLY)                 = 3
  13. fcntl(3, F_DUPFD, 10)                   = 10
  14. close(3)
  15. *****
复制代码
        可以看出,在系统调用open打开文件test1和test2时,返回的文件描述符都是其实3,但是系统调用dup2(3, 5)和fcntl(3, F_DUPFD, 10)将其复制为了文件描述符5和10。系统调用open的参数O_RDONLY表示以只读模式打开。
        如果在交互式bash中,将标准输入重定向为了其他文件,则终端会直接退出,如例3所示。
  1. # 例3
  2. [zhangchen@EDA Desktop]$ exec 0< test         # 终端会直接退出
  3. [zhangchen@EDA Desktop]$ exec 0< test; sleep 5 # 终端会在等待5秒后退出
复制代码
        下面的下令可以关闭已打开的文件描述符,如例4所示。
  1. exec [number|varname]<& -
  2. exec [number|varname]>& -
  3. # 两种命令效果是一样的,并不区分输入还是输出
  4. # 它们唯一的区别在于,不指定number或varname时
  5. # <&-默认指的是关闭标准输入描述符,即0<&-;>&-默认指的是关闭标准输出描述符,即1>&-
  6. # [number|varname]和<、>和&之间不能有空格,和-间可以有
复制代码
  1. # 例4
  2. [zhangchen@EDA Desktop]$ exec 5<& -     # 关闭文件描述符5
  3. [zhangchen@EDA Desktop]$ exec {fd}>& -  # fd的值为10,因此关闭文件描述符10
复制代码


输出重定向

  1. exec [number|varname]>[|] filename
  2. # 如果不使用exec,则无法影响当前Bash进程
  3. # [number|varname]和>h和[|]之间不能有空格
复制代码
        输出重定向会起首将文件名举行拓展(如参数扩展和下令替换),如果利用number,会在文件描述符number上以写模式打开该文件,如果文件不存在则会创建它;如果利用varname,则会自动分配一个大于等于10的文件描述符并将其数字赋值给变量varname;如果未指定number或varname,则默认为标准输出(文件描述符1)。
        如果重定向操作符是>,并且启用了noclobber选项(通过下令set -o noclobber启用),则如果目标文件已经存在且是常规文件,则重定向会失败。
        如果重定向操作符是>|,或者利用>但未启用noclobber选项,则如果目标文件已经存在,会将其截断为零字节。
        例5展示了输出重定向过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。
  1. # 例5
  2. # 文件:redirection.sh
  3. set -o noclobber
  4. exec 5> test1    # 重定向失败
  5. exec {fd}>| test2
  6. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  7. *****
  8. write(2, "redirection.sh: line 2: test1: c"..., 62redirection.sh: line 2: test1: cannot overwrite existing file
  9. ) = 62
  10. open("test2", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  11. fcntl(3, F_DUPFD, 10)                   = 10
  12. close(3)
  13. *****
复制代码
        系统调用open的参数O_WDONLY表示以只写模式打开,参数O_CREAT表示如果文件不存在,则创建它,参数O_TRUNC表示如果文件已存在,则将其内容截断为零字节。
        例6展示了输出重定向后,后续下令的输出都不出如今终端。
  1. # 例6
  2. [zhangchen@EDA Desktop]$ exec > test1         # 标准输出(文件描述符1)重定向到文件test1
  3. [zhangchen@EDA Desktop]$ echo 123456789       # 向标准输出(文件描述符1)输出文本
  4. [zhangchen@EDA Desktop]$ exec >&-             # 关闭标准输出(文件描述符1)
  5. [zhangchen@EDA Desktop]$ cat test1            # 向标准输出(文件描述符1)输出文本
  6. cat: standard output: Bad file descriptor     # 标准输出(文件描述符1)已被关闭
  7. [zhangchen@EDA Desktop]$ exec > /dev/pts/0    # 重新让标准输出(文件描述符1)指向终端
  8. [zhangchen@EDA Desktop]$ cat test1            # 向标准输出(文件描述符1)输出文本
  9. 123456789                                     # 成功输出文本
复制代码
        例6中还存在一个插曲,当将标准输出重定向到test1并写入文本12345678后,利用exec >&-关闭文件描述符,此时如果尝试利用cat输出test1文件的内容会报错,这是因为此时标准输出,即文件描述符0并未指向有效的终端,因此需要将标准输出重定向回终端,此时才气正常体现。

输出追加重定向

  1. exec [number|varname]>> filename
  2. # 如果不使用exec,则无法影响当前Bash进程
  3. # [number|varname]和>>之间不能有空格
复制代码
        与输出重定向雷同,不同的是,如果文件已经存在,则新的输出将附加到文件末尾,而不会清空原有内容。
        例7展示了输出追加重定向过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。
  1. # 例7
  2. # 文件:redirection.sh
  3. exec 5>> test1
  4. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  5. *****
  6. open("test1", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
  7. fcntl(5, F_GETFD)                       = -1 EBADF (Bad file descriptor)
  8. dup2(3, 5)                              = 5
  9. close(3)                                = 0
  10. rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
  11. *****
复制代码
        系统调用open的参数O_WDONLY表示以只写模式打开,参数O_CREAT表示如果文件不存在,则创建它,参数O_APPEND表示在写入数据时,全部数据都会追加到文件的末尾,而不是从文件开头开始写入。
        例8展示了输出追加重定向后,输出的内容是追加到文件末尾的。
  1. # 例8
  2. [zhangchen@EDA Desktop]$ cat test1
  3. 123456789
  4. [zhangchen@EDA Desktop]$ exec >> test1        # 标准输出(文件描述符1)重定向到文件test1
  5. [zhangchen@EDA Desktop]$ echo 123456789       # 向标准输出(文件描述符1)输出文本
  6. [zhangchen@EDA Desktop]$ exec >&-             # 关闭标准输出(文件描述符1)
  7. [zhangchen@EDA Desktop]$ exec > /dev/pts/0    # 重新让标准输出(文件描述符1)指向终端
  8. [zhangchen@EDA Desktop]$ cat test1            # 向标准输出(文件描述符1)输出文本
  9. 123456789                                     # 追加输出
  10. 123456789
复制代码


标准输出和标准错误重定向

  1. exec &> filename # 更好的格式
  2. exec >& filename
  3. # 如果不使用exec,则无法影响当前Bash进程
  4. # &和>之间不能有空格
复制代码
        标准输出和标准错误重定向会起首将文件名举行拓展(如参数扩展和下令替换),然后将标准输出(文件描述符1)和标准错误输出(文件描述符2)都重定向到同一个文件。
        需要注意的是,利用>&时文件不能扩展为number或-,如果是,则会被看成是复制文件描述符(见后),这是为了兼容性思量。 
        例9展示了标准输出和标准错误重定向过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。
  1. # 例9
  2. # 文件:redirection.sh
  3. exec &> test1
  4. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  5. *****
  6. open("test1", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  7. dup2(3, 1)                              = 1
  8. close(3)                                = 0
  9. dup2(1, 2)                              = 2
  10. *****
复制代码
        在系统调用open打开文件test1时,返回的文件描述符是3,而dup2(3, 1)将其复制为了文件描述符1,进一步dup2(1, 2)将文件描述符1复制为了文件描述符2,此时文件描述符1和2都指向test1文件。
        例10展示了标准输出和标准错误重定向后,后续下令的输出不出如今终端。
  1. # 例10
  2. # 文件:redirection.sh
  3. exec &> test1
  4. echo "This is standard output"            # 标准输出
  5. ls non_existent_file                      # 这将导致错误,产生标准错误
  6. echo "This will also be standard output"  # 另一个标准输出
  7. [zhangchen@EDA Desktop]$ bash redirection.sh
  8. [zhangchen@EDA Desktop]$ cat test
  9. This is standard output
  10. ls: cannot access non_existent_file: No such file or directory
  11. This will also be standard output
复制代码


标准输出和标准错误追加重定向

  1. exec &>> filename
  2. # 如果不使用exec,则无法影响当前Bash进程
  3. # &和>>之间不能有空格
复制代码
        与标准输出和标准错误重定向雷同,不同的是,如果文件已经存在,则新的输出将附加到文件末尾,而不会清空原有内容。 
        例11展示了标准输出和标准错误追加重定向过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。 
  1. # 例11
  2. # 文件:redirection.sh
  3. exec &>> test1
  4. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  5. *****
  6. open("test1", O_WRONLY|O_CREAT|O_APPEND, 0666) = 3
  7. dup2(3, 1)                              = 1
  8. close(3)                                = 0
  9. dup2(1, 2)                              = 2
  10. *****
复制代码
        在系统调用open打开文件test1时,返回的文件描述符是3,而dup2(3, 1)将其复制为了文件描述符1,进一步dup2(1, 2)将文件描述符1复制为了文件描述符2,此时文件描述符1和2都指向test1文件。 
        例12展示了标准输出和标准错误追加重定向后,输出的内容是追加到文件末尾的。 
  1. # 例12
  2. # 文件:redirection.sh
  3. exec &> test1
  4. echo "This is standard output"            # 标准输出
  5. ls non_existent_file                      # 这将导致错误,产生标准错误
  6. echo "This will also be standard output"  # 另一个标准输出
  7. [zhangchen@EDA Desktop]$ cat test
  8. 12345678
  9. [zhangchen@EDA Desktop]$ bash redirection.sh
  10. [zhangchen@EDA Desktop]$ cat test
  11. 12345678
  12. This is standard output
  13. ls: cannot access non_existent_file: No such file or directory
  14. This will also be standard output
复制代码

复制文件标识符

  1. exec [number|varname]<& filename
  2. exec [number|varname]>& filename
  3. # 两种命令效果是一样的,并不区分输入还是输出,,但建议与被复制(关闭)的文件描述符方向一致
  4. # 它们唯一的区别在于,不指定number或varname时
  5. # <& filename默认指的是被复制(关闭)的标准输入描述符,即0<& filename;>& filename默认指的是被复制(关闭)的标准输出描述符,即1>& filename
  6. # [number|varname]和<、>和&之间不能有空格
复制代码


  • 如果filename睁开为数字,则其表示要被复制的文件描述符,那么文件描述符number(指定)或varname(自动分配)将成为该文件描述符的副本,即指向同一个文件。
  • 如果filename睁开为-,则其表示要关闭文件描述符。
  • 如果filename睁开文件名,则其表示标准输出和标准错误重定向。
  • 其他情况下,会出现错误。
        例13展示了复制文件标识符过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。 
  1. # 例13
  2. # 文件:redirection.sh
  3. exec 3> test1
  4. exec 4>& 3
  5. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  6. *****
  7. open("test1", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  8. rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
  9. fcntl(4, F_GETFD)                       = -1 EBADF (Bad file descriptor)
  10. dup2(3, 4)                              = 4
  11. *****
复制代码
        在系统调用open打开文件test1时,返回的文件描述符是3,此时不再需要系统调用dup2调整了,因为这就是我们指定的数字,而dup2(3, 4)将其复制为了文件描述符4,此时文件描述符3和4都指向test1文件。


移动文件描述符

  1. exec [number|varname]<& number-
  2. exec [number|varname]>& number-
  3. # 两种命令效果是一样的,并不区分输入还是输出,但建议与被移动的文件描述符方向一致
  4. # 它们唯一的区别在于,不指定number或varname时
  5. # <& number-默认指的是移动到标准输入描述符,即0<& number-;>& number-默认指的是标准输出描述符,即1>& number-
  6. # [number|varname]和<、>和&之间不能有空格,number和-之间不能有空格,&与和number之间可以有空格
复制代码
        该下令将文件描述符number复制为文件描述符number(指定)或varname(自动分配),并在今后关闭文件描述符number。
        例14展示了移动文件描述符过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。 
  1. # 例14
  2. # 文件:redirection.sh
  3. exec 3> test1
  4. exec 4>& 3-
  5. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  6. *****
  7. open("test1", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  8. rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
  9. fcntl(4, F_GETFD)                       = -1 EBADF (Bad file descriptor)
  10. fcntl(4, F_GETFD)                       = -1 EBADF (Bad file descriptor)
  11. dup2(3, 4)                              = 4
  12. fcntl(3, F_GETFD)                       = 0
  13. close(3)                                = 0
  14. *****
复制代码
        可以看到,在系统调用dup2将文件描述符3赋值为了文件描述符4后,紧接着就是用系统调用close关闭了文件描述符3。

输入和输出重定向

  1. exec [number|varname]<> filename
  2. # 如果不使用exec,则无法影响当前Bash进程
  3. # [number|varname]和<和>之间不能有空格
复制代码
        以读写方式打开一个文件,是用文件描述符number(指定)或varname(自动分配),如果未指定number或varname,则默认为标准输入(文件描述符0)。 
        例15展示了移动文件描述符过程中的系统调用,由于strace无法直接在交互式bash中追踪内建下令exec,我们选择编写一个测试脚本。  
  1. # 例15
  2. # 文件:redirection.sh
  3. exec 3<> test1
  4. [zhangchen@EDA Desktop]$ strace bash redirection.sh
  5. *****
  6. open("test1", O_RDWR|O_CREAT, 0666)     = 3
  7. *****
复制代码
        系统调用open的参数O_RDWR表示以读写模式打开,参数O_CREAT表示如果文件不存在,则创建它。

叠加利用

        上面的八种重定向下令,可以多个混淆利用,此时从左往右依次执行,如下所示。
  1. exec &> filename  等价于  exec > filename 2>& 1或exec > filename 2<& 1(不建议)
  2. exec &>> filename 等价于  exec > filename 2>>& 1或exec > filename 2<<& 1(不建议)
复制代码

下令重定向

        全局重定向的影响范围很广,全部后续的下令都会受到影响(因为文件描述符继承),但下令重定向只针对一条下令而言,重定向只发生在子历程中,其他都是相同的。
        下面给了一个例子,将echo下令的标准输出(文件描述符1)重定向至文件test1。
  1. [zhangchen@EDA Desktop]$ echo test_message > test1
  2. [zhangchen@EDA Desktop]$ cat test1
  3. test_message
复制代码
        下面展示了下令重定向过程中的系统调用情况。
  1. # 文件:redirection.sh
  2. ls > test1
  3. echo 111111111111111111111
  4. [zhangchen@EDA Desktop]$ strace -f bash redirection.sh # -f选项很重要,加上它才能追踪子进程的系统调用
  5. *****
  6. [pid 71192] open("test1", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  7. [pid 71192] dup2(3, 1)                  = 1
  8. [pid 71192] close(3)                    = 0
  9. [pid 71192] execve("/usr/bin/ls", ["ls"], 0x12f4bb0 /* 66 vars */) = 0
  10. *****
  11. write(1, "111111111111111111111\n", 22111111111111111111111 # 后面一串是命令的输出
  12. ) = 22
  13. *****
复制代码
        可以看到,系统调用open是在子历程[pid 71192]中举行的,echo的输出利用原本的标准输出(文件描述符1),即还是原来的终端,所以在后面看到了下令的输出。
        仔细的人大概会发现,echo下令并没有创建子历程,虽然echo下令不是内建下令,但它是直接在原bash历程中执行的,如果重定向它的标准输入输出,岂不是会影响全局?其实不会,下面举例说明。
  1. # 文件:redirection.sh
  2. echo 111111111111111111111 > test1
  3. echo 222222222222222222222
  4. [zhangchen@EDA Desktop]$ strace -f bash redirection.sh # -f选项很重要,加上它才能追踪子进程的系统调用
  5. open("test1", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
  6. fcntl(1, F_GETFD)                       = 0
  7. fcntl(1, F_DUPFD, 10)                   = 10
  8. fcntl(1, F_GETFD)                       = 0
  9. fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
  10. dup2(3, 1)                              = 1
  11. close(3)                                = 0
  12. fstat(1, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
  13. mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6e4f095000
  14. write(1, "111111111111111111111\n", 22) = 22
  15. dup2(10, 1)                             = 1
  16. fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
  17. close(10)                               = 0
  18. rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
  19. write(1, "222222222222222222222\n", 22222222222222222222222
  20. ) = 22
  21. *****
复制代码
        原来在利用dup2函数将标准输出(文件描述符1)指向test1前,先利用了fcntl(1, F_DUPFD, 10)将文件描述符1原本指向的终端保存了下来,第一个echo执行后,又利用了dup2(10, 1) 将其还原!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

瑞星

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表