你有没有留意过下面这种场景:
$ ls file1 file2 file3 file4 file5 $ ls | cat file1 file2 file3 file4 file5 |
单独执行 ls 时,它的输出是一行多个文件名,在它后面接个管道的话,它的输出就变成了一行一个文件名,这是为什么呢?这种表现的原理是:ls 程序自身可以判断出(通过 isatty() 库函数)它的标准输出是指向了一个终端,还是别的什么地方(管道或者普通文件)。如果是前者,则按一行多个文件名输出,否则按一行一个输出。
我们可以自己写个简单的 c 程序演示一下:
$ cat foo.c #include <stdio.h> int main() { printf(isatty(1) ? "终端 " : "非终端 "); } $ gcc foo.c -o foo $ ./foo 终端 $ ./foo | cat 非终端 $ ./foo > file $ cat file 非终端 |
然而偏偏有时候,在我们想要把一个命令的标准输出保存起来或者交给其它命令继续处理的时候,遇到了上面这种情况。比如 git log 命令,git log 直接在终端上执行是有颜色的,git log > git.log; cat git.log; 就没有颜色了。这个时候我们需要用到 script 命令,再用上面自己写的小程序演示一下好了:
$ ./foo | cat 非终端 $ script -qc ./foo | cat 终端 |
script 命令可以骗过 foo 命令,让它以为自己的标准输出是个终端。script 命令的 -c 选项就是专门干这个的:
-c COMMAND
Run the COMMAND rather than an interactive shell. This makes it easy for a script to capture the output of a program that behaves differently when its stdout is not a tty.
在 Bash 中,也有类似 c 语言中 isatty 函数功能的东西,就是 -t fd 条件表达式:
$ cat foo.sh if [[ -t 1 ]];then echo 终端;else echo 非终端;fi $ ./foo.sh 终端 $ ./foo.sh | cat 非终端 $ script -qc ./foo.sh | cat 终端 |