Linux中标准输出和标准错误的重导向

Stella981
• 阅读 323

如果一个命令需要长时间在服务器上运行,那么很多时候会用到nohup命令,这时即便远程登录ssh中断了与服务器的联系,那么在服务器上运行的命令也不会因此而被迫停止。

通常情况下,nohup与&连用,&的意思是将该命令放在后台执行。如下:

nohup example.sh &

将exmaple.sh通过&放在服务器后台运行,nohup确保了即便当前ssh远程连接中断,example.sh仍然能够不受影响,继续在远程服务器中运行。

最近有两个配对测序文件,需要比对到参考基因组上,通过bwa可以完成,同时由于该文件比较大,运行时间长,为了避免网络连接不稳定造成ssh中断,使用nohup

nohup bwa mem ref.fa read1.fq.gz read2.fq.gz > read12.sam &

将两个测序文件合并并生成sam文件。
写好命令,一个回车键按下去,“啪”一声,那就一个爽。然后不用管它,十余个小时之后,果然生成了一个很大的文件read12.sam文件。
但是,当用该sam文件生成bam文件时,提示错误sam文件存在错误!!!【十余个小时的计算白费了】
仔细检查了一下sam文件,发现程序运行的结果和程序运行过程中的说明输出到了同一个文件中!

1、标准输出和标准错误

标准输出(standard output)即结果默认的输出地方,比如在bash中,

$ echo 'hello'
hello

在默认状态下,’hello’时输出到你的终端(terminal)上显示。
再如,通过cat命令显示一个文本文件,

$ cat hello.txt
Hello!
This is a test!

但是,如果这个文本文件在当前路径下不存在,在会输出错误:

$ cat No_exist.txt
cat: No_exist.txt: No such file or directory
这时的输出内容“cat: No_exist.txt: No such file or directory”就是标准错误(standard error)。

2、重导向输出

默认情况下,标准输出和标准错误都会在终端显示。如果要将标准输出不是输出在终端,而是输出到一个其他文件中,这个时候就是重导向输出,可以通过“>“符号来完成。

echo 'hello' > hello.txt

将“hello”输出到hello.txt文件中,系统会新创建该文件,如果路径中存在该文件,旧的文件将会被覆盖。
还可以使用”>>”符号,这样不会覆盖旧文件,会将”hello”添加到旧文件中。
那么,这儿有一个问题,是不是所有输出到终端的内容都可以重导向输出到一个文件中?比如,如果是标准错误,能否通过”>”输入到文件中?

$ cat No_exist.txt > output.txt
cat: No_exist.txt: No such file or directory

结果证明:不能!output.txt依旧是一个空文件,而错误内容并没有出现在该文件中,依旧在终端显示!所以不能直接通过”>”将标准错误输出到文件中!
那么应该怎样才能将标准错误输出到文件中呢?

3、文件描述符

在bash中,通常使用3个整数来表示标准输入(0)、标准输出(1)和标准错误(2)。
如果要把标准错误输出到文件中,可以使用

cat No_exist.txt 2> tt.txt

这时在tt.txt文件中就会出现标准错误“cat: No_exist.txt: No such file or directory”。
同样的道理,我们可以将标准错误重导向输出为标准输出,2>&1
比如

$ cat No_exist.txt 
cat: No_exist.txt: No such file or directory
$ cat No_exist.txt 2>&1
cat: No_exist.txt: No such file or directory

虽然它们在终端上输出的内容看起来没有什么区别,但是它们的身份是不一样的,第一个是以标准错误的形式输出的,而第二个是标准输出。我们可以通过管道符号验证一下它们的不同。

$ cat No_exist.txt | sed 's/or/and/'
cat: No_exist.txt: No such file or directory
$ cat No_exist.txt 2>&1| sed 's/or/and/'
cat: No_exist.txt: No such file and directory

现在可以看出区别了,第一个标准错误无法通过管道符号把“or”替换成“and”,而第二个是标准输出,可以通过管道符号,把其中的“or”替换成“and”.
同样的道理,也可以将标准输出重导向为标准错误“1 >2&“

