RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 819225
Accepted
aleksandr barakin
aleksandr barakin
Asked:2020-04-25 21:09:37 +0000 UTC2020-04-25 21:09:37 +0000 UTC 2020-04-25 21:09:37 +0000 UTC

同时将文件内容传递给两个命令并不总是有效

  • 772

为什么构造 1 和 2 按预期工作,而构造 3 只输出行数?

  1. 输出文件的第一行和最后一行:

    $ (head -n 1; tail -n 1) < файл
    
  2. 输出文件的第一行和行数:

    $ (head -n 1; wc -l) < файл
    

    (更新 1:事实上,事实证明它的工作方式并不像这样:它wc显示的数字比文件长度小一——这一行被“吃掉了” head)。

  3. 输出文件的行数,然后是第一行(不起作用 - 只显示行数):

    $ (wc -l; head -n 1) < файл
    

update2:关于来自gnu/coreutils的头程序的“特殊实现” ,例如,可以争辩说“busybox”实现的行为相同。此命令输出与第 1 点相同:

$ (busybox head -n 1; tail -n 1) < файл

而且一般来说,几乎不可能“注销一切”来实现head 。因为结构的行为:

$ cat файл | (head -n 1; tail -n 1)
$ (head -n 1; tail -n 1) < <(cat файл)

与第一个示例中给出的不匹配:仅显示文件的第一行。


程序版本(尽管这很可能几乎不重要):

$ head --version
head (GNU coreutils) 8.26
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by David MacKenzie and Jim Meyering.
$ wc --version
wc (GNU coreutils) 8.26
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Paul Rubin and David MacKenzie.
$ bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

ps,知道这样的构造 ( (команда1; команда2) < файл) 记录在哪里会很有趣。

