Shell:管道符与重定向

前言

到目前位置自己还没专门花时间研究过Linux上那些日日都在用的工具(如ShellVim)他们本来的用法和含义,本来觉得没必要,不过在看了missing-semester之后顿时感觉效率提高了不少。因此做一个笔记,把一些很实用但是自己并不会去关注的简单用法给记录一下。

参考文章

Shell

prompt

在使用最基础的bash作为命令行的时候,常常能发现用户后面有的时候是$二有的时候是#

1
2
[halc@Zephyrus blog]$ su
[root@Zephyrus blog]#

这里$代表了当前用户为普通用户,而#的含义则代表当前是在root用户下

重定向

输出重定向

shell程序中,程序的执行主要分为 输入流输出流 两种不同的流,程序会从输入流中读取信息,然后在通过处理之后打印到输出流当中。

cat为例,其作用为将一个文件的内容打印到输出流当中,通过使用 > file< file 来对流进行重定向,可以覆写或者创建对应内容的文件

1
2
3
4
5
6
7
8
# 将我存于github的pubkey打印输出成id_rsa.pub文件
curl https://github.com/HalcyonAzure.keys > id_rsa.pub

# 将id_rsa.pub的内容重定向 **追加** 到authorized_keys当中
cat id_rsa.pub >> authorized_keys

# 将id_rsa.pub的内容先重定向给cat,再由cat输出到pubkey当中
cat < id_rsa.pub > pubkey

拓展补充 1:所有的Linux进程都包含3个文件描述符,分别是 标准输入标准输出错误输出
标准输入(stdin):对应的文件标识符为0,使用<<<来操作
标准输出(stdout):对应的文件标识符为1,使用>>>来操作
错误输出(stderr): 对应的文件标识符为2,使用2>2>>来操作
默认情况下,标准输出和错误输出都默认为使用者的屏幕,通过使用输出重定向可以做到控制日志的效果

补充拓展 2:>/dev/null 2>&12>&1 >/dev/null的区别

  • 对于>/dev/null 2>&1是指先将标准输出指向黑洞设备,然后再将错误输出指向标准输出的指向内容(此时是黑洞),因此标准输出和错误输出都将不输出
  • 对于2>&1 >/dev/null是先将错误输出指向标准输出(此时为屏幕),然后将标准输出指向黑洞,因此此时错误输出将打印到屏幕,而标准输出将不输出
1
2
# 运行example.sh,并将标准输出重定向至stdout.log,将错误输出重定向至stderr.log
bash example.sh 1>stdout.log 2>stderr.log

以一个简单的test.cpp程序为例

1
2
3
4
5
6
7
8
#include<iostream>
using namespace std;

int main() {
cout << "This is stand output" << endl;
cerr << "This is error output" << endl;
return 0;
}

通过编译并重定向运行

1
2
3
4
5
6
7
8
[halc@Zephyrus ~]$ g++ test.cpp -o test
[halc@Zephyrus ~]$ ./test 1>stand.log 2>err.log
[halc@Zephyrus ~]$ rg output *.log
stand.log
1:This is stand output

err.log
1:This is error output

如果希望将对应的文件描述符关闭的话有两种方法

1
2
3
4
# $-指关闭对应的文件操作符
[halc@Zephyrus ~]$ ./test 1>$- 2>$-
# /dev/null为linux内的黑洞设备
[halc@Zephyrus ~]$ ./test 1>/dev/null 2>/dev/null
  • 对于>操作符,首先会判断右侧的文件是否存在,如果存在就删除再创建,如果不存在则直接创建,并且右侧的文件一定会置空。
  • 一条命令执行前会检查0,1,2三个I/O设备是否正常,如果异常则不会进行命令执行

输入重定向

如果想要将内容输出重定向到某个文件,以cat举例有两种不同的办法

