Linux 命令行与 shell 脚本编程大全 15 呈现数据

介绍如何将脚本的输出重定向到 Linux 的不同位置

更多精彩

导览

  1. 脚本的输入、输出都可以从 STDIN( 标准输入 )STDOUT( 标准输出 ) 重定向到任意文件中
  2. 除了 STDOUT 作为标准输出,还有 STDERR 作为标准错误输出来区分脚本中的正常消息和错误消息
  3. shell 从 0 - 8 一共提供 9 个文件描述符
    • 0 - 2 做为内置的文件描述符,分别指向 STDINSTDOUT 以及 STDERR
    • 3-8 则是支持自定义的文件描述符
  4. 使用 lsof 命令可以查看系统中的文件描述符使用情况
    • -p 可以过滤进程 ID
    • -d 可以过滤文件描述符
    • -a 可以合并前面两个条件的查询结果
  5. Linux 提供 /dev/null 作为一个特殊文件,用于重定向不需要的输出
    • 任何重定向到该文件的内容都会被直接丢弃
    • 也可以通过将该文件重定向到指定文件,来实现直接清空指定文件的内容
  6. 使用 mktemp 可以创建临时文件,并返回文件名
    • mktemp test.XXXXXX6 个 X 是系统用于生成 6 位随机字符串而需要使用的占位符
    • -t 可以实现生成的文件直接位于 /tmp 目录
    • -d 可以生成目录
  7. 使用 tee 命令可以将输出同时发送到 STDOUT 和指定的文件中
    • -a 可以实现内容的追加

15.1 理解输入和输出

15.1.1 标准文件描述符

  1. Linux 用 文件描述符( File Descriptor ) 来标识每个文件对象
    1. 文件描述符 是一个非负整数,可以唯一标识会话中打开的文件
  2. 每个会话一次最多可以有 9 个文件描述符,从 0 到 8
  3. 系统保留了 0 、1 、2 作为内置的文件描述符,分别用来标识如下三种类型


15.1.1.1 标准输入 STDIN

  1. STDIN 文件描述符标识 shell 的 标准输入
  2. 对于终端来说,标准输入的设备一般是键盘,但对于系统来说,例如在输入时使用 重定向符号( < ) ,从脚本获取内容并输入,也被认为是标准输入

15.1.1.2 标准输出 STDOUT

  1. STDOUT 文件描述符标识 shell 的 标准输出
  2. 对于终端来说,标准输出的目标就是显示器,一般来说,大部分命令的输出都会指向 STDOUT ,也就是显示器,只有这样,在执行完命令后,才能立即看到执行结果
  3. 使用 输出重定向( > ) 则可以改变这个效果,如下图
    • ls 命令默认输出的方式就是 STDOUT
    • 但使用输出重定向将命令的输出结果指向一个文件后,命令的输出结果就没有直接输出到显示器中
    • 使用 cat 命令查看该文件,则可以看到刚才 ls 命令的输出结果

15.1.1.3 标准错误 STDERR

  1. STDERR 文件描述符标识 shell 的 标准错误输出
  2. 我们再执行某个命令时,如果抛出了错误,一般也是直接显示在屏幕上,但这不属于标准输出,而是标准错误输出
  3. shell 对于错误消息的处理和普通输出是分开的 ,如下图
    • 想要查看一个不存在的目录的文件列表,并将显示结果输出到指定文件中
    • shell 直接将目录不存在的错误信息进行了显示
    • 用于接收命令结果的文件生成了,但文件中没有任何内容


  4. 默认情况下,STDERRSTDOUT 指向相同的地方,也就是显示器
    • 但两者不会随着另一个指向的变化而发生变化,从上一个例子中就可以看到

15.1.2 重定向错误

15.1.2.1 只重定向错误

  1. STDERR 的文件描述符是 2 ,所以在执行命令时,可以通过输出重定向将错误输出指向特定的文件,如下图
    • 需要注意的是,2 后面必须紧跟输出重定向符号,两者直接不能有空格
  2. 那么如果输出内容中既有 STDOUT ,也有 STDERR ,会是什么结果呢?如下图
    • 可以看到,使用 ls 命令一次性查看了三个目录的内容列表
    • 第一个目录是不存在的,但因为使用 2>STDERR 输出到了指定文件,所以错误消息没有直接显示
    • 第二和第三个目录是真实存在,所以都正常输出了显示结果


15.1.2.2 重定向错误和数据

  1. 要在一条语句中同时重定向错误和数据,只需要分别使用两个重定向符号即可,如下图
    • 只是在上一个例子中的语句里多加了一个 1> ,就可以让标准输出重定向到指定文件
  2. 如果想要让 STDOUTSTDERR 输出到同一个文件中,只需要使用 &> 即可,如下图
    • 可以看到,虽然三个目录中,不存在的目录分别是第一个和第三个
    • 但是在输出文件中两个错误信息出现的位置是最前端
    • 这是因为,为了避免错误信息散落在输出文件中,相对于 STDOUT ,shell 会自动赋予 STDERR 更高的优先级