bash
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Best Answer
    aleksandr barakin
    2020-04-25T23:55:44Z2020-04-25T23:55:44Z

    要回答的正确问题是:

    为什么构造(head -n 1; head -n 1) < файл(下面称为“源命令”)在发出文件的前两行时完全起作用?

    通过正确回答它,将有可能回答其余的问题。

    看了头程序的踪迹后得到了答案。测试文件:

    $ cat file
    first line
    second line
    last line
    

    在gnu/linux下运行(这里原始命令产生两行):

    $ (strace head -n 1; head -n 1) < file
    read(0, "first line\nsecond line\nlast line"..., 8192) = 33
    lseek(0, -22, SEEK_CUR)                 = 11
    write(1, "first line\n", 11)            = 11
    

    在solaris下运行(这里原始命令也产生两行):

    $ (truss head -n 1; head -n 1) < file
    read(0, " f i r s t   l i n e\n s".., 4096)     = 33
    write(1, " f i r s t   l i n e\n", 11)          = 11
    llseek(0, 0xFFFFFFFFFFFFFFEA, SEEK_CUR)         = 11
    

    在freebsd下运行(这里原来的命令只打印第一行):

    $ (truss head -n 1 ; head -n 1) < file
    read(0,"first line\nsecond line\nlast li"...,32768) = 33 (0x21)
    write(1,"first line\n",11)           = 11 (0xb)
    

    只剩下基本的输出线。他们表明,所有三个实现中的head程序都从stdin读取所有 33 个字节(由 shell 与真实文件关联),然后将前 11个字节写入stdout。

    但是在gnu/linux和solaris的情况下,读取后,指针被“倒带”(lseek/llseek)到输入文件中的第 11 位(到第二行的开头),而在freebsd中则不会发生这种情况。


    从这里可以清楚地看出为什么看似相同的结构:

    $ cat файл | (head -n 1; head -n 1)
    

    在所有三个测试系统中的工作方式都相同(仅显示文件的第一行):毕竟,只有在真实文件中才能“倒带”,而且当它不是文件,而是通过“ pipe” ( pipe , |),则无法重新排列指针。

    • 4
  2. Fat-Zer
    2020-04-25T21:34:55Z2020-04-25T21:34:55Z

    构造本身(команда1; команда2) < файл并没有什么特别之处:启动了一个子shell,它被赋予了文件。在subshel​​l中,首先执行command1 ,然后执行command2。它们都依次从同一个标准输入中读取。

    在简化版本中,您可以这样想:

    1. head读取第一行,然后停止并打印,读取其余部分tail。但是AFAIK给定的行为没有标准化。

    2. 同样,它head读取第一行,其余的都转到wc. wc打印输出的行数减一。

    3. wc阅读所有内容,但head'一无所获。

    所有这些都是非常肮脏的东西,而且第一个命令起作用的事实仅与 GNU 实现相关head。你不能依赖它。

    您可以正确地将输出重定向到两个不同的进程,bash例如:

    tee >(head -n1) >(tail -n1) >/dev/null
    

    但是没有定义输出head到终端的顺序。tail

    在符合基本 POSIX 的外壳程序 AFAIK 中,这只能使用命名管道来完成。

    更新

    各种shell的测试结果:

    测试文件:

    $ cat /tmp/file
    head_line
    body
    tail_line
    

    测试的外壳:* GNU/Linux:bash、dash、bussybox sh * FreeBSD 8.0:csh、bash

    测试命令:

    1. <shell> -c 'cat /tmp/file | (head -n 1; tail -n 1)'
      <shell> -c 'cat /tmp/file | (head -n 1; head -n 1)'
      在所有贝壳上给出相同的结果:

      head_line
      
    2. $ <shell> -c '(head -n 1; tail -n 1) </tmp/file'

      head_line
      tail_line
      
    3. $ <shell> -c '(head -n 1; head -n 1) </tmp/file'

      • GNU/Linux(所有外壳):

        head_line
        body
        
      • FreeBSD 8(所有外壳):

        head_line
        
    • 3
  3. jfs
    2020-04-26T02:08:59Z2020-04-26T02:08:59Z

    为什么构造 1 和 2 按预期工作,而构造 3 只输出行数?

    <файл在 shell(POSIX,bash)中将文件重定向到标准输入(从 stdin (fd 0) 读取的命令将获取文件的内容)。wc -l在第 3 步中读取整个输入,因此没有head什么可分享的了。

    该实用程序的文档(POSIX和Gnu )不保证它head -n 1不会消耗所有输入(允许步骤 1 和 2 中的命令工作)。可以合理地预期不会完成额外的工作,特别是如果它不会使实施复杂化。例如FreeBSD:headhead.c

    while (cnt && (cp = fgetln(fp, &readlen)) != NULL) {
    

    fgetln()使用 stdio 缓冲区,因此它可以从标准输入中读取更多内容。这解释了为什么(head -n 1; head -n 1) <файл 它在 FreeBSD 上不起作用。tail可以忽略文件中的当前位置@Fat-Zer 想通了。因此,它(head -n 1; tail -n 1) <file也可以在 FreeBSD 上运行。

    GNU head.c也可以读取更多BUFSIZ的内容(读取一行时,尝试将位置设置在行尾)。head 实用程序本身的文档不保证此行为。

    由于管道不可搜索,这就解释了为什么 c 选项cat file|不起作用。

    根据POSIX,需要标准实用程序(例如 head、sed、tail)来设置可搜索输入的文件位置(这是 POSIX 指定的分组命令中的尾部行为吗?):

    当标准实用程序读取可查找的输入文件并在到达文件结尾之前无错误终止时,该实用程序应确保打开文件描述中的文件偏移量正确定位在该实用程序处理的最后一个字节之后。对于不可搜索的文件,未指定该文件的打开文件描述中的文件偏移状态。符合要求的应用程序不应假定以下三个命令是等效的:

       tail -n +2 file
       (sed -n 1q; cat) < file
       cat file | (sed -n 1q; cat)
    

    也就是说,在 POSIX 上,for 的行为是有(head -n 1; tail -n 1) <file保证的。

    通常在哪里记录这样的构造 ((command1; command2) <file)

    ( compound-list )命令在子shell中执行(比看起来更常见,例如,管道中的每个命令都可以在子shell中执行(在单独的进程中)。如果不需要单独的过程,则 { compound-list ; } 可以使用该构造(重定向仍然有效)。

    如果不清楚重定向的工作原理以及作为官方文档的补充 -  POSIX、bash ,您可以查看带图表的介绍手册。

    • 3
  4. avp
    2020-04-26T05:21:16Z2020-04-26T05:21:16Z

    为什么构造 1 和 2 按预期工作,而构造 3 只输出行数?

    因为 shell 做了 1 open()次,然后 2 次dup2()。因此,在命令中,它共享与返回stdin的文件描述符关联的共享读写指针。 open

    有点出乎意料的非常整洁的行为head(tail在这方面我真的很震惊(请参阅@alexanderbarakin,@Fat-Zer 和 @jfs 的答案和评论中的纹理))将指针移动到与字节对应的逻辑正确位置它们实际上会在顺序读取文件时进行处理。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5