那么回过头来,看最开始的那个问题,为什么nohup同时会运算结果和运算过程的描述输出到同一个sam文件中呢?
为了简便,用下面的代码(example.sh)重现了nohup中的错误。

#!/bin/bash

echo "this is outcome!"
sleep 1
echo "sleep for 1s" >&2
echo "this is outcome, too!"
sleep 2
echo "second sleep for 2s" >&2

其中sleep的过程描述通过 >&2以标准错误的形式出现,而outcome则以标准输出的形式输出。
正常情况下,运行:

$ ./example.sh > outcome.txt
sleep for 1s
second sleep for 2s

$ cat outcome.txt 
this is outcome!
this is outcome, too!

标准错误直接输出到了终端中,运行结果输出到了outcome.txt中,没有任何问题。但是在nohup的情况下,这种情况就变了。
在nohup的说明中,提到“如果标准输出是在终端,那么输出的内容将会被添加到‘nohup.out’文件中;如果标准错误的输出是在终端,那么内容将会被重导向到标准输出“。这就意味着,在没有特殊说明的情况下,标准输出和标准错误将会被重导向输出到同一个地方。如下,

$ nohup ./example.sh 
appending output to nohup.out

$ cat nohup.out 
this is outcome!
sleep for 1s
this is outcome, too!
second sleep for 2s

在nohup.txt文件中不仅出现了我想要的运行结果,还出现了我不想要的运行过程!这也就解释了为什么在我的sam文件中会出现很多本不应该属于该文件的内容。
既然知道了原因,那么解决问题就不难了。我们可以通过重导向输出,把运行结果和运行过程分别输出到不同的文件中。

$ nohup ./example.sh 2>stderr.log 1>outcome.txt

$ cat stderr.log 
sleep for 1s
second sleep for 2s

$ cat outcome.txt 
this is outcome!
this is outcome, too!

这样,将过程以标准错误形式输出到stderr.log中,将结果以标准输出形式输出到outcome.txt中。
所以本文最开头提到的命令可以改为:

nohup bwa mem ref.fa read1.fq.gz read2.fq.gz 1> read12.sam 2>read12.log &

总结

nohup在默认条件下,标准错误和标准输出会重导向到同一个文件中;通过文件描述符(0,1,2)来对控制输出内容;养成良好的输出控制习惯,对标准输出和标准错误要区别对待。

===== THE END =====

参考资料:

https://robots.thoughtbot.com/input-output-redirection-in-the-shell#file-descriptors
https://www.brianstorti.com/understanding-shell-script-idiom-redirect/

Linux中标准输出和标准错误的重导向

点赞
收藏
评论区
推荐文章
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
刚刚好 刚刚好
4个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
晴空闲云 晴空闲云
4个月前
css中box-sizing解放盒子实际宽高计算
我们知道传统的盒子模型,如果增加内边距padding和边框border,那么会撑大整个盒子,造成盒子的宽度不好计算,在实务中特别不方便。boxsizing可以设置盒模型的方式,可以很好的设置固定宽高的盒模型。盒子宽高计算假如我们设置如下盒子:宽度和高度均为200px,那么这会这个盒子实际的宽高就都是200px。但是当我们设置这个盒子的边框和内间距的时候,那
艾木酱 艾木酱
3个月前
快速入门|使用MemFire Cloud构建React Native应用程序
MemFireCloud是一款提供云数据库,用户可以创建云数据库,并对数据库进行管理,还可以对数据库进行备份操作。它还提供后端即服务,用户可以在1分钟内新建一个应用,使用自动生成的API和SDK,访问云数据库、对象存储、用户认证与授权等功能,可专
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Wesley13 Wesley13
1年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
京东云开发者 京东云开发者
1个月前
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
helloworld_34035044 helloworld_34035044
6个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
helloworld_28799839 helloworld_28799839
4个月前
常用知识整理
Javascript判断对象是否为空jsObject.keys(myObject).length0经常使用的三元运算我们经常遇到处理表格列状态字段如status的时候可以用到vue