Bash中文件描述符的详细介绍

前言

linux将所有内核对象当做文件来处理,系统用一个size_t类型来表示一个文件对象,比如对于文件描述符0就表示系统的标准输入设备stdin,通常情况下stdin的值为键盘,如read命令就默认从stdin读取数据,当然stdin的值是可以改变的,比如将其改成其他文件,这样的话想read等命令就会默认从相应的文件读取数据了。

简单地说,一个文件描述符可以和一个文件挂钩,一旦挂钩就可以通过取地址运算符&获得该文件的句柄,比如&0就可以获得stdin设备在内存中的句柄(设备在系统中也被当做文件处理),可以这样理解,如果是一个shell中的普通变量var,可以通过$var的形式获得该变量所代表的值,而对于一个文件描述符fd,则可以通过&fd的形式获得文件描述符指向的文件的句柄,而这个句柄可以简单地理解成该文件的路径。

在 shell 编程里经常会用到重定向操作, 它本质上是对文件描述符进行操作, 本文会对 shell 脚本里的文件描述符做一个详细的介绍.

默认标准文件描述符

每个进程启动时默认都会有三个标准的文件描述符:

  • stdin 0 号描述符, 代表输入设备, 进程从它读入数据;
  • stdout 1 号描述符, 进程往其中写入数据;
  • stderr 2 号描述符, 进程会往其中写入错误信息;

这三个描述符默认是对应同一个 tty 设备, 这样我们便可以在终端中输入数据和获取进程的输出.

默认的文件描述符也是可以被替换的, 例如我们可以替换掉 stdout 到一个文件, 这样命令的输出就不是打印到终端, 而是被输出到文件中:

Bash中文件描述符的详细介绍

在上面的 demo 中, 我们先是通过 exec 1 > /tmp/stdout 把 stdout 指向了文件 /tmp/stdout, 紧接着我们执行了两条命令 ls 和 pwd, 可以看到此时终端已经没有了命令的输出. 当我们通过 exec 1 >&2 恢复 stdout 后, 可以发现文件 /tmp/stdout 里存储了之前命令的输出.

其中 exec 是一个 bash 内置命令, 不同于在终端中执行命令时会 fork 一个子进程, 通过 exec 执行的命令会直接修改当前的 shell 进程, 可以通过它执行命令来修改当前 shell 的 context.

如果你想使坏的话可以在别人的 ~/.bashrc 里加入 exec 1 > /tmp/stdout, 这样新开的所有的终端窗口里都看不到命令的输出, 要是因此被打概不负责 :) .

文件描述符的操作

shell 中对文件描述符的操作由三部分组成: (left, operation, right):

  • left 可以是 0-9 的数字, 代表第 n 号文件描述符;
         left 还可以为 &, 表示同时操作 stdout 和 stderr
  • right 可以是文件名或 0-9 的数字, 当 right 是数字时必须要加上 & 符号, 表示引用第 n 号文件描述符;
         right 还可以为 &-, 此时表示关闭 left 描述符, 例如 2<&- 表示关闭 stderr;
  • operation 可以为 < 或 >;
         为 < 时表示以读模式复制 right 到 left, 此时如果没有指定 left 的话, 则为默认值 0;
         当为 > 表示以写模式复制 right 到 left, 此时如果没有指定 left 的话, 则为默认值 1;
         operation 和 left 之间不能有空格;
         当 right 为文件名时, operation 和 right 可以有空格, 否则也不能有空格;

当存在多个文件描述符的操作时, 会按照从左往右的顺序依次执行. 例如通过命令 cmd 3>&1 1>&2 2>&3 3>&- 就可以交换 stdin 和 stdout.

我们通过下面的例子来验证上面的文件描述符交换是否生效:

  • 首先把默认的 stderr 重定向到文件 /tmp/stderr 中, 这样在终端中就不会看到错误输出了;
  • 当交换完 stderr 和 stdout 后, 我们就可以在 /tmp/stderr 文件中看到命令的正常输出了;

让我们来开始实验吧:

?

test exec 2> /tmp/stderr

test ls

a.txt

test ls 3>&1 1>&2 2>&3 3>&-

test cat /tmp/stderr

a.txt

和我们的预期时一致的!

一些示例

用文件重载 stdin :

?

test cat 0< a.txt

hello

test cat < a.txt # same with last command

hello

把 stderr 和 stdout 都过滤掉

?

ls not_exist 1> /dev/zero 2>&1

# another way

ls not_exist &> /dev/zero

处理上一个命令的错误输出:

?

➜ blog git:(hexo) ls not_exist 2>&1 | sed 's/not_exist/error/g'

ls: error: no such file or directory

# another way

