Linux 管道和重定向

发布于 2024-02-20  330 次阅读


Piping and Redirection!

Keeping the data flowing

了解使用管道和重定向创建功能强大的工作流有多容易,这些工作流将使您的工作自动化,为您节省时间和精力。
尽管这些机制及其使用非常简单,但如果你想有效地使用它们,了解它们行为的各种特征是很重要的。

我们在命令行上运行的每个程序都会自动连接三个数据流。

  • STDIN(0)-标准输入(输入程序的数据)
  • STDOUT(1)-标准输出(程序打印的数据,默认为终端)
  • STDERR(2)-标准错误(对于错误消息,也默认为终端)

管道和重定向是我们可以在程序和文件之间连接这些流以以有趣和有用的方式引导数据的手段。
我们将在下面用几个示例演示管道和重定向,但这些机制将适用于命令行上的每个程序,而不仅仅是我们在示例中使用的程序。

通常,我们会在屏幕上获得输出,这在大多数时候都很方便,但有时我们可能希望将其保存到文件中作为记录保存,输入到另一个系统,或发送给其他人。大于运算符(>)向命令行指示,我们希望将程序输出(或它发送到STDOUT的任何内容)保存在文件中,而不是打印到屏幕上。让我们看一个例子。

user@bash: ls
barry.txt bob example.png firstfile foo1 video.mpeg 
user@bash: ls > myoutput
user@bash: ls
barry.txt bob example.png firstfile foo1 myoutput video.mpeg
user@bash: cat myoutput
barry.txt
bob
example.png
firstfile
foo1
myoutput
video.mpeg
user@bash:

让我们来分解一下:

  • 第1行让我们从查看当前目录中的内容开始。
  • 第3行现在我们将运行相同的命令,但这次我们使用>来告诉终端将输出保存到myoutput文件中。您会注意到,在保存到文件之前,我们不需要创建文件。如果文件不存在,终端会自动创建它。
  • 第4行如您所见,我们的新文件已经创建。
  • 第6行让我们看看里面保存了什么。

一些观察

您会注意到,在上面的例子中,文件中保存的输出是每行一个文件,而不是打印到屏幕上时一行所有文件。这样做的原因是屏幕的宽度是已知的,程序可以根据需要对其输出进行格式化。当我们重定向时,它可能是到一个文件,也可能是在其他地方,所以最安全的选择是将其格式化为每行一个条目。这也使我们稍后可以更容易地操作这些数据,正如我们将在页面下方看到的那样。

管道和重定向时,实际数据将始终相同,但该数据的格式可能与通常打印到屏幕上的格式略有不同。记住这一点。

您还会注意到,我们创建的用于保存数据的文件也在我们的列表中。该机制的工作方式是,首先创建文件(如果尚不存在),然后运行程序并将输出保存到文件中。

如果我们重定向到一个不存在的文件,它将会自动为我们创建。然而,如果我们保存到一个已经存在的文件中,那么它的内容将被清空,然后新的输出将被保存到其中。

user@bash: cat myoutput
barry.txt
bob
example.png
firstfile
foo1
myoutput
video.mpeg
user@bash: wc -l barry.txt > myoutput
user@bash: cat myoutput
7 barry.txt
user@bash: 

我们可以使用双大于号运算符(>>)将新数据追加到文件中。

user@bash: cat myoutput
7 barry.txt
user@bash: ls >> myoutput
user@bash: cat myoutput
7 barry.txt
barry.txt
bob
example.png
firstfile
foo1
myoutput
video.mpeg
user@bash: 

如果我们使用小于运算符(<),那么我们可以将数据发送到另一个地方。我们将从文件中读取数据,并通过其标准输入流将其输入程序。

user@bash: wc -l myoutput
8 myoutput
user@bash: wc -l < myoutput
8
user@bash: 

许多程序(正如我们在前面的部分中看到的)允许我们将文件作为命令行参数提供,然后读取和处理该文件的内容。鉴于此,您可能会问为什么我们需要使用这个运算符。上面的例子说明了一个微妙但有用的区别。您会注意到,当我们运行 wc 并提供要处理的文件作为命令行参数时,程序的输出包括被处理的文件的名称。当我们将文件的内容重定向到 wc 时,文件名不会被打印。这是因为每当我们使用重定向或管道时,数据都是匿名发送的。因此,在上面的例子中,wc 接收到一些要处理的内容,但它不知道这些内容来自哪里,因此可能不会打印这些信息。因此,通常使用这种机制是为了使不需要打印的辅助数据不被打印。

我们可以很容易地将我们迄今为止看到的两种重定向形式合并为一个单一的命令,就像下面的例子所示。

user@bash: wc -l < barry.txt > myoutput
user@bash: cat myoutput
7
user@bash: 

现在让我们看看第三个流,即标准错误或STDERR。这三个流实际上与它们相关联的数字(在页面顶部的列表中用括号括起来)。STDERR是流号2,我们可以使用这些数字来识别流。如果我们在>运算符之前放置一个数字,那么它将重定向该流(如果我们不使用数字,就像我们迄今为止一直在做的那样,那么它默认为流1)。