15.2 在脚本中重定向输出

15.2.1 临时重定向

  1. 只需要在脚本中使用 >&2 就可以将输出重定向到 STDERR 指向的位置,如下图
    • 在脚本中,使用了一个简单的 if 结构判断传入的数值是不是 1 ,如果不是,就输出一句话,并使用 >&2 将其指向 STDERR
    • 因为在默认情况下,STDERRSTDOUT 都是指向显示器的,所以如果直接运行脚本,看不出有什么变化
    • 但是当运行脚本时,使用 2> 将错误信息指向特定文件,那么在运行脚本时,就不会直接输出错误信息,而是将错误信息输出到特定的文件中

15.2.2 永久重定向

  1. 如果不想在每次执行脚本时都指定各种输出需要的目标文件,则可以再脚本中使用 exec 命令提前告知对应的内容应该输出到什么文件中,如下图
    • 脚本开始就使用 exec 2>STDERR 指向了特定文件
    • 然后输出了两个语句,第一个语句没有指定文件标识符,会被默认作为 STDOUT
    • 第二个语句使用 >&2 指向了 STDERR ,表示这条语句是错误信息
    • 然后再使用 exec 1>STDOUT 指向了特定文件
    • 再次重复的输出两个语句
    • 执行脚本后发现第一个标准输出被直接输出了,这是因为 exec 1> 是在这条语句之后才定义的
    • 两个错误语句都成功的出现在了接收错误信息的文件中


15.3 在脚本中重定向输入

  1. 使用 exec 0< 可以实现将特定的文件作为标准输入传递给脚本,如下图
    • read 命令本来是用于接收用户的键盘输入的
    • 但是在使用 exec 0<STDIN 进行重定向后,read 命令就可以接收特定文件的内容作为标准输入进行读取

15.4 创建自己的重定向

  1. 前文中就有提到,shell 一共提供了 9 个文件描述符,除了 0 - 2 是内置的,其他的 6 个,3-8 都是支持自定义的文件描述符

15.4.1 创建输出文件描述符

15.4.1.1 普通创建

  1. 操作方式和之前一样,只需要使用 3-8 其中之一的数字作为文件描述符,并使用 3> 语法即可,如下图

15.4.1.1 内容追加创建

  1. 如果想要在输出时不覆盖之前的内容,可以使用 exec 3>> 即可,如下图
    • 这基本上就是之前 第 11 章 构建基础脚本 中对 重定向 这个知识点的结合运用

15.4.2 重定向文件描述符

  1. 如果想要临时修改一下文件描述符的指向,但是又不想丢失该文件描述符的默认位置,则可以使用类似于临时变量的方式中转一下,如下图
    • 这个操作看上去有点绕,但仔细阅读后不难理解


15.4.3 创建输入文件描述符

  1. 可以使用和重定向文件描述符一样的方式来让脚本在一次执行中既能接收文件输入,也能接收键盘输入,如下图


15.4.4 创建读写文件描述符

  1. 使用 exec 3<> targetFile 可以创建自定义的读写文件描述符
  2. 但是对同一个文件进行数据读写时,shell 内部维护的位置指针会导致文件中的数据混乱
  3. 不推荐使用

15.4.5 关闭文件描述符

  1. 一般情况下,在脚本中创建的文件描述符会在脚本运行结束后自动关闭
  2. 但如果想要在脚本执行中手动关闭文件描述符,可以使用 exec 3>&-
  3. 如果在脚本执行中手动关闭了文件描述符,那么在该脚本的后续执行过程中,就无法再次直接使用该文件描述符,如下图


15.4.5.1 关闭后再次打开文件描述符

  1. 从上一个例子可以看出来,如果在关闭文件描述符后,想要再次使用该文件描述符,则必须再次打开才行,不能直接使用
  2. 但如果再次打开文件描述符,这个文件描述符 会被 shell 识别为一个新的文件描述符 ,之前输出到目标文件中的内容也会被后续输出覆盖,如下图
    • 可以看到, 输出文件中只保留了最后一次输出的内容
    • 这是因为当文件描述符再次打开时,之前的输出内容被覆盖了


15.4.5.2 关闭后再次打开并实现内容追加

  1. 其实在第二次打开时使用 输出重定向追加符号( >> ) 即可,如下图

15.5 lsof 列出打开的文件描述符

  1. lsof 命令会列出整个 Linux 中打开的所有文件描述符
  2. 如果直接使用该命令,会输出大量的系统信息,所以一般会配合指令对输出进行过滤

