tee-同时显示到文件和屏幕

tee 只能重定向 stdout

很多文章是这么写的:

  1. ls |tee a

这样的话,屏幕和文件a 都会有 ls 的内容。初看这样的教程,认为只要输出时加个 tee 就能把「屏幕所有输出」全部重定向到文件中。

反例-tee 不能直接重定向 stderr

使用下列命令,发现屏幕输出了但是文件 a 为空。

  1. go build -o hodor -v -n . |tee > a

tee 定义

man 手册上对 tee 的定义:

tee - read from standard input and write to standard output and files

tee 读取标准输出,然后将「标准输出」输出到屏幕和文件。
换而言之,如果 tee 读取的不是标准输出,那么就不能捕获到。

tee 读取一切输出- 2>&1

可以先把所有输出重定向到标准输出,然后再使用 tee

  1. go build -o hodor -v -n . 2>&1 |tee > a

2>&1

1 和 2 代表什么

参考:https://superuser.com/questions/71428/what-does-21-do-in-command-line

The 1 denotes standard output (stdout). The 2 denotes standard error (stderr). So 2>&1 says to send standard error to where ever standard output is being redirected as well. 1 代表标准输出。2 代表标准错误。所以 2>&1 代表将标准错误重定向标准输出。
这个回答说的不错,但是没有解释为啥还需要 &

为什么不写成 2>&1

参考:https://stackoverflow.com/questions/818255/in-the-shell-what-does-21-mean
stackoverflow 上的这个答案说的更具有参考性: File descriptor 1 is the standard output (stdout). File descriptor 2 is the standard error (stderr). Here is one way to remember this construct (although it is not entirely accurate): at first, 2>1 may look like a good way to redirect stderr to stdout. However, it will actually be interpreted as “redirect stderr to a file named 1“. & indicates that what follows is a file descriptor and not a filename. So the construct becomes: 2>&1.

  • 1 和 2 都是文件描述符;
  • 2>1 就可以实现将标准错误重定向标准输出的作用。但是写成 2>1 解释器会把 1 理解为文件名。 & 表明接下来的是一个文件描述符而不是文件。

从 C 语言角度理解

参考: https://www.cnblogs.com/zhangyabin—-acm/p/3203745.html
stdout — 标准输出设备 (printf(“..”)) 同 stdout。
stderr — 标准错误输出设备
两者默认向屏幕输出。
但如果用转向标准输出到磁盘文件,则可看出两者区别。stdout输出到磁盘文件,stderr在屏幕。

重定向和管道的区别?

链接

区别

  • pipe | 用来把输出传递给另外一个程序或者设备;
  • Redirect > 用来把输出传递给另外一个文件或流.

Pipe is used to pass output to another program or utility.
Redirect is used to pass output to either a file or stream.

Example: thing1 > thing2 vs thing1 | thing2
thing1 > thing2

  1. Your shell will run the program named thing1
  2. Everything that thing1 outputs will be placed in a file called thing2. (Note - if thing2 exists, it will be overwritten)

If you want to pass the output from program thing1 to a program called thing2, you could do the following:
thing1 > temp_file && thing2 < temp_file
which would

  1. run program named thing1
  2. save the output into a file named temp_file
  3. run program named thing2, pretending that the person at the keyboard typed the contents of temp_file as the input.

However, that’s clunky, so they made pipes as a simpler way to do that. thing1 | thing2 does the same thing as thing1 > temp_file && thing2 < temp_file
EDIT to provide more details to question in comment:
If > tried to be both “pass to program” and “write to file”, it could cause problems in both directions.
First example: You are trying to write to a file. There already exists a file with that name that you wish to overwrite. However, the file is executable. Presumably, it would try to execute this file, passing the input. You’d have to do something like write the output to a new filename, then rename the file.
Second example: As Florian Diesch pointed out, what if there’s another command elsewhere in the system with the same name (that is in the execute path). If you intended to make a file with that name in your current folder, you’d be stuck.
Thirdly: if you mis-type a command, it wouldn’t warn you that the command doesn’t exist. Right now, if you type ls | gerp log.txt it will tell you bash: gerp: command not found. If > meant both, it would simply create a new file for you (then warn it doesn’t know what to do with log.txt).

什么时候用 管道符| /文件重定向变量$

参考链接: 关于 Linux shell 你必须知道的, https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247485000&idx=1&sn=6fcb162ca9dc85be23efb61db5f8a09d&chksm=9bd7f840aca071568417b5f48517dfb9742da0025b2bae918306be6ccd72039d79a31f6a3c7a&scene=21#wechat_redirect

这个问题一定是最容易让人迷惑的,具体来说,就是搞不清什么时候用管道符|和文件重定向>,<,什么时候用变量$。
比如说,我现在有个自动连接宽带的 shell 脚本connect.sh,存在我的家目录:

  1. $ where connect.sh
  2. /home/fdl/bin/connect.sh

如果我想删除这个脚本,而且想少敲几次键盘,应该怎么操作呢?我曾经这样尝试过:

  1. $ where connect.sh | rm

实际上,这样操作是错误的,正确的做法应该是这样的:

  1. $ rm $(where connect.sh)

前者试图将where的结果连接到rm的标准输入,后者试图将结果作为命令行参数传入。

什么是标准输入?

标准输入就是编程语言中诸如scanf或者readline这种命令;

什么是参数?

而参数是指程序的main函数传入的args字符数组。

管道符和重定向是标准输入

管道符和重定向符是将数据作为程序的标准输入,而$(cmd)是读取cmd命令输出的数据作为参数,前文画图解释过:

输入重定向就是说,程序想读取数据的时候就会去 files[0] 读取,所以我们只要把 files[0] 指向一个文件,那么程序就会从这个文件中读取数据,而不是从键盘: [图片]

同理,输出重定向就是把files[1]指向一个文件,那么程序的输出就不会写入到显示器,而是写入到这个文件中: [图片]

管道符其实也是异曲同工,把一个进程的输出流和另一个进程的输入流接起一条「管道」,数据就在其中传递: [图片] labuladong,公众号:labuladong> Linux 进程、线程、文件描述符的底层原理

用刚才的例子说,rm命令源代码中肯定不接受标准输入,而是接收命令行参数,删除相应的文件。作为对比,cat命令是既接受标准输入,又接受命令行参数:

  1. $ cat filename
  2. ...file text...
  3. $ cat < filename
  4. ...file text...
  5. $ echo 'hello world' | cat
  6. hello world

如何判读某命令是否支持标准输入?

如果命令能够让终端阻塞,说明该命令接收标准输入,反之就是不接受,比如你只运行cat命令不加任何参数,终端就会阻塞,等待你输入字符串并回显相同的字符串。

比如 cat 会阻塞, 而 mv 不会.