重定向

前言

  1. 本章我们将要探讨命令行最酷的功能- I/O 重定向。
  2. I/O 是输入/输出 (input/output) 的缩写。这个功能可以把命令行的输入的重定向为从文件中获取内容,也可以把命令行的输出结果重定向到文件中。
  3. 如果我们将多个命令行关联起来,将形成非常强大的命令-管道。
  4. 本章的主要命令:
  • cat: 合并文件
  • sort: 对文本进行排序
  • uniq: 报告或删除文件中的重复的行
  • wc: 打印文件中的换行符、字和字节的个数
  • grep: 打印匹配行
  • head: 输出文件的第一部分内容
  • tail: 输出文件的最后一部分内容
  • tee: 读取标准输入的数据,并将其内容输出到标准输出和文件中

标准输入、标准输出和标准错误

基本介绍

  1. 目前为止,我们使用过的很多程序生成不同种类的输出。
  2. 这些输出通常包含两种类型。一种是程序运行的结果,即该程序生成的数据;另一种是状态和错误信息,表示程序当前的运行情况。
  3. 比如输入 ls 命令,屏幕上将显示它的运行结果以及它的相关错误信息。
  4. 与 UNIX 系统一切都是文件的思想一致,类似 ls 的程序实际上把它们的运行结果发送到一个称为标准输出 (standard output,通常称为 stdout) 的特殊文件中,它们的状态信息则发送到另一个成为标准错误 (standard error,表示为 stderr) 的文件中。
  5. 默认情况下,标准输出和标准错误都将被链接到屏幕上,并且不会被保存在磁盘文件中。
  6. 另外,许多程序从一个称为标准输入 (standard input,表示为 stdin) 的设备来输入。默认情况下,标准输入连接到键盘。
  7. I/O 重定向功能可以改变输出内容发送的目的地,也可以改变输入内容的来源地。
  8. 通常来说,输出内容显示在屏幕上,输入内容来自于硬盘,但是使用 I/O 重定向功能可以改变这一惯例。

标准输出重定向

  1. I/O 重定向功能可以重新定义标准输出内容发送到哪里。
  2. 使用重定向操作符 > ,后面接文件名,就可以把标准输出重定向到另一个文件中,而不是显示在屏幕上。它主要用于把命令的输出内容保存到一个文件中
  3. 比如,我们可以按照下面的形式把 ls 命令的输出保存到 ls-output.txt 文件中,而不是输出到屏幕上
    1
    [me@linuxbox ~]$ ls -l /usr/bin > ls-output.txt
  4. 这里,我们将创建 /usr/bin 目录的一个长列表信息,并把这个结果输出到 ls-output.txt 文件中。
  5. 检查下该命令被重定向的输出内容
    1
    2
    [me@linuxbox ~]$ ls -l ls-output.txt
    -rw-rw-r-- 1 me me 167878 2012-02-01 15:07 ls-output.txt
  6. 如果使用 less 命令查看这个文件,我们可以看到 ls-output.txt 文件确定包含了 ls 命令的执行结果。
    1
    [me@linuxbox ~]$ less ls-output.txt
  7. 现在,让我们重复重定向测试,但是这次做一点变换。我们把目录名称换成一个不存在的目录。
    1
    2
    [me@linuxbox ~]$ ls -l /bin/usr > ls-output.txt
    ls: cannot access /bin/usr: No such file or directory
  8. 我们会收到一条错误信息。因为我们指定的是一个不存在的目录 /bin/usr/ ,所以这个错误信息是正确的。
  9. 这个错误信息显示在屏幕上,而不是重定向到 ls-output.txt 文件中的原因:ls 程序并不会把它运行的错误信息发送到标准输出文件中。
  10. 而是与大多数写得很好的 UNIX 程序一样,它把错误信息发送到标准错误文件中。因为我们只重定向了标准输出,并没有重定向标准错误,所以这个错误信息仍然输出在屏幕上。
  11. 我们首先看看这个输出文件发生的变化。
    1
    2
    [me@linuxbox ~]$ ls -l ls-output.txt
    -rw-rw-r-- 1 me me 0 2012-02-01 15:08 ls-output.txt
  12. 当前这个文件大小为零。这是因为当使用重定向符 > 来重定向标准输出时,目的文件通常会从文件开头部分重新改写。由于 ls 命令执行后没有输出任何内容,只显示一条错误信息,所以重定向操作开始重新改写这个文件,并在出现错误的情况下停止操作,最终导致了该文件内容被删除。
  13. 事实上,如果我们需要删除一个文件内容 (或创建一个新的空文件) ,可以采用这样的方式:
    1
    [me@linuxbox ~]$ > ls-output.txt
  14. 仅仅使用了重定向符,并在它之前不加任何命令,就可以删除一个已经存在的文件内容或者创建一个新的空文件。
  15. 如果我们想不从文件的首位置开始覆盖文件,而是从文件的尾部开始添加输出内容,可以使用重定向符 >> 来实现,比如:
    1
    [me@linuxbox ~]$ ls -l /usr/bin >> ls-output.txt
  16. 使用重定向符 >> 将使得输出内容添加在文件的尾部。如果这个文件并不存在,将与操作符 > 的作用一样创建这个文件。
    1
    2
    3
    4
    5
    [me@linuxbox ~]$ ls -l /usr/bin >> ls-output.txt
    [me@linuxbox ~]$ ls -l /usr/bin >> ls-output.txt
    [me@linuxbox ~]$ ls -l /usr/bin >> ls-output.txt
    [me@linuxbox ~]$ ls -l ls-output.txt
    -rw-rw-r-- 1 me me 503634 2012-02-01 15:45 ls-output.txt
  17. 重复执行这条命令三次,系统将最终生成一个为原来三倍大小的输出文件。

