当前位置: 首页 > 工具软件 > bash-fun > 使用案例 >

bash 重定向_使用描述符进行bash重定向的乐趣

田马鲁
2023-12-01

bash 重定向

Bash重定向:基础知识 (Bash Redirection: The Basics)

A bash script is commonly a set of commands. There are three standard file descriptors of any command:

bash脚本通常是一组命令。 任何命令都有三个标准文件描述符:

  • 0 → stdin

    0→标准输入
  • 1 → stdout

    1→标准输出
  • 2 → stderr

    2→标准错误

There are two commonly used redirection operators:

有两种常用的重定向运算符:

  • > output (This is the default operator for fd 1)

    >输出(这是fd 1的默认运算符)

  • < input (This is the default operator for fd 0)

    <输入(这是fd 0的默认运算符)

输出重定向 (Output Redirection)

The most basic example of redirecting the output of a command to a file is:

将命令输出重定向到文件的最基本示例是:

echo "hello world" 1> foo.txt

This redirects the output of the echo command to a file called foo.txt. Since 1 is the default file descriptor for the > operator, it can be omitted. Hence the following is a valid bash command.

这会将echo命令的输出重定向到名为foo.txt的文件。 由于1是>运算符的默认文件描述符,因此可以省略。 因此,以下是有效的bash命令。

echo “hello world” > foo.txt

echo “hello world” > foo.txt

无障碍和强制重定向 (No Clobber and Forced Redirection)

If the noclobber option to the set builtin has been enabled, the output redirection will fail if the file exists and is a regular file.

如果noclobber选项的set内建已启用,如果该文件存在并且是一个普通文件的输出重定向将失败。

The forced redirection operator >| will redirect the output to the file even if the noclobber option is set.

强制重定向运算符 >| 即使设置了noclobber选项,也会将输出重定向到文件。

输入重定向 (Input Redirection)

Likewise, the most canonical example of the < operator is the following command:

同样, <运算符的最典型示例是以下命令:

read input < foo.txt && echo "${input}"

为文件分配文件描述符 (Assigning file descriptors to files)

Turns out there’s more that can be done with file descriptors in bash than just use it to redirect the input or output of commands.

事实证明,使用bash中的文件描述符可以完成的工作不仅仅用于重定向命令的输入或输出。

In particular, bash allows of to assign file descriptors to files, and then operate on those files by just using the descriptors. This can be done with the exec command:

特别是,bash允许文件描述符分配给文件,然后仅使用描述符对这些文件进行操作。 可以使用exec命令来完成:

exec Z > file

exec Z > file

which will assign file descriptor Y to a file for output.

这会将文件描述符Y分配给文件以进行输出。

~ : exec 5> bar.txt
~ : echo "bye, world" >& 5
~ : cat bar.txt
bye, world

The exec X < file command allows one to assign file descriptor X to a file for input.

exec X < file命令允许将文件描述符X分配给文件以供输入。

~ : exec 6< bar.txt
~ : read -n input <& 6 && echo "${input}"
bye, world

Gotcha (Gotcha)

Using file descriptor 5 might cause problems since when Bash creates a child process, as with exec, the child inherits fd 5.

使用文件描述符 5可能会引起问题,因为当Bash与exec一样创建子进程时,该子进程继承了fd 5。

关闭文件描述符 (Closing File Descriptors)

Of course, the fact that bash allows one to open file descriptors also means there must surely be a way to close them. And there is!

当然,bash允许一个人打开文件描述符这一事实也意味着肯定有一种关闭它们的方法。 还有!

exec Z <&-

exec Z <&-

is the syntax to close file descriptor Z, where Z can include values like 0, 1 and 2.

关闭文件描述符Z的语法,其中Z可以包含0、1和2之类的值。

~ : exec 6< bar.txt
~ : read input <& 6 && echo "${input}"
bye, world
~ : exec 6<&-
~ : read input <& 6 && echo "${input}"
bash: 6: bad file descriptor

In the snippet above, we see that we can assign file descriptor 6 for input and successfully read from the file by just referring to the file descriptor 6. We then then close file descriptor 6, after which trying to read from the file using descriptor 6 results in an error.

在上面的代码段中,我们看到可以为文件分配输入的文件描述符6并仅通过引用文件描述符6就能成功地从文件中读取文件。然后关闭文件描述符6,然后尝试使用描述符6读取文件。导致错误。

We can even close the 3 standard file descriptors (0, 1 and 2) using this command. Basic shell functions like cat while rely on these file descriptors become non-functional in these cases.

