内容


学习 Linux,101

流、管道和重定向

熟悉 Linux 管道

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 学习 Linux,101

敬请期待该系列的后续内容。

此内容是该系列的一部分:学习 Linux,101

敬请期待该系列的后续内容。

概述

本教程将介绍 Linux 中重定向标准 IO 流的基本技术。学习:

  • 重定向标准 IO 流:标准输入、标准输出和标准错误
  • 传输一个命令的输出作为另一个命令的输入
  • 将输出发送到 stdout 和文件
  • 使用命令输出作为另一个命令的参数

本教程可以帮助您针对 Linux Server Professional (LPIC-1) 考试 101 的主题 103 中的目标 103.4 进行应考准备。该目标的权重为 4。

输入、输出和流

Linux shell(比如 Bash)以字符序列或 的形式接收输入和发送输出。每个字符与它之前和之后的字符独立。字符未组织为结构化记录或固定大小的字符块。流使用了文件 IO 技术访问,无论是否是来自或传到文件、键盘、显式窗口或某种其他 IO 设备的实际字符流。Linux shell 使用 3 种标准 I/O 流,每种流与一种著名的文件描述符相关联:

  1. stdout标准输出流,它显示来自命令的输出。它拥有文件描述符 1。
  2. stderr标准错误流,它显示来自命令的错误输出。它拥有文件描述符 2。
  3. stdin标准输入流,它向命令提供输入。它拥有文件描述符 0。

输入流向程序提供输入,这些输入通常来自终端击键。输出流打印文本字符,通常打印到终端。终端最初为 ASCII 打字机或显示终端,但现在通常是图形桌面上的文本窗口。

如果您已学习了教程 “学习 Linux 101:文本流和过滤器”,那么您应该熟悉本教程中的一些内容。

前提条件

要从本系列教程中获得最大收获,您应该拥有 Linux 的基本知识和一个正常工作的 Linux 系统,您可以在这个系统上实践本教程中涵盖的命令。有时,程序的不同版本会得到不同的输出格式,所以您的结果可能并不总是与这里给出的清单和图完全相同。本教程中的示例使用了 Fedora 22,它们应适用于任何 Linux 系统。

设置示例

在本教程中,将使用教程 “学习 Linux 101:文本流和过滤器” 中创建的一些文件来练习这些命令。但是,如果您尚未阅读该教程,或者未保存所使用的文件,不必担心!首先在您的主目录中创建一个名为 lpi103-4 的新子目录,并在其中创建必要的文件。为此,可打开一个文本窗口,采用您的主目录作为当前目录。然后将 清单 1的内容复制并粘贴到该窗口中来运行相关命令,这些命令将会创建 lpi103-4 子目录和您将使用的文件。提示:在大多数 X 视窗 ®系统中,按住鼠标中键会将选定的文本粘贴在光标位置。选定的文本可位于同一个或另一个窗口中。

清单 1. 创建示例文件
mkdir -p lpi103-4 && cd lpi103-4 && { 
 echo -e "1 apple\n2 pear\n3 banana" > text1 
 echo -e "9\tplum\n3\tbanana\n10\tapple" > text2 
 echo "This is a sentence. " !#:* !#:1->text3 
 split -l 2 text1 
 split -b 17 text2 y 
 ls; }

您的窗口应类似于 清单 2,您的当前目录现在应该是新建的 lpi103-4 目录。

清单 2. 创建示例文件 - 输出
 [ian@atticf22 ~]$ mkdir -p lpi103-4 && cd lpi103-4 && {
 > echo -e "1 apple\n2 pear\n3 banana" > text1
 > echo -e "9\tplum\n3\tbanana\n10\tapple" > text2
 > echo "This is a sentence. " !#:* !#:1->text3
 echo "This is a sentence. " "This is a sentence. " "This is a sentence. ">text3
 > split -l 2 text1
 > split -b 17 text2 y
 > ls; }
 text1  text2  text3  xaa  xab  yaa  yab