1
2
3
4
# 将read.md的内容重定向至write.md
cat > write.md < read.md
# "<< EOF"指对流进行重定向输入,直到遇到"EOF"(可修改)作为末尾则结束
cat > input.md << EOF

摘自:How does “<<” operator work in linux shell?

<< 操作符主要有以下三个操作逻辑

  1. 首先执行该操作符左侧的程序,在上面的例子中就是cat
  2. 抓取用户包括换行等所有的输入内容,直到输入的内容为用户指定的EOF结尾内容(在上面的例子中则恰好也是EOF)则停止
  3. 将所有除了EOF的内容都作为(1)程序的标准输入执行

实例:通过bash脚本创建一个test.cpp文件

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
cat > test.cpp << EOF
#include<iostream>
using namespace std;

int main() {
cout << "This is stand output" << endl;
cerr << "This is error output" << endl;
return 0;
}
EOF

管道和重定向的使用区别

1、文件类型上
左边的命令应该有标准输出 | 右边的命令应该能接受标准输入;
左边的命令应该有标准输出 > 右边只能是文件;
左边的命令应该需要标准输入 < 右边只能是文件;

2、管道触发两个子进程执行 “|” 两边的程序,而重定向是在一个进程内执行。

结合管道的输入重定向

1
2
3
4
5
$ (sed -n '1,$p' | grep -n 'output') < test.cpp
5: cout << "This is stand output" << endl;
6: cerr << "This is error output" << endl;
# 等价于 sed -n '1,$p' < test.cpp | grep -n 'output'
# 对于管道运算符,如果希望将`test.cpp`传递给前面的第一个可执行文件`sed`,则需要使用单括号将整个管道传输看作一个单独的指令,否则`test.cpp`将传入`grep`内

由于标准输入和标准输出在管道运算符中的重定向是发生在"内容输出"之前的,因此可以通过重定向来修改管道中传输的数据

Because pipeline assignment of standard input or standard output or both takes place before redirection, it can be modified by redirection.

举个例子来说,本来管道传输默认只传输标准输出的内容,并不会传输错误输出

1
[halc@Zephyrus ~]$ command1 2>&1 | command2

通过上面的指令,首先command1错误输出 会在执行前被重定向至标准输入,然后command1执行,将 标准输出错误输出 一并通过管道进行传输,作为command2的标准输入

结合管道的输出重定向

1
2
3
4
# 首先将test.sh的内容通过cat打印到标准输出, 然后管道传输该输出给tee
# tee执行之前通过&>将tee的标准输入和错误输出都重定向至/dev/null中,
# 然后执行tee将管道获取的内容写入text.txt,并且将相同的内容写入null设备当中
cat test.sh | tee text.txt &> /dev/null

> 输出重定向,往往在命令最右边(也可以用到命令中间),接收左边命令的输出结果,重定向到指定文件。

1
2
3
4
5
6
7
8
# 如果管道符左侧的程序已经将标准重定向指向了其他文件,那么在bash中管道传输的数据将为空
[halc@Zephyrus ~]$ rg Word dict &>log | cat
# 此时无输出
[halc@Zephyrus ~]$ cat log
Word1
Word2
Word3
# 但是log内应该是有内容匹配的
zsh下的重定向与管道传输

在自己实验的时候发现zsh下即使重定向了标准输出和错误输出依旧可以通过管道读取内容,这主要是zsh有一个可以将输出重定向给多个文件的特性,对于管道也会进行二次传递

参考 Redirection and pipe behavior in bash vs. zsh

Read the MULTIOS documentation in the zshmisc man page. It’s a feature of zsh which causes it to redirect the output to multiple files at the same time, and it can also be a pipe.

具体举例

1
ls >a >b

上面这个命令在bash当中只有文件a会有内容,而b中并没有获取到标准输出。但是在zsh下执行上面的命令,则ab中都会拥有相同的输出内容。


Shell:管道符与重定向
https://halc.top/p/db6cc46f
作者
HalcyonAzure
发布于
2022年7月4日
许可协议