我们甚至可以使用此命令关闭3个标准文件描述符(0、1和2)。 在这些情况下,像cat这样的基本shell函数在依赖这些文件描述符的情况下将无法工作。

bash-5.0$ echo "hello world" > foo.txt
bash-5.0$ cat foo.txt
hello world
bash-5.0$ exec 1<&-
bash-5.0$ cat foo.txt
cat: stdout: Bad file descriptor

When closing stdin (fd 0), the bash prompt understandably exits.

关闭stdin(fd 0)时,bash提示符可以理解地退出。

bash-5.0$ exec 0<&-
bash-5.0$ exit

And trying to close stderr (fd 2) results in a hang, on both macOS and Linux.

在macOS和Linux上尝试关闭stderr(fd 2)都会导致挂起。

Closing file descriptors becomes important, inasmuch as child processes inherit all of the open file descriptors of the parent process. Closing a file descriptor prevents it from being inherited by a child process.

由于子进程继承了父进程的所有打开文件描述符,因此关闭文件描述符变得很重要。 关闭文件描述符可防止其被子进程继承。

移动文件描述符 (Moving File Descriptors)

Yes, that’s right.

是的,这是对的。

Bash allows one to move file descriptors.

Bash允许移动文件描述符。

移动输入文件描述符 (Moving Input File Descriptors)

exec X<&Y-

moves the file descriptor Y to file descriptor X, or the standard input (file descriptor 0) if X is not specified.

将文件描述符Y移到文件描述符X,或者如果未指定X,则将标准输入(文件描述符0)移到文件描述符X。

Y is closed after being duplicated to X.

Y复制到X后关闭。

bash-5.0$ echo "hello world" > foo.txt
bash-5.0$ exec 6< foo.txt
bash-5.0$ read input <& 6 && echo "${input}"
hello world
bash-5.0$ exec 7<&6-
bash-5.0$ read input <& 6 && echo "${input}"
bash: 6: Bad file descriptor
bash-5.0$ read input <& 7 && echo "${input}"
hello world

In the above code snippet, we see that once file descriptor 6 has been moved to 7, one can no longer access input from the file foo.txt using fd 6. However, one can access input from the file using fd 7.

在上面的代码片段中,我们看到将文件描述符6移到7后,就无法再使用fd 6访问文件foo.txt的输入。但是,可以使用fd 7访问文件的输入。

移动输出文件描述符 (Moving Output File Descriptors)

Likewise, it’s also possible to move output descriptors.

同样,也可以移动输出描述符。

exec X>&Y-

moves the file descriptor Y to file descriptor X, or the standard output (file descriptor 1) if X is not specified.

将文件描述符Y移到文件描述符X,或者将标准输出(文件描述符1)移至未指定X的位置。

bash-5.0$ touch foo.txt
bash-5.0$ exec 6> foo.txt
bash-5.0$ echo "hello, world" >& 6
bash-5.0$ cat foo.txt
hello, world
bash-5.0$ echo "bye, world" >& 6
bash-5.0$ cat foo.txt
hello, world
bye, world
bash-5.0$ exec 8>&6-
bash-5.0$ echo "hello again, world" >& 6
bash: 6: Bad file descriptor
bash-5.0$ echo "hello again, world" >& 8
bash-5.0$ cat foo.txt
hello, world
bye, world
hello again, world

In the above snippet, we first assign fd 6 to file foo.txt for output. We can then redirect output to fd 6 instead of specifying the file by name. Next, we move fd 6 to fd 8, after which we can no longer use fd 6 to refer to the file for output redirection. However, we can use fd 8 for output redirection.

在以上代码段中,我们首先将fd 6分配给文件foo.txt进行输出。 然后,我们可以将输出重定向到fd 6,而不用通过名称指定文件。 接下来,将fd 6移至fd 8,之后便无法再使用fd 6来引用文件以进行输出重定向。 但是,我们可以使用fd 8进行输出重定向。

使用单个文件描述符读取和写入 (Using a Single File Descriptor to Read And Write)

So far, we’ve specifically assigned file descriptors for input or output, but not both. It turns out bash has a special operator that allows one to perform both forms of redirection using a single operator:

到目前为止,我们已经为输入输出(而不是两者)专门分配了文件描述符。 事实证明,bash有一个特殊的运算符,它允许一个人使用一个运算符执行两种形式的重定向:

exec X<>file

