Merge pull request #12597 from zero-MK/master

translated
This commit is contained in:
Xingyu.Wang 2019-03-03 07:51:58 +08:00 committed by GitHub
commit 10543b9801
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 162 additions and 162 deletions

View File

@ -1,162 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Ampersands and File Descriptors in Bash)
[#]: via: (https://www.linux.com/blog/learn/2019/2/ampersands-and-file-descriptors-bash)
[#]: author: (Paul Brown https://www.linux.com/users/bro66)
Ampersands and File Descriptors in Bash
======
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/ampersand-coffee.png?itok=yChaT-47)
In our quest to examine all the clutter (`&`, `|`, `;`, `>`, `<`, `{`, `[`, `(`, ), `]`, `}`, etc.) that is peppered throughout most chained Bash commands, [we have been taking a closer look at the ampersand symbol (`&`)][1].
[Last time, we saw how you can use `&` to push processes that may take a long time to complete into the background][1]. But, the &, in combination with angle brackets, can also be used to pipe output and input elsewhere.
In the [previous tutorials on][2] [angle brackets][3], you saw how to use `>` like this:
```
ls > list.txt
```
to pipe the output from `ls` to the _list.txt_ file.
Now we see that this is really shorthand for
```
ls 1> list.txt
```
And that `1`, in this context, is a file descriptor that points to the standard output (`stdout`).
In a similar fashion `2` points to standard error (`stderr`), and in the following command:
```
ls 2> error.log
```
all error messages are piped to the _error.log_ file.
To recap: `1>` is the standard output (`stdout`) and `2>` the standard error output (`stderr`).
There is a third standard file descriptor, `0<`, the standard input (`stdin`). You can see it is an input because the arrow (`<`) is pointing into the `0`, while for `1` and `2`, the arrows (`>`) are pointing outwards.
### What are the standard file descriptors good for?
If you are following this series in order, you have already used the standard output (`1>`) several times in its shorthand form: `>`.
Things like `stderr` (`2`) are also handy when, for example, you know that your command is going to throw an error, but what Bash informs you of is not useful and you don't need to see it. If you want to make a directory in your _home/_ directory, for example:
```
mkdir newdir
```
and if _newdir/_ already exists, `mkdir` will show an error. But why would you care? (Ok, there some circumstances in which you may care, but not always.) At the end of the day, _newdir_ will be there one way or another for you to fill up with stuff. You can supress the error message by pushing it into the void, which is _/dev/null_ :
```
mkdir newdir 2> /dev/null
```
This is not just a matter of " _let's not show ugly and irrelevant error messages because they are annoying,_ " as there may be circumstances in which an error message may cause a cascade of errors elsewhere. Say, for example, you want to find all the _.service_ files under _/etc_. You could do this:
```
find /etc -iname "*.service"
```
But it turns out that on most systems, many of the lines spat out by `find` show errors because a regular user does not have read access rights to some of the folders under _/etc_. It makes reading the correct output cumbersome and, if `find` is part of a larger script, it could cause the next command in line to bork.
Instead, you can do this:
```
find /etc -iname "*.service" 2> /dev/null
```
And you get only the results you are looking for.
### A Primer on File Descriptors
There are some caveats to having separate file descriptors for `stdout` and `stderr`, though. If you want to store the output in a file, doing this:
```
find /etc -iname "*.service" 1> services.txt
```
would work fine because `1>` means " _send standard output, and only standard output (NOT standard error) somewhere_ ".
But herein lies a problem: what if you *do* want to keep a record within the file of the errors along with the non-erroneous results? The instruction above won't do that because it ONLY writes the correct results from `find`, and
```
find /etc -iname "*.service" 2> services.txt
```
will ONLY write the errors.
How do we get both? Try the following command:
```
find /etc -iname "*.service" &> services.txt
```
... and say hello to `&` again!
We have been saying all along that `stdin` (`0`), `stdout` (`1`), and `stderr` (`2`) are _file descriptors_. A file descriptor is a special construct that points to a channel to a file, either for reading, or writing, or both. This comes from the old UNIX philosophy of treating everything as a file. Want to write to a device? Treat it as a file. Want to write to a socket and send data over a network? Treat it as a file. Want to read from and write to a file? Well, obviously, treat it as a file.
So, when managing where the output and errors from a command goes, treat the destination as a file. Hence, when you open them to read and write to them, they all get file descriptors.
This has interesting effects. You can, for example, pipe contents from one file descriptor to another:
```
find /etc -iname "*.service" 1> services.txt 2>&1
```
This pipes `stderr` to `stdout` and `stdout` is piped to a file, _services.txt_.
And there it is again: the `&`, signaling to Bash that `1` is the destination file descriptor.
Another thing with the standard file descriptors is that, when you pipe from one to another, the order in which you do this is a bit counterintuitive. Take the command above, for example. It looks like it has been written the wrong way around. You may be reading it like this: " _pipe the output to a file and then pipe errors to the standard output._ " It would seem the error output comes to late and is sent when `1` is already done.
But that is not how file descriptors work. A file descriptor is not a placeholder for the file, but for the _input and/or output channel_ to the file. In this case, when you do `1> services.txt`, you are saying " _open a write channel to services.txt and leave it open_ ". `1` is the name of the channel you are going to use, and it remains open until the end of the line.
If you still think it is the wrong way around, try this:
```
find /etc -iname "*.service" 2>&1 1>services.txt
```
And notice how it doesn't work; notice how errors get piped to the terminal and only the non-erroneous output (that is `stdout`) gets pushed to `services.txt`.
That is because Bash processes every result from `find` from left to right. Think about it like this: when Bash gets to `2>&1`, `stdout` (`1`) is still a channel that points to the terminal. If the result that `find` feeds Bash contains an error, it is popped into `2`, transferred to `1`, and, away it goes, off to the terminal!
Then at the end of the command, Bash sees you want to open `stdout` as a channel to the _services.txt_ file. If no error has occurred, the result goes through `1` into the file.
By contrast, in
```
find /etc -iname "*.service" 1>services.txt 2>&1
```
`1` is pointing at `services.txt` right from the beginning, so anything that pops into `2` gets piped through `1`, which is already pointing to the final resting place in `services.txt`, and that is why it works.
In any case, as mentioned above `&>` is shorthand for " _both standard output and standard error_ ", that is, `2>&1`.
This is probably all a bit much, but don't worry about it. Re-routing file descriptors here and there is commonplace in Bash command lines and scripts. And, you'll be learning more about file descriptors as we progress through this series. See you next week!
--------------------------------------------------------------------------------
via: https://www.linux.com/blog/learn/2019/2/ampersands-and-file-descriptors-bash
作者:[Paul Brown][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://www.linux.com/users/bro66
[b]: https://github.com/lujun9972
[1]: https://www.linux.com/blog/learn/2019/2/and-ampersand-and-linux
[2]: https://www.linux.com/blog/learn/2019/1/understanding-angle-brackets-bash
[3]: https://www.linux.com/blog/learn/2019/1/more-about-angle-brackets-bash

View File

@ -0,0 +1,162 @@
[#]: collector: "lujun9972"
[#]: translator: "zero-mk "
[#]: reviewer: " "
[#]: publisher: " "
[#]: url: " "
[#]: subject: "Ampersands and File Descriptors in Bash"
[#]: via: "https://www.linux.com/blog/learn/2019/2/ampersands-and-file-descriptors-bash"
[#]: author: "Paul Brown https://www.linux.com/users/bro66"
Bash中的符号和文件描述符
======
![](https://www.linux.com/sites/lcom/files/styles/rendered_file/public/ampersand-coffee.png?itok=yChaT-47)
在我们寻求检查所有的clutter`&``|``;``>``<``{``[``(` `]``}` 等等是在大多数链式Bash命令中都会出现[我们一直在仔细研究(`&`)符号][1]。
[上次,我们看到了如何使用`&`把可能需要很长时间运行的进程放到后台运行][1]。但是,``与尖括号`<`结合使用,也可用于管道输出或向其他地方的输入。
在[前面的][2] [尖括号教程中][3],您看到了如何使用`>`,如下:
```
ls > list.txt
```
将`ls`输出传递给_list.txt_文件。
现在我们看到的是简写
```
ls 1> list.txt
```
在这种情况下,`1`是一个文件描述符,指向标准输出(`stdout`)。
以类似的方式,`2`指向标准error`stderr`
```
ls 2> error.log
```
所有错误消息都通过管道传递给_error.log_文件。
回顾一下:`1>`是标准输出(`stdout``2>`标准错误输出(`stderr`)。
第三个标准文件描述符,`0<`标准输入(`stdin`)。您可以看到它是一个输入,因为箭头(`<`)指向`0`,而对于 `1`和`2`,箭头(`>`)是指向外部的。
### 标准文件描述符有什么用?
如果您在阅读本系列以后,您已经多次使用标准输出(`1>`)的简写形式:`>`。
例如,当(假如)你知道你的命令会抛出一个错误时,像`stderr``2`这样的东西也很方便但是Bash告诉你的东西是没有用的你不需要看到它。如果要在_home/_目录中创建目录例如
```
mkdir newdir
```
如果_newdir/_已经存在`mkdir`将显示错误。但你为什么要关心好吧在某些情况下你可能会关心但并非总是如此。在一天结束时_newdir_会以某种方式让你填写一些东西。您可以通过将错误消息推入void即_/dev/null_来抑制错误消息
```
mkdir newdir 2> /dev/null
```
这不仅仅是“ _让我们不要看到丑陋和无关的错误消息因为它们很烦人_因为在某些情况下错误消息可能会在其他地方引起一连串错误。比如说你想找到_/etc_下所有的*.service_文件。你可以这样做
```
find /etc -iname "*.service"
```
但事实证明,在大多数系统中,`find`显示错误会导致许多行出现问题因为普通用户对_/etc_下的某些文件夹没有读取访问权限。它使读取正确的输出变得很麻烦如果`find`是更大的脚本的一部分,它可能会导致行中的下一个命令排队。
相反,你可以这样做:
```
find /etc -iname "*.service" 2> /dev/null
```
而且你只得到你想要的结果。
### 文件描述符入门
单独的文件描述符`stdout`和`stderr`还有一些注意事项。如果要将输出存储在文件中,请执行以下操作:
```
find /etc -iname "*.service" 1> services.txt
```
工作正常,因为`1>`意味着“ _发送标准输出只有标准输出非标准错误_ ”。
但这里存在一个问题:如果你想保留命令抛出的错误信息的和非错误信息你该怎么*做*?上面的说明并不会这样做,因为它只写入`find`正确的结果
```
find /etc -iname "*.service" 2> services.txt
```
只会写入命令抛出的错误信息。
我们如何得到两者?请尝试以下命令:
```
find /etc -iname "*.service" &> services.txt
```
…… 再次和`&`打招呼!
我们一直在说`stdin``0``stdout``1`)和`stderr``2`是_文件描述符_。文件描述符是一种特殊构造指向文件的通道用于读取或写入或两者兼而有之。这来自于将所有内容都视为文件的旧UNIX理念。想写一个设备将其视为文件。想写入套接字并通过网络发送数据将其视为文件。想要读取和写入文件显然将其视为文件。
因此,在管理命令的输出和错误的位置时,将目标视为文件。因此,当您打开它们来读取和写入它们时,它们都会获得文件描述符。
这是一个有趣的效果。例如,您可以将内容从一个文件描述符传递到另一个文件描述符:
```
find /etc -iname "*.service" 1> services.txt 2>&1
```
该管道`stderr`以`stdout`与`stdout`通过管道被输送到一个文件中_services.txt的_。
它再次出现:`&`发信号通知Bash `1`是目标文件描述符。
标准文件描述符的另一个问题是,当你从一个管道传输到另一个时,你执行此操作的顺序有点违反直觉。例如,按照上面的命令。它看起来像是错误的方式。您可能正在阅读它:“ _将输出传输到文件然后将错误传递给标准输出。_ ”看起来错误输出会很晚,并且在`1`已经完成时发送。
但这不是文件描述符的工作方式。文件描述符不是文件的占位符而是文件的_输入and/or输出通道_。在这种情况下当你`1> services.txt`这样做时,你会说“ _打开一个写管道到services.txt并保持打开状态_ ”。`1`是您要使用的管道的名称,它将保持打开状态直到该行的结尾。
如果你仍然认为这是错误的方法,试试这个:
```
find /etc -iname "*.service" 2>&1 1>services.txt
```
并注意它是如何工作的; 注意错误是如何通过管道输送到终端的,所以只有非错误的输出(即`stdout`)被推送到`services.txt`。
这是因为Bash从左到右处理`find`的每个结果。这样想当Bash到达`2>&1`时,`stdout` (`1`) 仍然是指向终端的通道。如果`find` Bash的结果包含一个错误它将被弹出到`2`,转移到`1`,然后离开终端!
然后在命令结束时Bash看到您要打开`stdout`作为_services.txt_文件的通道。如果没有发生错误结果将`1`进入文件。
相比之下,在
```
find /etc -iname "*.service" 1>services.txt 2>&1
```
`1`从一开始就指向services.txt因此任何弹出到`2`的内容都会通过`1`进行管道传输,而`1`已经指向services.txt中的最后一个休息位置这就是它工作的原因。在任何情况下如上所述`&>`都是“标准输出和标准错误”的缩写,即`2>&1`。
在任何情况下,如上所述`&>`是“_标准输出和标准误差_”的简写即`2>&1`。
这可能有点多但不用担心。调整文件描述符在Bash命令行和脚本中是司空见惯的事。随着本系列的深入您将了解更多关于文件描述符的知识。下周见!
--------------------------------------------------------------------------------
via: https://www.linux.com/blog/learn/2019/2/ampersands-and-file-descriptors-bash
作者:[Paul Brown][a]
选题:[lujun9972][b]
译者:[zero-mk](https://github.com/zero-mk)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://www.linux.com/users/bro66
[b]: https://github.com/lujun9972
[1]: https://www.linux.com/blog/learn/2019/2/and-ampersand-and-linux
[2]: https://www.linux.com/blog/learn/2019/1/understanding-angle-brackets-bash
[3]: https://www.linux.com/blog/learn/2019/1/more-about-angle-brackets-bash