➜ blog git:(hexo) ls not_exist |& sed 's/not_exist/error/g'

ls: error: no such file or directory

把标准输出转入到错误输出上: echo hello 1>&2

process substitution

在 bash 中提供了两个特殊的操作, 它们都可以被直接当成文件名使用:

  • <(cmd) : 可以看作时一个可读文件, cmd 命令的输出是这个文件的内容;
  • >(cmd) : 可以看作时一个可写文件, cmd 会接受输入并进行处理;

示例

利用 <(cmd) 来验证一对公私钥是否匹配:

?

➜ blog git:(hexo) diff <(ssh-keygen -y -e -f ~/.ssh/id_rsa) <(ssh-keygen -y -e -f ~/.ssh/id_rsa.pub)

➜ blog git:(hexo)

利用 >(cmd) 来对错误信息进行处理, 同时保证错 stderr 信息不回变成 stdout:

?

➜ blog git:(hexo) ls not_exist 2> >(sed 's/not_exist/keep_error/g')

ls: keep_error: no such file or directory

➜ blog git:(hexo)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

references

  • file descriptors in bourne shell
  • process substitution

原文链接:https://hiberabyss.github.io/2018/04/05/shell-file-description/

原创文章,作者:JOCEB,如若转载,请注明出处:http://www.wangzhanshi.com/n/6339.html

(0)
JOCEB的头像JOCEB
上一篇 2025年1月1日 16:20:40
下一篇 2025年1月1日 16:20:43

相关推荐

  • 浅谈linux中sed命令和awk命令的使用

    本文主要研究的是linux中sed命令和awk命令的使用的相关内容,具体如下。 1、sed命令:没有重定向不会真正修改源文件中的内容 查询语句 ①sed -n ‘/sbin/p&#…

    Linux 2025年1月1日
  • 使用 LVM 添加存储

    LVM 为你配置存储的方式提供了极大的灵活性。 逻辑卷管理器Logical Volume Manager(LVM)允许在操作系统和硬件之间建立一个抽象层。通常,你的操作系统会查找磁…

    2025年1月1日
  • Linux中logrotate日志轮询操作总结

    前言 对于Linux系统安全来说,日志文件是极其重要的工具。不知为何,我发现很多运维同学的服务器上都运行着一些诸如每天切分Nginx日志之类的CRON脚本,大家似乎遗忘了Logro…

    Linux 2025年1月1日
  • Ubuntu无网络连接及标识的解决方法

    一、Bug描述 这是之前安装虚拟机时遇到的一个问题,放在草稿好久了,今天终于有空完善一下发布了。 大家输入ifconfig的时候肯定是只能找到本地环回. 二、可能的报错原因 原因一…

    Linux 2024年12月17日
  • 详解Centos/Linux下调整分区大小(以home和根分区为例)

    在安装新系统的时候,有时候没法预估或者说错误的划分了分区大小,常常会导致我们后面的操作出现极大地不方便,比如某个分区分的太小了,导致软件安装的时候会报安装空间不够,这就很麻烦。在这…

    2025年1月1日
  • Linux服务器磁盘空间占用情况分析与清理指南(解决方法)

    为确保重大节日期间,团队负责的测试环境服务器磁盘不会占用过高,导致频繁报警。我们要求在重大节假日前对服务器磁盘占用情况进行检查。如果发现占用过高,则需人为介入,进行相应清理。 一、…

    Linux 2024年12月17日
  • Linux中7个判断文件系统类型的方法

    文件通过文件系统在磁盘及分区上命名、存储、检索以及更新,文件系统是在磁盘上组织文件的方式。 文件系统分为两个部分:用户数据和元数据(文件名、创建时间、修改时间、大小以及目录层次结构…

    2025年1月1日
  • linux操作系统原理 linux系统基础教程

    本篇文章系统的给大家讲述linux操作系统原理,这是一篇非常好的linux系统基础教程,我们总结了相关的全部精选内容,一起来学习下。 linux操作系统原理文字版 一.计算机经历的…

    2025年1月1日
  • 浅谈linux线程切换问题

    处理器总处于以下状态中的一种: 1、内核态,运行于进程上下文,内核代表进程运行于内核空间; 2、内核态,运行于中断上下文,内核代表硬件运行于内核空间; 3、用户态,运行于用户空间;…

    Linux 2025年1月1日
  • 详解Linux下挂载新硬盘方法

    Linux的硬盘识别: 一般使用”fdisk -l”命令可以列出系统中当前连接的硬盘 设备和分区信息.新硬盘没有分区信息,则只显示硬盘大小信息. 1.关闭服务器加上新硬盘 2.启动…

    Linux 2025年1月1日

发表回复

登录后才能评论