causes the file named file to be opened for both reading and writing on file descriptor X, or on file descriptor 0 if X is not specified. If the file does not exist, it is created.

导致命名的文件file ,如果没有指定X被打开以便读取和写入的文件描述符X,或者在文件描述符0。 如果该file不存在,则会创建它。

bash-5.0$ touch foo.txt
bash-5.0$ exec 7<>foo.txt
bash-5.0$ echo "Hello, World!" >&7
bash-5.0$ echo "Hello, New World!" >&7
bash-5.0$ cat foo.txt
Hello, World!
Hello, New World!
bash-5.0$
bash-5.0$
bash-5.0$ read input <&7 && echo "${input}"
bash-5.0$

In the above snippet, we open a file foo.txt for both input and output on file descriptor 7. We then redirect two output streams to fd 7. When we then cat the file, we see that both of output streams have been written to the file.

在上面的代码片段中,我们打开了文件描述符7上的输入和输出文件foo.txt ,然后将两个输出流重定向到fd7。当我们对文件进行分类时,我们看到两个输出流都已写入到文件中。文件。

When we next try to read from the file, the read doesn’t return anything. This is because the file descriptor 7 is pointing to the end of the file after the last character of the last output stream. Bash (thankfully!) doesn’t implement a built in or an operator that emulates lseek(2), so there’s no way to seek to the beginning of the file before issuing the read.

接下来,当我们尝试从文件中读取内容时,读取操作不会返回任何内容。 这是因为文件描述符7指向最后输出流的最后一个字符之后的文件末尾 。 Bash(非常感谢!)没有实现模拟lseek(2)的内置或运算符,因此在发出读取之前无法找到文件的开头。

However, if we reissue the redirection command, we see that we can access input from the file descriptor.

但是,如果重新发出重定向命令,则会看到可以访问文件描述符中的输入。

bash-5.0$ touch foo.txt
bash-5.0$ exec 7<>foo.txt
bash-5.0$ echo "Hello, World!" >&7
bash-5.0$ echo "Hello, New World!" >&7
bash-5.0$ cat foo.txt
Hello, World!
Hello, New World!
bash-5.0$
bash-5.0$
bash-5.0$ read input <&7 && echo "${input}"
bash-5.0$
bash-5.0$ exec 7<>foo.txt
bash-5.0$ read input <&7 && echo "${input}"
Hello, World!

Furthermore, we can redirect output of a command to the file we’ve just read a line from using the same file descriptor. It’ll overwrite the existing contents of the file, since we’re effectively just writing bytes to a file descriptor.

此外,我们可以将命令的输出重定向到我们刚刚使用同一文件描述符读取的一行文件。 因为我们实际上只是将字节写入文件描述符,所以它将覆盖文件的现有内容。

bash-5.0$ touch foo.txt
bash-5.0$ exec 7<>foo.txt
bash-5.0$ echo "Hello, World!" >&7
bash-5.0$ echo "Hello, New World!" >&7
bash-5.0$ cat foo.txt
Hello, World!
Hello, New World!
bash-5.0$ read input <&7 && echo "${input}"
bash-5.0$
bash-5.0$ exec 7<>foo.txt
bash-5.0$ read input <&7 && echo "${input}"
Hello, World!
bash-5.0$ cat foo.txt
Hello, World!
Hello, New World!
bash-5.0$ echo "Bye, World!" >&7
bash-5.0$ cat foo.txt
Hello, World!
Bye, World!
orld!
bash-5.0$

In the above snippet, we’re written two lines to a file, then done a “reset” on the file descriptor, then read a line from the file, and then used the same file descriptor to write another output stream to the file, which overwrites the content of the file.

在以上代码段中,我们向文件写入了两行,然后对文件描述符进行了“重置”,然后从文件中读取了一行,然后使用相同的文件描述符将另一个输出流写入文件中,它将覆盖文件的内容。

结论 (Conclusion)

The more I learn about bash, the more I’m convinced to never use the more advanced features bash offers.

我对bash的了解越多,我越坚信永远不会使用bash所提供的更高级的功能。

A small and relatively safe subset of bash is best used for scripting. Anything this advanced/esoteric is likely to cause maintenance headaches as well as readability problems.

最好使用一小部分相对安全的bash脚本。 任何这种高级/深奥的东西都可能引起维护难题以及可读性问题。

翻译自: https://medium.com/@copyconstruct/bash-redirection-fun-with-descriptors-e799ec5a3c16

bash 重定向

 类似资料: