Linux 管道与命令重定向
查看【Linux】专题可浏览更多内容
Linux 有一个有趣的概念,基本上所有的输入和输出(都是文本)实际上都是数据/文本的流。就像水管一样,你可以连接和断开部分,把水重新引向不同的地方,所以你也可以连接和断开数据流。
「重定向」在命令和文件之间改变输入和输出,还可以连接多个命令,形成功能强大的「管道」。
标准输入、标准输出及标准错误
运行的每个命令或程序都有三个数据流与之相连:
- 标准输入:
STDIN (0)
; - 标准输出:
STDOUT (1)
; - 标准错误:
STDERR (2)
;
许多程序从 STDIN 标准输入获取输入,然后程序将运行结果发送给 STDOUT 标识输出,状态消息则是发给 STDEER 标准错误。
💡 默认情况下,标准输入与键盘相关联,而标准输出和标准错误与显示器屏幕相关联,并不会保存为磁盘文件。
管道与重定向
💡 UNIX 有一个「只做一件事并做到极致」的哲学理念,也就是说一个程序的功能就应该只用于某个单一用途,当既需要 A 又需要 B 时,则让它们「连接」在一起工作。
基于这个理念 UNIX/Linux 上有许多性能非常好的小型实用程序。
再想一想那些让人恼火的「大而全」而又号称「小而美」的软件...
管道
「管道」的作用在于让一个命令的输出作为输入发送到另一个程序。
管道是 Linux 中最有用的命令行功能之一,管道有无数种用途,几乎无处不在。
举个例子,/etc/hosts
文件的内容如下:
127.0.0.1 localhost
127.0.1.1 debian
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
管道操作符:|
我想要查看该文件前五行的最后一行是什么,那么可以这么做:
# 使用管道操作符 |
cat /etc/hosts | head -n 5 | tail -n 1
::1 localhost ip6-localhost ip6-loopback
发生了什么呢?
- 首先使用
cat
命令查看/etc/hosts
文件,但因为|
管道操作符所以不同于cat /etc/hosts
命令将结果输出在屏幕上,而是将结果作为输入交给了head
命令; head
命令搭配-n
选项将文件的头五行内容输出作为输入给了tail
命令;tail
命令搭配-n
选项输出head -n 5
拿到的输入内容的最后一行,后面没有|
管道操作符了,所以标准输出到了屏幕上;
重定向
命令行的重定向特性对解决某些特定的问题颇有帮助。
很多命令都用到了标准输入和标准输出,绝大多数命令行程序使用标准错误来显示提示性信息。
重定向允许我们修改输出结果的去处和输入的来源。
重定向标准输出操作符:> 与 >>
如把标准输出重定向到其他文件,而非出现在屏幕上,可以使用重定向操作符 >
:
# 使用重定向操作符
ip address > network.txt
# 查看 network.txt
cat network.txt
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 00:0c:29:fb:61:13 brd ff:ff:ff:ff:ff:ff
altname enp2s1
inet 192.168.1.1/24 brd 192.168.1.255 scope global dynamic ens33
valid_lft 1006sec preferred_lft 1006sec
inet6 fe80::20c:29ff:fefb:6113/64 scope link
valid_lft forever preferred_lft forever
使用 >
重定向到同一个文件会发生吗?
echo "hi" > hi.txt
echo "hi" > hi.txt
❓ 此时 hi.txt
文件的内容是什么?
只有一行 hi
,因为 >
对于重定向到的文件如果不存在则创建,如果已存在则覆盖,那么如果我想要追加输出结果而不是覆盖呢?
使用 >>
操作符:
echo "hi" > hi.txt
echo "hi" >> hi.txt
echo "hi" >> hi.txt
cat hi.txt
hi
hi
hi
重定向标准错误操作符:2> 与 2>>
文章开头提到过「标准错误」的「文件描述符」是 2
。
所以当想把标准错误的输出重定向到文件时,可以这么做:
ls /rootly 2> err.txt
cat err.txt
ls: cannot access '/rootly': No such file or directory
命令 ls /rootly
的标准错误信息就重定向到文件 err.txt
中了。
同标准输出操作符一样,如果想要追加标准错误信息而不是覆盖,则使用 2>>
操作符即可。
/dev/null
有时候可能会想要丢掉错误和状态消息,就可以将输出结果重定向到名为 /dev/null
的特殊文件。
/dev/null
是一个系统设备,通常称作「位桶(bit bucket)」,能够接收输入结果但不做任何处理。
重定向标准输出和标准错误输出:&> 与 &>>
再来看一个例子,假设当前我使用的是非特权用户,使用命令查看 /root
与 /home
目录:
ls /root /home
/home:
toor
ls: cannot open directory '/root': Permission denied
ls
告诉了我 /home
下有一个 toor
(非特权用户的主目录),然后告诉我我没有权限访问 /root
目录。
如果使用 >
标准输出操作符:
ls /root /home > info.txt
它会将 /home
目录下的结果重定向到文件 info.txt
,然后在屏幕上输出 ls: cannot open directory '/root': Permission denied
。
如果我想将标准输出与标准错误重定向分别保存到文件中呢?
ls /root /home > info.txt 2> err.txt
但如果我同时需要标准输出与标准错误重定向到文件中呢?使用 &>
操作符:
ls /root /home &> info.txt
cat info.txt
/home:
toor # 非特权用户的主目录
ls: cannot open directory '/root': Permission denied
💡 在旧版本的 Shell 中,一般会写成:
ls /root /home > info.txt 2>&1
这表示先将/home
的结果重定向到到info.txt
文件中,然后使用2>&1
将文件描述符 2(标准错误)重定向到文件描述符 1(标准输出)
这样就同时得到标准输出与标准错误的结果了。
和之前一样,如果想要追加内容而不是覆盖,使用 &>>
操作符。