标准错误重定向

  1. 标准错误的重定向并不能简单地使用一个专用的重定向符来实现。要实现标准错误的重定向,不得不提到它的文件描述符 (file descriptor) 。
  2. 一个程序可以把生成的输出内容发送任意文件流中。
  3. 如果把这些文件流中的前三个分别对应标准输入文件、标准输出文件和标准错误文件,那么 shell 将在内部用文件描述符分别索引它们为 0 ,1 和 2 。
  4. shell 提供了使用文件描述编号来重定向文件的表示法。由于标准错误等同于文件描述符 2 ,所以可以使用这种表示法来重定向标准错误。
    1
    [me@linuxbox ~]$ ls -l /bin/usr 2> ls-error.txt
  5. 文件描述符 2 紧放在重定向符之前,将标准错误重定向到 ls-error.txt 文件中。

将标准输出和标准错误重定向到同一个文件

  1. 在许多情况下,我们会希望把一个命令的所有输出内容都放在同一个独立的文件中。为此,我们必须同时重定向标准输出和标准错误。
  2. 有两种方法可以满足要求,第一种是传统的方法,在旧版本的 shell 中使用。
    1
    [me@linuxbox ~]$ ls -l /bin/usr > ls-output.txt 2>&1
  3. 使用这种方法,将执行两个重定向操作。首先重定向标准输出和标准输出到 ls-output.txt 文件中,然后使用标记符 2>&1 把文件描述符 2(标准错误) 重定向到文件描述符 1(标准输出) 中。
  4. 注意:这些重定向操作的顺序是非常重要的。标准错误的重定向通常发生在标准输出重定向之后,否则它将不起作用。
  5. 在上面的例子中,>ls-output.txt 2>&1 把标准错误重定向到 ls-output.txt 文件中,但是如果顺序改变为 2>&1 >ls-output.txt ,那么标准错误将会重定向到屏幕上。
  6. 最近的 bash 版本提供了效率更高的第二种方法来实现这一联合的重定向操作。
    1
    [me@linuxbox ~]$ ls -l /bin/usr &> ls-output.txt
  7. 这个例子中,只使用一个标记符 &> 就把标准输出和标准错误都重定向到了 ls-output.txt 文件中。

处理不想要的输出

  1. 有时候沉默是金,命令执行后我们并不希望得到输出,而是想把这个输出丢弃,尤其是在输出错误和状态信息的情况下更为需要。
  2. 系统提供了一种方法,即通过把输出重定向到一个称为 /dev/null 的特殊文件中来实现它。
  3. 这个文件是一个称为位桶 (bit bucket) 的系统设备,它接受输入但是不对输入进行任何处理。
    1
    [me@linuxbox ~]$ ls -l /bin/usr 2> /dev/null

