【问题标题】:How would you represent EOF in bash?您将如何在 bash 中表示 EOF?
【发布时间】:2010-10-02 14:39:24
【问题描述】:

我正在尝试做类似的事情

read -d EOF stdin

for word in $stdin; do stuff; done

我想将“EOF”替换为文件结尾字符的实际表示。

编辑:感谢您的回答,这确实是我想要做的。当我看到stdin=$(cat)时,我真的有一个捂脸的时刻。lol

只是为了好玩,尽管您将如何匹配诸如 C-d(或 C-v M-v 等)之类的东西,基本上只是一个结合了 Control、Alt、Shift 或 bash 中的任何东西的字符?

【问题讨论】:

    标签: bash


    【解决方案1】:

    两件事...

    EOF 字符由 C-d 表示(或 C-v C-d,如果您想键入它),但要执行您正在尝试的操作,最好这样做:

    while read line; do stuff "${line}"; done
    

    【讨论】:

    • EOF 不是 control-d。 Control-d 只是最常见的按键设置以发出 EOF 信号。
    【解决方案2】:

    真的没有文件结束符。当您按 Ctrl-d 或类似字符时,终端驱动程序会通过返回无效值向读取应用程序发出信号,表明已到达文件末尾。当您到达文件末尾时,操作系统也会这样做。这是通过使用整数而不是字节来完成的(因此您的范围类似于 -2^16 .. 2^16,而不仅仅是 0..255)并返回一个超出范围的值 - 通常是 -1 .但是没有字符可以代表eof,因为它的全部目的是不是一个字符。如果您想从标准输入读取所有内容,直到文件末尾,请尝试

    stdin=$(cat)
    for word in $stdin; do stuff; done
    

    然而,这会将整个标准输入读入变量。您可以只使用数组为一行分配内存,然后让read 将一行的字读入该数组:

    while read -r -a array; do 
        for word in "${array[@]}"; do 
            stuff;
        done
    done
    

    【讨论】:

    • 在 Unix 中,EOF 一个字符。见en.wikipedia.org/wiki/End-of-transmission_character
    • EOT/EOF 是 ASCII 中的“控制字符”。请参阅 en.wikipedia.org/wiki/ASCII>。您描述的这种“终端驱动程序”行为的参考在哪里?这对我来说是新的。
    • 您所指的 EOT 只是触发阅读程序 EOF 的一种方式。这不是“eof 角色”。如前所述, eof 不是一个字符。您可以通过说“stty eof A”(或任何其他字符)来更改触发 EOF 的字符。然后,在终端上按“A”将发出“EOF”信号。
    • 并且 EOF 没有价值。这是一个条件。像“网络通信结束”,或“传输结束”,或“文件结束”或其他什么。无论如何,我仍然没有看到我的答案有问题。我想我永远不会理解它的问题:) 请再读一遍,也许你忽略了一些东西
    • 我想我的批评不是你的错误或困惑,只是你提到了一个不适用于 shell 编程的 EOF 定义。
    【解决方案3】:

    litb 和 Daniel 是对的,我将回答您的“Just for kick”问题: Bash(与一般的任何命令行 unix 程序一样)仅将字符视为字节。因此,您无法匹配 Alt-v,您将匹配从解释用户这些按键的 UI(伪 tty)发送给您的任何字节。它甚至可以是 unix 信号,甚至不是字节。这将取决于所使用的终端程序、用户设置和所有类型的东西,所以我建议你不要尝试匹配它们。

    但如果您知道您的终端将 C-v 作为字节数 22 (0x16) 发送,您可以使用以下内容:

    if test "$char" = '^V'; then...
    

    通过在编辑器下输入真正的 ^V 字符(emacs 下的 C-q C-v,xterm 下的 C-v C-v,...),而不是两个字符 ^ 和 V

    【讨论】:

    • 表示和匹配控制字符的一种更可靠的方法是将它们转换为 ANSI C 引用的字符串,如下所示(如果您还不知道它们的 ASCII 代码点):print '%q\n' <press C-q> <press C-whatever>;对于C-v,你会得到$'\026'(八进制转义;相当于$'\x16')。
    【解决方案4】:

    要查找控制字符是什么,请运行

    $ cat | od -b
    ^D
    0000000 004 012
    0000002
    

    我在发出命令后键入 ^V^D,然后键入 RET 和另一个 ^D (未引用),结果是 EOF 是八进制 004。

    将该结果与read(1) 结合起来:

    $ read -d "$(echo -e '\004')" stdin
    foo
    bar quuz^Hx
    ^D
    $ echo "$stdin"
    foo
    bar quux
    $ for word in $stdin; do echo $word; done
    foo
    bar
    quux
    

    是的,我在上面输入了 ^H 以进行退格,以查看 read(1) 是否正确。确实如此。

    【讨论】:

    • EOT 为 0x04。 EOF 什么都不是。只需在终端上执行 ^D ,您就会看到 od 不显示任何字节,正是 因为 EOF 不是字符,而只是一个符号数字。如果真的像你说的那样,我们怎么能读取包含 0x04 的二进制文件??
    • 我试过了,它停止读取文件。在用户级别,EOF 为 0x04。在程序员的层面上,它不是一个字符,而是一个值。无论如何,您不会将 read 与二进制文件一起使用。你应该用 C 编程。
    • 您可以将"$(echo -e '\004')" 简化为$'\x04'。但是请注意,您在这里所做的实际上是 模拟 正常的 ^-D 处理,因为 read - 出于我不知道的原因 - 停用它 当使用-d 选项。因此,与正常情况不同^-D实际上是作为字符0x04发送到read,然后终止读取,因为0x04发生被定义为自定义分隔符。归根结底,简单地使用stdin=$(cat) 是更简单的解决方案。
    • 当你说I typed ^H above for backspace to see if read(1) did the right thing.时,什么是正确的?我假设您的意思是0x08(退格)characterinserted(表示为^-H)而不是左侧的字符被删除。虽然该行为与 cat 等一致,但您失去的一件事是 使用实际退格键进行实际退格的能力
    • 不,没有插入 ^H。相反,quuz 变成了 quux。
    【解决方案5】:

    我自己的终端驱动,当 getc 返回 EOT 时,fclose 的 stdout 并重新打开。这样,当读取器的 getc 检测到一个空的写入队列并返回 EOF(非字符值)以表示它已关闭时,诸如“cat”之类的用户子例程可以移动参数并最终退出。因此将 EOF 呈现为流条件或文件标记,在“char”范围内没有值。

    【讨论】:

      猜你喜欢
      • 2011-01-30
      • 1970-01-01
      • 2011-04-15
      • 2010-10-04
      • 1970-01-01
      • 2020-12-15
      • 1970-01-01
      • 2020-08-22
      • 2020-10-03
      相关资源
      最近更新 更多