linux · 2024-01-26 0

linux 的标准输入、标准输出、标准错误、重定向的使用

前言

今天使用 nc 扫描端口,过滤成功的端口,发现并没有过滤成功。于是探究了原因,发现标准错误输出,管道不能直接过滤,管道接受的是标准输入。

root@44d615a2f5b2:/# nc -znv 127.0.0.1 1-1024 | grep succeeded

解决办法,重定向到标准输出,再用管道过滤。

root@44d615a2f5b2:/# nc -znv 127.0.0.1 1-1024 2>&1 | grep succeeded

1.标准输入(stdout)

echo 打印字符串,会进行标准输入

root@44d615a2f5b2:/# echo "hello world"
hello world

每个基于 Unix 的操作系统都有一个“输出的默认位置”的概念。大家都称它为“标准输出”或“stdout”。shell(可能是 bash 或 zsh)一直在监视默认输出位置。当 shell 在标准输出那里看到新的输出时,它会将其打印在屏幕上。否则,shell 不去监视标准输出位置,echo "hello world" 会将 "hello world" 发送到那个默认位置,但我们却看不到。

2.标准输出(stdin)

root@44d615a2f5b2:/# cat 
hello
hello
world
world

标准输入是命令监听信息的默认位置。shell 会一直监视默认输入位置,一有新的输入进来,shell 便会把数据读进来,然后输出到 stdout。

3.标准错误(stderr)

使用 cat 打开一个不存在文件,会进行标准错误输出

root@44d615a2f5b2:/# cat hello
cat: hello: No such file or directory

stderr 似乎和 stdout 没什么两样,但是借助管道验证下

在 linux 中,管道是将一个命令的 stdout 连接到另一个命令的 stdin,可以使用管道符号 | 来完成这个操作

root@44d615a2f5b2:/# echo "hello world" | sed "s/hello/hi/"
hi world
root@44d615a2f5b2:/# cat hello | sed "s/hello/hi/"
cat: hello: No such file or directory

使用 sed 替换,第一个是标准输出,管道可以就接受到,可以替换成功;第二个是标准错误输出,管道没有接受到,就无法替换。

4.重定向

默认情况下,当 shell 执行的命令的输出一定会到 stdout 或者 stderr。

如果不想让信息输出到 stdout 或者 stderr,那就要用到重定向了,可以使用 > 将输出进行重定向。

root@44d615a2f5b2:/# echo "hello world" > file
root@44d615a2f5b2:/# cat file 
hello world
root@44d615a2f5b2:/# cat hello > file
cat: hello: No such file or directory
root@44d615a2f5b2:/# cat file
root@44d615a2f5b2:/# cat hello 2> file
root@44d615a2f5b2:/# cat file 
cat: hello: No such file or directory

如果 > 或者 >> 前不添加文件描述符,则默认是将标准输出重定向到 file,如果想重定向 stderr,则需要 2> file

5.文件描述符

文件描述符(File descriptor)是表示输入/输出源的正整数,例如 stdin 是 0,stdout 是 1,stderr 是 2,这些数字是由 POSIX 标准定义的

如果想将输出重定向到某一文件描述符,则需要借助>&运算符并跟上文件描述符来完成。

名称 代码 操作符 Linux 下文件描述符
标准输入(stdin) 0 < 或 << /dev/stdin -> /proc/self/fd/0 -> /dev/pts/0
标准输出(stdout) 1 >, >>, 1> 或 1>> /dev/stdout -> /proc/self/fd/1 -> /dev/pts/0
标准错误输出(stderr) 2 2> 或 2>> /dev/stderr -> /proc/self/fd/2 -> /dev/pts/0

echo "hello" > hello.txt,其实也可以写成 echo "hello" 1> hello.txt

root@44d615a2f5b2:/# echo "hello world" >&1 | sed "s/hello/hi/"
hi world
root@44d615a2f5b2:/# echo "hello world" >&2 | sed "s/hello/hi/"
hello world

可以通过 2>&1 将标准错误重定向给标准输出,符号 >& 是一个整体,不可分开

root@44d615a2f5b2:/# cat hello
cat: hello: No such file or directory
root@44d615a2f5b2:/# cat hello | sed "s/hello/hi/"
cat: hello: No such file or directory
root@44d615a2f5b2:/# cat hello 2>&1 | sed "s/hello/hi/"
cat: hi: No such file or directory

6.常见用法

cmd > logfile 2>&1

将一个命令的 stdout 和 stderr 都重定向到某一个文件中。2>&1 要放在后面,才表示标准错误输出和标准输出都定向到 logfile 中。

分析 "cmd > logfile 2>&1", 不妨把1和2都理解是一个指针

本来1 -----> 屏幕 (1 指向屏幕)
执行 > logfile 后, 1-----> logfile (1 指向 logfile)
执行 2>&1 后, 2-----> 1 (2 指向 1,而 1指向 logfile,因此 2也指向了 logfile)

分析 "cmd 2>&1 > logfile"

本来 1-----> 屏幕 (1 指向屏幕)
执行 2>&1 后, 2 ----- > 1 (2 指向 1,而 1 指向屏幕,因此 2 也指向了屏幕)
执行 > log 后, 1 -----> log ( 1指向 log,2 还是指向屏幕)
所以这就不是想要的结果。

"> logfile 2>&1" 简写 "&> log" 或 “>& log”

"cmd 1 > logfile 2>&1" 与 "cmd 1>logfile 2>logfile“,区别就在于前者只打开一次文件 logfile,后者会打开文件两次,并导致 stdout 被 stderr 覆盖,可能导致某些输出错误

7.输出静音

/dev/null 它会吞下所有接收到的内容并且不做任何操作

echo "hello world" > /dev/null