标准输入重定向

  1. cat - 合并文件
  • cat 命令读取一个或多个文件,并把它们复制到标准输出文件中,格式如下:
    1
    cat [file...]
  • 在大多数情况下,你可以认为 cat 命令和 DOS 中的 TYPE 命令类似。使用它显示文件而不需要分页,例如:
    1
    [me@linuxbox]$ cat ls-output.txt
  • 上面命令将显示 ls-output.txt 文件的内容。cat 经常用来显示短的文本文件
  • 由于 cat 可以接受多个文件作为输入参数,所以它也可以用来把文件连接在一起
  • 假设我们下载了一个很大的文件,它已被拆分为多个部分 (Usenet 上的多媒体文件经常采用拆分是这种方式) ,现在我们想要把各部分连接在一起,并还原为原来文件
  • 如果这些文件命名为 moive.mpeg.001 movie.mpeg.002…movie.mpeg.099 ,我们可以使用这个命令让它们重新连接在一起
    1
    [me@linuxbox ~]$ cat movie.mpeg.0* > movie.mpeg
  • 通配符一般都是按照顺序来扩展的,因此这些参数将正确的顺序来排序
  • 如果输入 cat 命令却不带任何参数,它将从标准输入读取内容。由于标准输入在默认情况下是连接到键盘,所以实际上它正在等待着从键盘输入内容
    1
    2
    [me@linuxbox ~]$ cat
    The quick brown fox jumped over the lazy dog.
  • 下一步,按下 Ctrl-D (按住 Ctrl 键同时按下 D) ,告知 cat 命令它已经达到了标准输入的文件尾 (end-of-file, EOF)
    1
    2
    3
    [me@linuxbox ~]$ cat
    The quick brown fox jumped over the lazy dog.
    The quick brown fox jumped over the lazy dog.
  • 在缺少文件名参数的情况下,cat 把标准输入内容复制到标准输出文件中,因此我们将可以看到文本行重复显示。用这种方法我们可以创建短的文本文件
  • 如果想要创建一个名叫 lazy_dog.txt 的文件,文件中包含之前例子中的文本内容,我们可以这样做:
    1
    2
    [me@linuxbox ~]$ cat > lazy_dog.txt
    The quick brown fox jumped over the lazy dog.
  • 在 cat 命令后输入想要放在文件中的文本内容。记住在文件结束时按下 Ctrl-D 。为了看到结果,我们可以使用 cat 命令再次把文件复制到标准输出文件中
    1
    2
    [me@linuxbox ~]$ cat lazy_dog.txt
    The quick brown fox jumped over the lazy dog.
  • 现在我们已经知道 cat 命令除了接受文件名参数之外,是如何接受标准输入的
    1
    2
    [me@linuxbox ~]$ cat < lazy_dog.txt
    The quick brown fox jumped over the lazy dog.
  • 使用重定向符 < ,我们把标准输入的源从键盘变成 lazy_dog.txt 文件
  • 可以看到,得到的结果和只传递单个文件名参数结果一样。和传输一个文件名参数的方式作对比,这种方式并不是特别有用,但是可以用来说明把一个文件作为标准输入的源文件

管道

基本介绍

  1. 命令从标准输入到读取数据,并将数据发送到标准输出的能力,是使用了名为管道的 shell 特性。
  2. 使用管道操作符 | (竖线) 可以把一个命令的标准输出传送到另一个命令的标准输入中。
    1
    Command1 | Command2
  3. 使用 less 命令可以分页显示任意命令的输入,该命令将它的结果发送到标准输出。
    1
    [me@linuxbox ~]$ ls -l /usr/bin | less
  4. 通过使用该技术,可以很方便地检查任意一条生成标准输出的命令的运行结果。

过滤器

  1. 管道功能经常用来对数据执行复杂的操作。也可以多条命令合在一起构成一个管道。这种方式中用到的命令通常被称为过滤器 (filter) 。
  2. 过滤器接受输入,按照某种方法对输入进行改变,然后输出它。
  3. 第一个要用到的命令是 sort 。假设要把 /bin 和 /usr/bin 目录下的所有可执行程序合并成一个列表,并且按照顺序排列,最后再查看这个列表。
    1
    [me@linuxbox ~]$ ls /bin /usr/bin | sort | less
  4. 由于我们指定了两个目录 (/bin 和 /usr/bin) , ls 的输出将包含两个排好序的列表,每个对应一个目录。
  5. 通过在管道中包含 sort 命令,我们改变输出数据,从而产生一个排好序的列表。

uniq - 报告或忽略文件中的重复行

  1. uniq 命令经常和 sort 命令结合使用。
  2. uniq 可以接受来自于标准输入或者一个单一文件名参数对应的已排好序的数据列表 (可以查看 uniq 命令的 man 页面获取详细信息) 。默认情况下,该命令删除列表中的所有重复行。
  3. 因此,在管道中添加 uniq 命令,可以确保所有列表都没有重复行(即在 /bin 和 /usr/bin 目录下都出现的相同名字的任意程序)。
    1
    [me@linuxbox ~]$ ls /bin /usr/bin | sort| uniq | less
  4. 在这个例子中,我们使用了 uniq 命令来删除来自 sort 命令输出内容中的任意重复行。
  5. 如果反过来想要查看重复行的列表,可以再 uniq 命令后面添加 -d 选项,如下所示。
    1
    [me@linuxbox ~]$ ls /bin /usr/bin | sort| uniq -d | less

wc - 打印行数、字数和字节数

  1. wc (字数,word count) 命令用来显示文件中包含的行数、字数和字节数。例如:
    1
    2
    [me@linuxbox ~]$ wc ls-output.txt
    7902 64566 503634 ls-output.txt
  2. 上面例子中,我们打印输出了三个数据,即 ls-output.txt 文件中包含的行数、字数和字节数。
  3. 和前面的命令一样,如果在执行 wc 时没有输入命令行参数,它将接受标准输入内容。
  4. -l 选项限制命令只报告行数,把它添加在管道中可以很方便地实现计数功能。
  5. 如果我们要查看已排序的列表中的条目数,可以按照以下方式输入。
    1
    [me@linuxbox ~]$ ls /bin /usr/bin | sort| uniq | wc -l

grep - 打印匹配行

  1. grep 是一个功能强大的程序,它用来在文件中查找匹配文本,其使用方式如下:
    1
    grep pattern [file...]
  2. 当 grep 在文件中遇到模式的时候,就打印出包含该模式的行。
  3. grep 能够匹配的模式内容可以是非常复杂的,不过这里,我们只关注简单文本的匹配。
  4. 如果想我们从列出的程序中搜索出文件中包含 zip 的所有文件,该搜索将获悉系统中与文件压缩的相关程序,操作如下。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [me@linuxbox ~]$ ls /bin /usr/bin | sort| uniq | grep zip
    bunzip2
    bzip2
    gunzip
    gzip
    unzip
    zip
    zipcloak
    zipgrep
    zipinfo
    zipnote
    zipsplit
  5. grep 存在一对的选项:-i ,该选项使得 grep 在搜索时忽略大小写 (通常情况下,搜索区分大小写) ;-v ,该选项使得 grep 只是输出和模式不匹配的行。

head/tail - 打印文件的开头部分

  1. 有的时候,你并不需要命令输出的所有内容,可能只是需要开头几行或者最后几行。
  2. head 命令将输出文件的前 10 行,tail 命令则输出文件的最后 10 行。默认情况下,这两条命令都是输出文件的 10 行内容,不过可以使用 -n 选项来调整输出的行数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [me@linuxbox ~]$ head -n 5 ls ls-output.txt
    total 343496
    -rwxr-xr-x 1 root root 31316 2011-12-05 08:58 [
    -rwxr-xr-x 1 root root 8240 2011-12-09 13:39 411toppm
    -rwxr-xr-x 1 root root 111276 2011-11-26 14:27 a2p
    -rwxr-xr-x 1 root root 25368 2010-10-06 20:16 a52dec
    [me@linuxbox ~]$ tail -n 5 ls-output.txt
    -rwxr-xr-x 1 root root 5234 2011-06-27 10:56 znew
    -rwxr-xr-x 1 root root 691 2009-09-10 04:21 zonetab2pot.py
    -rw-r--r-- 1 root root 930 2011-11-01 12:23 zonetab2pot.pyc
    -rw-r--r-- 1 root root 930 2011-11-01 12:23 zonetab2pot.pyo
    1rwxrwxrwx 1 root root 6 2012-01-31 05:22 zsoelim -> soelim
  3. 这些命令选项也可以应用在管道中。
    1
    2
    3
    4
    5
    [me@linuxbox ~]$ ls /usr/bin | tail -n 5
    zonetab2pot.py
    zonetab2pot.pyc
    zonetab2pot.pyo
    zsoelim
  4. tail 中有一个选项用来实时查看文件,该选项在观察正在被写入的日志文件的进展状态时很有用。
  5. 在下面的例子中,我们将观察 /var/log 目录下的 messages 文件。
  6. 因为 /var/log/messages 文件可能包含安全信息,所以在一些 Linux 发行版本中,需要有超级用户的权限才能执行该操作。
    1
    [me@linuxbox ~]$ tail -f /var/log/messages
  7. 使用 -f 选项,tail 将持续监视这个文件,一旦添加了新行,新行就会立即显示在屏幕上。该动作在按下 Ctrl-c 命令后停止。

tee - 从 stdin 读取数据,并同时输出到 stdout 和文件

  1. 为了和我们的管道隐喻保持一致,Linux 提供了一个叫做 tee 的命令。就好像安装了一个 T 在管道上。
  2. tee 程序读取标准输入,再把读到的内容复制到标准输出 (允许数据可以继续向下传递到管理中) 和一个或多个的文件中去。
  3. 当在某个中间处理阶段来捕获一个管道中的内容时,会很有用。我们使用 tee 命令来获取整个目录列表并输出到 ls.txt 文件中,具体操作如下:
    1
    2
    3
    4
    5
    6
    7
    8
    [me@linuxbox ~]$ ls /usr/bin | tee ls.txt | grep zip
    bunzip2
    bzip2
    gunzip
    gzip
    unzip
    zip
    zipcloak

参考文章

  • 转载:Linux 命令行大全