重定向标准 IO

尽管标准输入和输出模型是发往和来自 ASCII 终端的顺序字符流,但您可能希望在文件中准备好输入数据,或者将输出或错误信息保存在文件中。这时就需要使用 重定向

重定向输出

可通过两种方式将输出重定向到文件:

n>
将来自文件描述符 n的输出重定向到某个文件。您必须拥有该文件的写权限。如果该文件不存在,则需要创建它。如果它已经存在,通常会毫无预兆地丢失现有内容。
n>>
也将来自文件描述符 n的输出重定向到某个文件。同样地,您必须拥有该文件的写权限。如果该文件不存在,则需要创建它。如果它已经存在,输出会被附加到现有文件。

n> 或 n>> 中的 n指的是 文件描述符。如果省略它,则会假设使用的是标准输出(文件描述符 1)。清单 3演示了如何使用重定向和您之前在 lpi103-4 目录中创建文件将 ls命令的标准输出和标准错误分开。其中还演示了如何将输出附加到现有文件。

清单 3. 输出重定向
 [ian@atticf22 lpi103-4]$ ls x* z*
 ls: cannot access z*: No such file or directory 
 xaa  xab 
 [ian@atticf22 lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt
 [ian@atticf22 lpi103-4]$ ls w* y*
 ls: cannot access w*: No such file or directory 
 yaa  yab 
 [ian@atticf22 lpi103-4]$ ls w* y* >>stdout.txt 2>>stderr.txt
 [ian@atticf22 lpi103-4]$ cat stdout.txt
 xaa 
 xab 
 yaa 
 yab 
 [ian@atticf22 lpi103-4]$ cat stderr.txt
 ls: cannot access z*: No such file or directory 
 ls: cannot access w*: No such file or directory

我说过使用 n> 的输出重定向通常会覆盖现有文件。您可以使用内建命令 setnoclobber选项来控制此行为。如果已设置它,可以使用 n>| 覆盖它,如 清单 4所示。

清单 4. 使用 noclobber 的输出重定向
 [ian@atticf22 lpi103-4]$ set -o noclobber
 [ian@atticf22 lpi103-4]$ ls x* z* >stdout.txt 2>stderr.txt
 bash: stdout.txt: cannot overwrite existing file 
 [ian@atticf22 lpi103-4]$ ls x* z* >|stdout.txt 2>|stderr.txt
 [ian@atticf22 lpi103-4]$ cat stdout.txt
 xaa 
 xab 
 [ian@atticf22 lpi103-4]$ cat stderr.txt
 ls: cannot access z*: No such file or directory 
 [ian@atticf22 lpi103-4]$ set +o noclobber #restore original noclobber setting

有时,您可能希望将标准输出和标准错误都重定向到一个文件中。通常,会对自动化流程或后台作业这么做,以便可以在以后检查输出。使用 &> 或 &>> 将标准输出和标准错误都重定向到同一个位置。另一种方法是重定向文件描述符 n,然后使用结构 m>&n 或 m>>&n 将文件描述符 m重定向到相同位置。重定向输出的顺序很重要。例如,
command 2>&1 >output.txt
不同于
command >output.txt 2>&1
在第一种情况下,stderr 被重定向到当前的 stdout 位置,然后将 stdout 重定向到 output.txt,但第二次重定向仅会影响 stdout,而不会影响 stderr。在第二种情况下,stderr 被重定向到当前的 stdout 位置,也就是 output.txt。我们在 清单 5中演示了这些重定向。请注意,在最后一个命令中,标准输出在标准错误之后重定向,所以标准错误仍输出到终端窗口。

清单 5. 将两个流重定向到一个文件
 [ian@atticf22 lpi103-4]$ ls x* z* &>output.txt 
 [ian@atticf22 lpi103-4]$ cat output.txt 
 ls: cannot access z*: No such file or directory 
 xaa 
 xab 
 [ian@atticf22 lpi103-4]$ ls x* z* >output.txt 2>&1 
 [ian@atticf22 lpi103-4]$ cat output.txt 
 ls: cannot access z*: No such file or directory 
 xaa 
 xab 
 [ian@atticf22 lpi103-4]$ ls x* z* 2>&1 >output.txt # stderr does not go to output.txt 
 ls: cannot access z*: No such file or directory 
 [ian@atticf22 lpi103-4]$ cat output.txt 
 xaa 
 xab

在其他时候,您可能希望完全忽略标准输出或标准错误。为此,可将合适的流重定向到空文件 /dev/null。清单 6展示了如何重定向来自 ls命令的错误输出,还使用 cat命令展示了 /dev/null 的确是空的。

清单 6. 使用 /dev/null 忽略输出
 [ian@atticf22 lpi103-4]$ ls x* z* 2>/dev/null
 xaa  xab 
 [ian@atticf22 lpi103-4]$ cat /dev/null

重定向输入

就像重定向 stdout 和 stderr 流一样,您还可以使用 < 运算符重定向来自文件的 stdin。如果已经学习了教程 “学习 Linux 101:文本流和过滤器”,您可能会想起,在对 sort 和 uniq 的讨论中,使用了 tr 命令将 text1 文件中的空格替换为制表符。在该示例中,使用了 cat 命令的输出来创建 tr 命令的标准输入。无需多余地调用 cat,您现在可以使用输入重定向将空格转换为制表符,如 清单 7所示。

清单 7. 输入重定向
 [ian@atticf22 lpi103-4]$ cat text1
 1 apple 
 2 pear 
 3 banana 
 [ian@atticf22 lpi103-4]$ tr ' ' '\t'<text1
 1 	 apple 
 2 	 pear 
 3 	 banana

Shell(包括 bash)也拥有 here-document概念,这是输入重定向的另一种形式。它将 << 和一个单词结合构成一个标记,比如与 END 结合来表示输入结束。清单 8演示了此概念,其中按第二个字段 (fruit name) 对输入进行了排序。

清单 8. 使用 here-document 的输入重定向
 [ian@atticf22 lpi103-4]$ sort -k2 <<END
 > 1 apple
 > 2 pear
 > 3 banana
 > END
 1 apple 
 3 banana 
 2 pear

您可能想知道能否仅输入 sort -k2,输入您的数据,然后按下 Ctrl-d来表示输入结束。简单答案是可以,您一定不熟悉 here-document。实际答案是 shell 脚本中通常使用 here-document。(脚本没有其他方式来表明应将那几行脚本视为输入。)因为 shell 脚本广泛使用制表符进行缩进来确保可读性,所以 here-document 还有另一个特征。如果使用 <<- 而不是 <<,则会删除前导制表符。

在 清单 9中,使用命令替换创建了一个强制制表符,然后创建了一段仅包含两个 cat命令的很短的 shell 脚本,每个命令从一个 here-document 读取信息。请注意,您使用 END 作为您从终端读取的 here-document 的标记。如果在脚本中使用同一个单词作为标记,则会导致过早结束输入。所以,可使用 EOF 代替。创建脚本后,使用 .(点号)命令 导入它,这意味着在当前 shell 上下文中运行它。

清单 9. 使用 here-document 的输入重定向
 [ian@atticf22 lpi103-4]$ ht=$(echo -en "\t")
 [ian@atticf22 lpi103-4]$ cat<<END>ex-here.sh
 > cat <<-EOF
 > apple
 > EOF
 > ${ht}cat <<-EOF
 > ${ht}pear
 > ${ht}EOF
 > END
 [ian@atticf22 lpi103-4]$ cat ex-here.sh
 cat <<-EOF 
 apple 
 EOF 
	 cat <<-EOF 
	 pear 
	 EOF 
 [ian@atticf22 lpi103-4]$ . ex-here.sh
 apple 
 pear

您将在本系列的后续教程中进一步学习命令替换和脚本编写。请参阅我们的 系列路线图,获得本系列中每篇教程的说明和链接。

创建管道

在教程 “学习 Linux 101:文本流和过滤器” 中,我介绍过 过滤是获取输入文本流并在文本上执行某种转换,然后将它发送到输出流的过程。通常,这种过滤通过构造命令 管道来完成,其中来自一个命令的输出 通过管道传输重定向而被用作下一个命令的输入。管道的这种使用方式并不仅限于文本流,但这是它们的常见用途。

通过管道将 stdout 传输到 stdin

您在两个命令之间使用 |(竖线)运算符,将第一个命令的 stdout 定向到第二个命令的 stdin。可通过添加更多命令和更多竖线运算符来构造更长的管道。任何命令都可以有选项或参数。许多命令使用连字符 (-) 代替文件名作为参数,以表明输入应来自 stdin 而不是文件。请检查命令的手册页进行确认。构造分别拥有有限功能的命令的长管道,是 Linux 和 UNIX® 完成任务的一种常见方式。在 清单 10中的虚构管道中,command2command3 都有一些参数,而 command3 使用 - 参数表明输入来自 stdin,还使用了其他一些虚构的参数。

清单 10. 通过管道将输出传经多个命令
command1 | command2 paramater1 | command3 parameter1 - parameter2 | command4

要注意的一点是,管道 将 stdout 传输到 stdin。您不能使用 2| 来单独传输 stderr,至少使用您目前了解的工具不能这么做。如果 stderr 已重定向到 stdout,两个流都将通过管道传输。清单 11演示了一个不太可能出现的 ls命令,它拥有 4 个未按字母顺序排列的通配符参数,然后使用一个管道对正常和错误输出组合进行排序。

清单 11. 通过管道传输两个输出流
 [ian@atticf22 lpi103-4]$ ls y* x* z* u* q*
 ls: cannot access z*: No such file or directory 
 ls: cannot access u*: No such file or directory 
 ls: cannot access q*: No such file or directory 
 xaa  xab  yaa  yab 
 [ian@atticf22 lpi103-4]$  ls y* x* z* u* q*  2>&1 |sort
 ls: cannot access q*: No such file or directory 
 ls: cannot access u*: No such file or directory 
 ls: cannot access z*: No such file or directory 
 xaa 
 xab 
 yaa 
 yab

这里有一点很有趣。如果将 ls输出到终端,输出将格式化为填满终端窗口的列数。如果它被重定向,则每行一个输出条目。这样就更容易对一组文件中的每一个执行进一步操作。

Linux 和 UNIX 系统中的管道的一个优点是,不同于其他一些流行的操作系统,管道不会涉及到中间文件。第一个命令的 stdout 不会写入到一个文件,然后由第二个命令读取。在教程 “学习 Linux,101:文件和目录管理” 中,我们学习了如何使用 tar命令在一步中存档和压缩文件。如果在您使用的 UNIX 系统上,tar命令不支持使用 -z(表示 gzip)或 -j(表示 bzip2)执行压缩,不用担心。您可以使用一个类似

bunzip2 -c somefile.tar.bz2 | tar -xvf -

的管道来执行该任务。

以一个文件而不是 stdout 作为管道开头

在以前的管道中,首先使用一个命令生成输出,然后将该输出传输到管道的每个阶段。如果您想以一个已存在的数据文件作为开头,该怎么办?许多命令接受 stdin 或一个文件作为输入,所以不用担心这些命令。接受来自 stdin 的输入并将输出发送到 stdout 的程序通常被称为 过滤器。如果您有一个需要使用来自 stdin 的输入的过滤器,您可能会考虑使用 cat命令将文件复制到 stdout,然后将它传输到命令,这种方法将会起效。但是,您可以对第一个命令使用输入重定向,然后将该命令的输出传输到管道的剩余部分,这是更常见的解决方案。只需使用 < 运算符将第一个命令的 stdin 重定向到您想要处理的文件。您会在下一节看到一个例子。

使用输出作为参数

在前面对管道的讨论中,您学习了如何获取一个命令的输出并用作另一个命令的输入。但是,假设您想使用一个命令的输出或一个文件的内容作为另一个命令的参数,而不是作为输入。管道无法完成此任务。三种常见的方法是:

  1. xargs命令
  2. -exec选项的 find命令
  3. 命令替换

现在来看看前两种方法。在 清单 9中创建强制制表符时,您已经看到了命令替换的例子。命令替换可用在命令行上,但更常用在脚本中;您将在本系列的后续教程中进一步了解它和脚本编写。请参阅我们的 系列路线图,获得本系列中每篇教程的说明和链接。

使用 xargs命令

xargs命令读取标准输入,然后使用该输入作为餐胡来构建和执行命令。如果未提供命令,则使用 echo命令。清单 12是一个使用您的 text1 文件的基本示例,该文件包含 3 行,每行两个单词。

清单 12. 使用 xargs
 [ian@atticf22 lpi103-4]$ cat text1
 1 apple 
 2 pear 
 3 banana 
 [ian@atticf22 lpi103-4]$ xargs < text1
 1 apple 2 pear 3 banana

那么为什么 xargs只有一行输出?默认情况下,xargs 会在空格处中断输入,而且得到的每个标记变成一个参数。但是,在 xargs 构建命令时,它会一次传递尽可能多的参数。您可以使用 -n--max-args 参数覆盖此行为。清单 13演示了两种形式的用法,并向 xargs中添加了对 echo的显式调用。

清单 13. 使用 xargsecho
 [ian@atticf22 lpi103-4]$ xargs<text1 echo "arg list:"
 arg list: 1 apple 2 pear 3 banana 
 [ian@atticf22 lpi103-4]$ xargs --max-args 3 <text1 echo "arg list:"
 arg list: 1 apple 2 
 arg list: pear 3 banana 
 [ian@atticf22 lpi103-4]$ xargs -n 1 <text1 echo "arg list:"
 arg list: 1 
 arg list: apple 
 arg list: 2 
 arg list: pear 
 arg list: 3 
 arg list: banana

如果输入包含放在单或双引号中的空格,或者通过反斜杠转义的空格,那么 xargs不会在这些位置中断输入。清单 14演示了此用法。

清单 14. 结合使用 xargs和引号
 [ian@atticf22 lpi103-4]$ echo '"4 plum"' | cat text1 -
 1 apple 
 2 pear 
 3 banana 
"4 plum"
 [ian@atticf22 lpi103-4]$ echo '"4 plum"' | cat text1 - | xargs -n 1
 1 
 apple 
 2 
 pear 
 3 
 banana 
 4 plum

目前为止,所有参数都被添加到命令的末尾。如果您需要将它们用作参数且后跟其他参数,可以使用 -I选项指定一个替换字符串。只要您要求 xargs执行的命令中出现替换字符串,它就会被替换为一个参数。这么做时,仅会将一个参数传递给每个命令。但是,该参数是使用整行输入创建的,而不是单个标记。也可以使用 xargs-L选项,让它将各行视为参数,而不是视为空格分隔的单独标记(默认情况)。使用 -I选项表示 -L 1。清单 15给出了使用 -I-L的例子。

清单 15. 使用 xargs和多行输入
 [ian@atticf22 lpi103-4]$ xargs -I XYZ echo "START XYZ REPEAT XYZ END" <text1
 START 1 apple REPEAT 1 apple END 
 START 2 pear REPEAT 2 pear END 
 START 3 banana REPEAT 3 banana END 
 [ian@atticf22 lpi103-4]$ xargs -IX echo "<X><X>" <text2
 <9 	 plum><9 	 plum> 
 <3 	 banana><3 	 banana> 
 <10 	 apple><10 	 apple> 
 [ian@atticf22 lpi103-4]$ cat text1 text2 | xargs -L2
 1 apple 2 pear 
 3 banana 9 plum 
 3 banana 10 apple

我们的例子使用了简单的文本文件来演示我们介绍的内容,但您可能很少希望对 xargs使用这样的输入。通常,您会处理一个命令(比如 lsfindgrep)生成的庞大的文件列表。清单 16展示了一种通过 xargs将目录清单传递给命令(比如 grep)的方式。

清单 16. 使用 xargs和文件列表
 [ian@atticf22 lpi103-4]$ ls |xargs grep "1"
 text1:1 apple 
 text2:10 	 apple 
 xaa:1 apple 
 yaa:1

在上一个例子中,如果一个或多个文件名包含空格,会发生什么?如果像在 清单 16中一样使用该命令,则会获得一个错误。实际上,您的文件列表可能来自自定义脚本或命令等来源,而不是 ls,或者您可能希望将它传递给其他一些管道阶段来进一步过滤,所以我们将忽略可以使用 grep "1" *代替此结构的事实。

对于 ls命令,您可以使用 --quoting-style选项强制给问题文件名称加上引号或进行转义。一个更好的解决方案(如果可用)是使用 xargs命令的 -0选项,这样会使用 null 字符 (\0) 来分隔输入参数。尽管 ls没有提供 null 分隔文件名作为输出的选项,但许多命令都会这样做。

清单 17首先将 text1 复制到 "text 1",然后展示了一些结合使用包含空格的文件名列表和 xargs的方式。这些示例仅用于演示概念,因为 xargs很难掌握。具体地讲,如果一些文件名已包含换行符,最后一个将换行符转换为 null 的例子将不起作用。在本教程的下一部分中,将了解 一个使用 find命令生成合适的 null 分隔输出的更稳健的解决方案。

清单 17. 使用 xargs和包含空格的文件名
 [ian@atticf22 lpi103-4]$ cp text1 "text 1"
 [ian@atticf22 lpi103-4]$ ls *1 |xargs grep "1" # error
 text1:1 apple 
 grep: text: No such file or directory 
 grep: 1: No such file or directory 
 [ian@atticf22 lpi103-4]$ ls --quoting-style escape *1
 text1  text\ 1 
 [ian@atticf22 lpi103-4]$ ls --quoting-style shell *1
 text1  'text 1'
 [ian@atticf22 lpi103-4]$ ls --quoting-style shell *1 |xargs grep "1"
 text1:1 apple 
 text 1:1 apple 
 [ian@atticf22 lpi103-4]$ # Illustrate -0 option of xargs
 [ian@atticf22 lpi103-4]$ ls *1 | tr '\n' '\0' |xargs -0 grep "1"
 text1:1 apple 
 text 1:1 apple

xargs命令不会构建任意长度的命令。在 Linux 内核版本 2.26.3 之前,命令的最大长度是有限制的。对于一个包含大量具有长名称的文件的目录,rm somepath/*这样的命令可能失败,并抛出一条表明参数列表太长的消息。在更低版本的 Linux 系统或可能仍有限制的 UNIX 系统上,了解如何使用 xargs来处理这些情形可能很有用。

您可以使用 --show-limits选项显示 xargs的默认限制,使用 -s选项将输出命令的大小限制到特定的最大字符数。请参阅手册页,了解这里未介绍的其他命令。

结合使用 find命令和 -exec选项或 xargs

在教程 “学习 Linux,101:文件和目录管理” 中,您将学习如何使用 find命令按名称、修改时间、大小或其他特征来查找文件。找到这样一组文件后,您通常希望对它们进行某种处理:删除它们,将它们复制到另一个位置,重命名它们,或者执行其他某种操作。现在看看 find-exec选项,它的功能类似于使用 find并将输出传输到 xargs

清单 18. 结合使用 find-exec
 [ian@atticf22 lpi103-4]$ find text[12] -exec cat text3 {} \;
 This is a sentence.  This is a sentence.  This is a sentence. 
 1 apple 
 2 pear 
 3 banana 
 This is a sentence.  This is a sentence.  This is a sentence. 
 9 	 plum 
 3 	 banana 
 10 	 apple

与您已学到的 xargs用法相比,这里存在着一些不同:

  1. 必须包含 {} 来标记文件名在命令中的位置。它不会自动添加到末尾。
  2. 您必须使用分号终止命令,而且分号必须转义;\;、';' 或 ";" 都可以使用。
  3. 该命令对每个输入文件执行一次。

尝试运行 find text[12] |xargs cat text3来亲自了解这些区别。

现在,让我们返回到文件名包含空格的情况。在 清单 19中,您尝试结合使用 find-exec,而不是 lsxargs

清单 19. 结合使用 find-exec和包含空格的文件名
 [ian@atticf22 lpi103-4]$ find . -name "*1" -exec grep "1" {} \;
 1 apple 
 1 apple

目前为止,一切顺利。是否遗漏了什么?哪些文件包含 grep找到的行?文件名遗漏了,因为 find对每个文件调用 grep一次,而且 grep能够聪明地知道,如果您仅向它提供一个文件名,就不再需要它告诉您该文件是什么。

您可以使用 xargs代替,但您已看到文件名包含空格的问题。我也暗示过,事实上 find可以生成一个使用 null 作为分隔符的文件名列表,这需要使用 -print0选项。现代 find 版本可能使用 + 符号代替分号来进行分隔,这会导致 find 在一次命令调用中传递尽可能多的名称,类似于 xargs的工作方式。无需说,在这种情况下仅能使用 {} 一次,而且它必须是命令的最后一个参数。清单 20展示了两种方法。

清单 20. 结合使用 findxargs和包含空格的文件名
 [ian@atticf22 lpi103-4]$ find . -name "*1" -print0 |xargs -0 grep "1"
 ./text1:1 apple 
 ./text 1:1 apple 
 [ian@atticf22 lpi103-4]$ find . -name "*1" -exec grep "1" {} +
 ./text1:1 apple 
 ./text 1:1 apple

一般而言,两种方法都有效,但具体采用哪种方法选择取决于个人风格。请记住,传输包含未受保护的空格或空白的内容可能导致问题,所以如果将输出传输到 xargs,请使用 find-print0选项,并使用 -0选项告诉 xargs要求采用以 null 分隔的输入。其他命令(包括 tar)也使用 -0选项来支持 null 分隔的输入,所以您应始终对支持它的命令使用该选项,除非您确定输入列表不会导致问题。

最后介绍一下对文件列表的操作。在提交给批量操作(比如删除或重命名文件)之前,全面测试您的列表并非常仔细地测试您的命令始终是个不错的想法。拥有良好的备份也很重要。

拆分输出

最后,本节将简单讨论一下另一个命令。有时您可能想在屏幕上查看输出,同时保存某个副本供以后使用。尽管 可以通过将命令输出重定向到窗口中的某个文件,然后使用 tail -fn1命令在另一个屏幕中跟踪输出来实现此目的,但使用 tee命令更容易。

可以将 tee与管道结合使用。对标准输出而言,参数是一个(或多个)文件。-a选项会附加而不是覆盖文件。在之前对管道的讨论中您已看到,如果想要同时保存 stderr 和 stdout,需要在传输到 tee之前将 stderr 重定向到 stdout。清单 21展示了如何使用 tee将输出保存到两个文件 f1 和 f2。

清单 21. 使用 tee 拆分 stdout
 [ian@atticf22 lpi103-4]$ ls text[1-3]|tee f1 f2
 text1 
 text2 
 text3 
 [ian@atticf22 lpi103-4]$ cat f1
 text1 
 text2 
 text3 
 [ian@atticf22 lpi103-4]$ cat f2
 text1 
 text2 
 text3

对流、管道和重定向的介绍到此就结束了。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux
ArticleID=1036395
ArticleTitle=学习 Linux,101: 流、管道和重定向
publish-date=08232016