重定向符号
应用重定向符号前,要清楚下面的三个问题:
- fd 文件描述符是什么
- dup、dup2函数的作用
- 0、1、2 fd分别是什么文件描述符
第一个问题:点我
第二个问题:
- int dup(int oldfd):返回一个新的文件描述符newfd,newfd的值为可用文件描述符的最小值。并且newfd指向oldfd所指向的文件表项
- int dup2(int oldfd,int newfd):让传入的新的文件描述符newfd与旧的文件描述符oldfd指向同一文件表项
第三个问题:
名称 | 文件描述符 | 含义 | 对应设备 | 功能说明 | 对应Java |
---|---|---|---|---|---|
STDIN | 0 | 标准输入 | 键盘 | 获取执行时所要的输入数据 | System.in |
STDOUT | 1 | 标准输出 | 显示器 | 输出执行后的输出结果 | System.out |
STDERR | 2 | 标准错误输出 | 显示器 | 输出执行时的错误信息 | System.err |
命令查询验证
[root@VM-24-5-centos ~]# ll /dev/ | grep std
lrwxrwxrwx 1 root root 15 3月 29 01:56 stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 3月 29 01:56 stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 3月 29 01:56 stdout -> /proc/self/fd/1
通过下图来辅助理解stdin、stdout、stderr(方便理解的简略图,并不完整)
每个进程可以根据fd获取数据的输入与数据的发出。每个进程至少要有三个fd即:{0, 1, 2}。如果还有其他的文件,在fd表中加入对应项即可。假如在程序中,我们需要获取用户的输入数据,那么就可以依靠fd为0的文件。输出时,具体是输出到屏幕还是其他文件中,取决于我们应用哪一个fd项。
我们还可以通过dup2函数,来将两个fd指向同一个文件。比如:
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
const char *str = "hello world!\n";
dup2(1,3);
write(3,str,strlen(str));
close(3);
return 0;
}
执行编译后的可执行文件时,就会在屏幕上显示:hello world!
输入重定向符
<:重定向标准输入符。将stdin标准输入进行重定向。
# 将指定路径下的文件作为标准输入传递给 cat 命令
congqingquan@localhost Desktop % cat < out
Hello world
输出重定向符(最常用)
> 与 >>(默认省略了1):重定向标准输出符。将stdout重定向到指定文件。前者覆盖,后者追加。
congqingquan@localhost Desktop % echo C99 > out
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop % echo C99 >> out
congqingquan@localhost Desktop % cat out
C99
C99
congqingquan@localhost Desktop % echo C99 1> out
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop %
2> 与 2>>:重定向标准错误输出符。将stderr重定向到指定文件。前者覆盖,后者追加。
public class Test {
public static void main(String[] args) throws Exception {
int a = 1/0;
System.out.println("Hello world");
}
}
congqingquan@localhost Desktop % java Test 2> out
congqingquan@localhost Desktop % cat out
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
congqingquan@localhost Desktop % java Test 2>> out
congqingquan@localhost Desktop % cat out
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
congqingquan@localhost Desktop % java Test 2> out
congqingquan@localhost Desktop % cat out
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
标准输出符无法应用在程序发生错误时
congqingquan@localhost Desktop % java Test >> out
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:3)
congqingquan@localhost Desktop % cat out
congqingquan@localhost Desktop %
&> 与 &>>:重定向标准输出 & 标准错误输出符。前者覆盖,后者追加。
注意:&>是一个整体,表示重定向标准输出、标准错误输出。
congqingquan@localhost Desktop % echo C99 &> out
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop % echo C99 &>> out
congqingquan@localhost Desktop % cat out
C99
C99
congqingquan@localhost Desktop % echo2 C99 &> out
congqingquan@localhost Desktop % cat out
zsh: command not found: echo2
congqingquan@localhost Desktop %
&> 的另一种写法:2>&1
congqingquan@localhost Desktop % echo C99 > out 2>&1
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop % echo C99 >> out 2>&1
congqingquan@localhost Desktop % cat out
C99
C99
congqingquan@localhost Desktop % echo2 C99 > out 2>&1
congqingquan@localhost Desktop % cat out
zsh: command not found: echo2
congqingquan@localhost Desktop %
问题1:>后面的&是什么意思?
&表明后面的1为一个fd,而非文件名。如果写成2>1,此时的1代表的是一个文件名,而非一个fd。文件名不等于文件的fd。
验证1:终端已经提示了bad file descriptor
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop % echo C992 > out 1>&99999
zsh: 99999: bad file descriptor
congqingquan@localhost Desktop %
验证2:
congqingquan@localhost Desktop % echo2 C99 2> 1
congqingquan@localhost Desktop % cat 1
zsh: command not found: echo2
congqingquan@localhost Desktop %
如何查看文件的fd?
终端1:
[root@VM-24-5-centos home]# tail -f out
C99
终端2:
[root@VM-24-5-centos ~]# ll /proc/11292/fd/
总用量 0
lrwx------ 1 root root 64 5月 28 21:20 0 -> /dev/pts/1
lrwx------ 1 root root 64 5月 28 21:20 1 -> /dev/pts/1
lrwx------ 1 root root 64 5月 28 21:19 2 -> /dev/pts/1
lr-x------ 1 root root 64 5月 28 21:20 3 -> /home/out
lr-x------ 1 root root 64 5月 28 21:20 4 -> anon_inode:inotify
问题2:为什么2>&1要写到最后?
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("Hello world");
int a = 1/0;
}
}
分析下两条命令的执行过程:
congqingquan@localhost Desktop % java Test > out 2>&1
congqingquan@localhost Desktop % cat out
Hello world
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:5)
congqingquan@localhost Desktop %
- 起初:fd1 -> 屏幕
- > out,fd1 -> out,
- 2>&1,fd2 -> fd1 -> out
- 执行java Test,程序输出内容写入到fd1文件中,即out文件。发生异常,错误信息写入fd2文件中,即out文件
congqingquan@localhost Desktop % java Test 2>&1 > out
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:5)
congqingquan@localhost Desktop % cat out
Hello world
congqingquan@localhost Desktop %
- 起初:fd1 -> 屏幕
- 2>&1,fd2 -> fd1 -> 屏幕
- >out,fd1 -> out
- 执行java Test,程序输出内容写入到fd1,即out文件。发生异常,错误信息写入fd2文件中,即展示在屏幕
分别设定标准输出目的地 与 标准错误输出目的地
congqingquan@localhost Desktop % echo C99 1> out 2>error
congqingquan@localhost Desktop % cat out
C99
congqingquan@localhost Desktop % cat error
congqingquan@localhost Desktop % echo2 C99 1> out 2>error
congqingquan@localhost Desktop % cat out
congqingquan@localhost Desktop % cat error
zsh: command not found: echo2
congqingquan@localhost Desktop %