【问题标题】:exec n<&m versus exec n>&m -- based on Sobell's Linux bookexec n<&m 与 exec n>&m -- 基于 Sobell 的 Linux 书籍
【发布时间】:2011-05-04 04:04:10
【问题描述】:

在 Mark Sobell 的 Linux 命令、编辑器和 Shell 编程实用指南,第二版中,他写道(第 432 页):

& 复制一个输出 文件描述符。

这似乎与同一页面上的另一个说法不一致:

使用以下格式打开或 将文件描述符 n 重定向为 文件描述符 m 的副本:

执行n

并且在同一页面上还有一个示例:

# File descriptor 3 duplicates standard input
# File descriptor 4 duplicates standard output
exec 3<&0 4<&1

如果 >& 复制了一个输出文件描述符,那么我们不应该说

exec 4>&1

复制标准输出?

【问题讨论】:

    标签: linux bash shell unix file-descriptor


    【解决方案1】:

    这个例子在实践中是正确的。这本书的原始解释是对 POSIX 标准所说内容的准确描述,但是我手边的类似 POSIX 的 shell(bashdash,我认为唯一在 Linux 上常见的 shell)并不那么挑剔。

    POSIX 标准says the same thing as the book 关于输入和输出描述符,并继续说:对于n&lt;&amp;word,“如果word 中的数字不代表已经为输入打开的文件描述符,则应出现重定向错误结果”。所以如果你想注意 POSIX 兼容性,你应该避免这种用法。

    bash 文档还 says the same thing 关于 &lt;&amp;&gt;&amp;,但没有保证会出错。这很好,因为它实际上并没有给出错误。相反,经验上n&lt;&amp;mn&gt;&amp;m 似乎可以互换。 &lt;&amp;&gt;&amp; 之间的唯一区别是,如果您省略左侧的 fd 编号,&lt;&amp; 默认为 0(标准输入),&gt;&amp; 默认为 1(标准输出)。

    例如,让我们使用 fd 1 指向文件 bar 启动一个 shell,然后完全尝试 exec 4&lt;&amp;1 示例,尝试写入结果 fd 4,看看它是否有效:

    $ sh -c 'exec 4<&1; echo foo >&4' >bar; cat bar
    foo
    

    确实如此,并且对于 shell 使用 dashbash(或 bash --posix)。

    在后台,这是有道理的,因为 & 几乎可以肯定只是调用 dup2(),它并不关心 fds 是否打开以进行读取或写入或附加或什么。

    [编辑:在 cmets 讨论后添加了对 POSIX 的引用。]

    【讨论】:

    • 谢谢,特别是参考。有时我对应该起作用的东西比对实际可行的东西更感兴趣。
    • @Alexandros:很公平。查看 POSIX 标准 (pubs.opengroup.org/onlinepubs/009695399/utilities/…),这是对“应该工作”的一种解释,它对n&lt;&amp;word 表示“如果word 中的数字不代表已经打开以供输入的文件描述符,则重定向将导致错误”。所以如果你想小心 POSIX 兼容性,那么你应该避免这种用法。
    • 从这个意义上说,这本书的描述是对的,它的例子是错的,但书名毕竟是“A Practical Guide”。 =)
    • @Alexandros Gezerlis:好的,更新了答案以描述 POSIX 所说的内容。
    • @Greg Price:再次感谢,我认为现在答案已经完成,所以我继续接受它。
    【解决方案2】:

    如果 stdout 是 tty,则可以安全地克隆它以进行读取或写入。如果 stdout 是一个文件,那么它可能不起作用。我认为示例应该是4&gt;&amp;1。我同意 Greg 的观点,即您可以读取和写入克隆描述符,但使用 &lt;&amp; 请求重定向应该使用可读的源描述符完成,并且期望 stdout 可读是没有意义的。 (虽然我承认我没有这个说法的参考。)

    举个例子可能会更清楚。使用此脚本:

    #!/bin/bash
    
    exec 3<&0
    exec 4<&1
    
    read -p "Reading from fd 3: " <&3
    echo From fd 3: $REPLY >&2
    REPLY=
    read -p "Reading from fd 4: " <&4
    echo From fd 4: $REPLY >&2
    
    echo To fd 3 >&3
    echo To fd 4 >&4
    

    我得到以下输出(在终端输入“Reading from”行的 : 之后的内容):

    $ ./5878384b.sh
    Reading from fd 3: foo
    From fd 3: foo
    Reading from fd 4: bar
    From fd 4: bar
    To fd 3
    To fd 4
    $ ./5878384b.sh < /dev/null
    From fd 3:
    Reading from fd 4: foo
    From fd 4: foo
    ./5878384b.sh: line 12: echo: write error: Bad file descriptor
    To fd 4
    $ ./5878384b.sh > /dev/null
    Reading from fd 3: foo
    From fd 3: foo
    ./5878384b.sh: line 9: read: read error: 0: Bad file descriptor
    From fd 4:
    To fd 3
    

    【讨论】:

    • 目前还不清楚“foo”和“bar”的来源。这是一个很好的例子,但如果您将脚本调用为“printf 'foo\nbar\n' | ./5878384.sh”可能会更清楚
    • 感谢您的反馈。我倾向于你的观点,但我希望有一个具体的参考(例如 POSIX 标准)。我很惊讶关于 exec 和文件描述符的权威资料很少。
    • @William 管道它不起作用,因为管道只进入标准输入,但我试图澄清哪些部分是输入/输出。感谢您的建议。
    • 这里的错误与exec 无关,它工作正常。相反,当您尝试实际写入或读取一个仅用于读取或写入的文件时,它们就会发生。作为演示,如果你写 bash -c 'echo To fd 0 &gt;&amp;0' &lt; /dev/nullbash -c 'read -p "Reading from fd 1: " &lt;&amp;1' &gt; /dev/null,你会得到完全相同的错误。
    • 简而言之,你可以只读可读的文件描述符,只写可写的文件描述符,exec 无论如何调用都无法将一种转换为另一种。但问题在于,exec 中的实际 &lt;&amp;&gt;&amp; 形式本身是否重要,而在 bash 和 dash 中它们不重要,尽管 POSIX 说它们应该如此。
    【解决方案3】:

    注意文件描述符和 IO 流(例如 stderr 和 stdout)之间的区别。

    重定向操作符只是通过不同的文件描述符重定向 IO 流(IO 流处理机制);他们不会对 IO 流进行任何复制或复制(这就是 tee(1) 的用途)。

    见:File Descriptor 101

    另一个表明 n&m 可以互换的测试是“使用 'n&-' 的样式来关闭文件描述符,即使它没有匹配打开文件描述符的读/写模式”(http://www.gnu.org/s/hello/manual/autoconf/File-Descriptors.html)。

    【讨论】:

      猜你喜欢
      • 2018-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多