user@bash: ls -l video.mpg blah.foo
ls: cannot access blah.foo: No such file or directory
-rwxr--r-- 1 ryan users 6 May 16 09:14 video.mpg
user@bash: ls -l video.mpg blah.foo 2> errors.txt
-rwxr--r-- 1 ryan users 6 May 16 09:14 video.mpg
user@bash: cat errors.txt
ls: cannot access blah.foo: No such file or directory
user@bash: 

也许我们希望将正常输出和错误消息保存到一个文件中。这可以通过将 STDERR 流重定向到 STDOUT 流并将 STDOUT 重定向到文件来实现。我们首先重定向到文件,然后重定向错误流。通过在流号前面放置一个 & 来识别重定向到流(否则它将重定向到一个名为 1 的文件)。

user@bash: ls -l video.mpg blah.foo > myoutput 2>&1
user@bash: cat myoutput
ls: cannot access blah.foo: No such file or directory
-rwxr--r-- 1 ryan users 6 May 16 09:14 video.mpg
user@bash: 

到目前为止,我们已经处理了与文件之间的数据传输。现在我们将看一下一种从一个程序发送数据到另一个程序的机制。这被称为管道,我们使用的运算符是( | )(在大多数键盘的反斜杠(\)键上方找到)。这个运算符的作用是将左侧程序的输出作为右侧程序的输入。在下面的示例中,我们将仅列出目录中的前3个文件。

user@bash: ls
barry.txt bob example.png firstfile foo1 myoutput video.mpeg
user@bash: ls | head -3
barry.txt
bob
example.png
user@bash: 

我们可以根据需要将任意数量的程序组合在一起。在下面的示例中,我们将输出管道传递给tail,以便只获取第三个文件。

user@bash: ls | head -3 | tail -1
example.png
user@bash: 
我们为程序提供的任何命令行参数必须紧跟在该程序后面。
我经常发现人们试图一次性写出他们的管道,但在某个地方出错了。他们认为问题出在一个地方,但实际上是另一个地方。他们浪费了很多时间试图修复一个不存在的问题,却没有看到真正存在的问题。如果你逐步构建你的管道,那么你就不会陷入这个陷阱。运行第一个程序,确保它提供了你期望的输出。然后添加第二个程序,再次检查,然后再添加第三个,依此类推。这将为你节省很多时间。

您也可以组合管道和重定向。

user@bash: ls | head -3 | tail -1 > myoutput
user@bash: cat myoutput
example.png
user@bash: 

以下是一些更多的例子,以便让你了解使用管道可以做的事情。使用管道可以实现许多事情,以下只是其中一部分。通过经验和一点创造性思维,我相信你会找到更多使用管道来让生活更轻松的方法。
示例中使用的所有程序都是我们以前见过的程序。但是,我使用了一些我们尚未涵盖的命令行参数。查找相关的手册页,了解它们的作用。您也可以自己尝试这些命令,逐步构建以准确查看每个步骤在做什么。

在这个例子中,我们对data进行ROT13的解密

user@bash: cat data.txt 
Gur cnffjbeq vf WIAOOSFzMjXXBC0KoSKBbJ8puQm5lIEi
user@bash: cat data.txt |tr 'a-zA-Z' 'n-za-mN-ZA-M'
The password is JVNBBFSmZwKKOP0XbFXOoW8chDz5yVRv
user@bash: 

在这个例子中,我们对目录的列表进行排序,以便所有目录都排在前面。

user@bash: ls -l /etc | tail -n +2 | sort
drwxrwxr-x 3 nagios nagcmd 4096 Mar 29 08:52 nagios
drwxr-x--- 2 news news 4096 Jan 27 02:22 news
drwxr-x--- 2 root mysql 4096 Mar 6 22:39 mysql
...
user@bash: 

在这个例子中,我们将将程序的输出输入到less程序中,以便更容易查看。

user@bash: ls -l /etc | less
(Full screen of output you may scroll. Try it yourself to see.)

识别您的主目录中所有组具有写权限的文件。

user@bash: ls -l ~ | grep '^.....w'
drwxrwxr-x 3 ryan users 4096 Jan 21 04:12 dropbox
user@bash: 

创建一个列出拥有给定目录中文件的每个用户以及他们拥有的文件和目录数量的清单。

user@bash: ls -l /projects/ghosttrail | tail -n +2 | sed 's/\s\s*/ /g' | cut -d ' ' -f 3 | sort | uniq -c
8 anne
34 harry
37 tina
18 ryan
user@bash: 
>
将输出保存到文件。
>>
将输出追加到文件。
<
从文件中读取输入。
2>
重定向错误消息。
|
将一个程序的输出作为输入发送到另一个程序。
在命令行上运行的每个程序都有3个流,STDIN、STDOUT和STDERR。
後悔しない
最后更新于 2024-03-01