15.5.1 lsof -p 指定进程 ID

  1. 使用 lsof -p PID 命令可以只显示指定进程 ID 的相关信息
  2. 例如使用 lsof -p $$ 就可以只显示与当前进程有关的信息,如下图
    • $$特殊环境变量 ,用于标识当前的进程 ID

15.5.2 lsof -d 指定文件描述符编号

  1. 使用 lsof -d fileDescriptorNo 命令可以只显示指定的文件描述符相关信息

15.5.3 lsof -a 合并过滤条件

  1. -a 选项单独使用没有意义,因为该选项的作用就是合并其他选项的过滤条件
  2. 使用 lsof -a -p $$ -d 0,1,2 命令则表示只显示即属于当前进程 ID ,又是 0,1,2 文件描述符的相关信息,如下图

15.5.4 lsof 列表内容解析

15.6 阻止命令输出

  1. Linux 提供了一个特殊文件,位于 /dev/null
  2. 这个文件里面什么内容都没有,而且任何输出到该文件的内容都不会被保存,全部都会被丢弃
  3. 所以如果在执行命令时,不想显示错误信息,则可以将 STDERR 指向该文件
  4. 例如在前文 15.1.2 重定向错误 中我们使用过的语句 ls no-exist-dir /opt 2> redirect-error-message
  5. 只需要稍作修改,改为 ls no-exist-dir /opt 2> /dev/null ,这样就可以实现错误信息 既不输出,也不保存 的效果,如下图

15.7 mktemp 创建临时文件

  1. Linux 使用 /tmp 目录来存放临时文件
  2. 大多数 Linux 都开启勒系统在启动时自动清空 /tmp 目录中的所有内容的配置

15.7.1 创建本地临时文件

  1. 使用 mktemp file.XXXXXX 命令可以在当前目录创建一个文件,如下图
    • 6 个 X 表示系统会为该文件分配一个 6 位的唯一字符串,来保证该文件不会重名
    • 同时可以看到,使用该命令创建文件后,会直接输出该文件的文件名
    • 这表示如果在脚本中使用该命令,可以在创建的同时接收到该文件的文件名,方便后续操作


15.7.1.1 创建本地临时文件并在脚本中使用

  1. 使用 mktemp 命令创建文件时,命令会直接返回创建的文件名,所以在脚本中使用也非常方便,如下图

15.7.2 mktemp -t 在 /tmp 目录创建临时文件

  1. 使用 mktemp -t temp.XXXXXX 命令可以实现在 Linux 的 /tmp 目录创建临时文件,如下图
    • 可以看到,使用该命令创建文件后,当前目录没有找到这个文件,在 /tmp 目录可以找到该文件
    • 需要注意的是,如果临时文件不是在当前目录创建的,创建后返回的文件名会携带完整路径

15.7.3 mktemp -d 创建临时目录

  1. 使用 mktemp -d dir.XXXXXX 命令可以创建一个临时目录,这让脚本在操作临时文件时可以更丰富,如下图

15.8 tee 记录消息

  1. 使用 tee 命令可以将 STDIN 的数据发往两处,一处是 STDOUT ,另一处可以是指定的文件,如下图
    • 这样就可以实现即输出内容到屏幕,也输出内容到文件


15.8.1 tee -a 实现记录并追加消息

  1. 使用 tee -a 命令可以实现将新的内容追加到指定文件中,而不是直接覆盖文件中的原始内容,如下图

15.9 一个创建 SQL 语句的实例

  1. 首先展示一下作者的写法,如下图


  2. 对于上述这个写法,有两处我目前无法理解
    • 第一处是 cat >> $outfile << EOF ,不太明白为啥要先使用 cat >> 读取文件,才能再使用 << EOF 追加内容
    • 第二处是 done < ${1} ,不太明白这个地方 while 命令是如何接收到文件数据的
  3. 所以我使用自己能理解的方式写了以下脚本,来实现相同的效果,如下图
    • cat >> $sqlFile << EOF 这块实现不知道怎么优化,不这么写就没有数据
    • 而且需要注意的是,EOF 的结尾不能有空格,一开始按照格式优化的标准加了两个空格,运行就一直报错,删除空格就好了
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 《Linux命令行与shell脚本编程大全》,4 E -- Chapter 15 一、理解输入和输出 显示脚本输出...
    生信摆渡阅读 5,064评论 0 2
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,362评论 0 5
  • 说明:本文部分内容均摘取自书籍《Linux命令行与shell脚本编程大全》,版权归原作者所有。《Linux命令行与...
    piziyang12138阅读 323评论 0 0
  • 说明:本文部分内容均摘取自书籍《Linux命令行与shell脚本编程大全》,版权归原作者所有。《Linux命令行与...
    数据萌新阅读 116评论 0 0
  • 看看自己的身边,那些成熟的人总是招人羡慕。 乐观而靠谱,工作效率高,意见中肯,总是能控制住自己,拒绝不良嗜好、垃圾...
    Baby露露阅读 373评论 0 1