所以这个 shell 脚本:
#!/bin/bash
head >/dev/null;
head;
当使用序列号调用时几乎总是给出相同的输出(例如 seq 10000 | ./sscript)
输出:
//blank line
1861
1862
1863
1864
1865
1866
1867
1868
1869
我用它进行了追踪,strace seq 10000 | ./sscript
但无法向自己解释这些数字到底来自哪里。在 strace 的末尾:
write(1, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 4096) = 4096
write(1, "1\n1042\n1043\n1044\n1045\n1046\n1047\n"..., 4096) = 4096
write(1, "\n1861\n1862\n1863\n1864\n1865\n1866\n1"..., 4096) = 4096
write(1, "2680\n2681\n2682\n2683\n2684\n2685\n26"..., 4096) = 4096
write(1, "499\n3500\n3501\n3502\n3503\n3504\n350"..., 4096) = 4096
write(1, "18\n4319\n4320\n4321\n4322\n4323\n4324"..., 4096) = 4096
write(1, "7\n5138\n5139\n5140\n5141\n5142\n5143\n"..., 4096) = 4096
write(1, "\n5957\n5958\n5959\n5960\n5961\n5962\n5"..., 4096) = 4096
write(1, "6776\n6777\n6778\n6779\n6780\n6781\n67"..., 4096) = 4096
write(1, "595\n7596\n7597\n7598\n7599\n7600\n760"..., 4096) = 4096
write(1, "14\n8415\n8416\n8417\n8418\n8419\n8420"..., 4096) = 4096
write(1, "3\n9234\n9235\n9236\n9237\n9238\n9239\n"..., 3838) = 3838
write
为什么只返回第三个(有时只返回第二个)?
实际上,由于脚本中的第二行,仅打印了返回行(第三次或第二次写入)的前 10 行head
,但仍然丢失。
答案1
head
默认情况下打印 10 行,但在执行此操作时它会读入尽可能多的输入 - 请注意,GNUhead
有一些选项要求它知道文件中总共有多少行,因此尽可能多地读入没有错。
head
尽可能多地读入以填充其缓冲区,该缓冲区似乎为 8192 字节:
~ seq 10000 | strace -fe read ./foo.sh
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260e\1\0\0\0\0\0"..., 832) = 832
...
Process 17610 attached
...
[pid 17610] read(0, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14"..., 8192) = 8192
...
[pid 17611] read(0, "\n1861\n1862\n1863\n1864\n1865\n1866\n1"..., 8192) = 8192
...
由于前两次写入每次都是 4096 字节,因此它们可以被第一次写入使用head
。
这取决于时机。如果在第一个打印 10 行并退出时才seq
成功带走一个,那么第二个将被第二个带走。write
head
write
head
这来自迈克塞夫的评论很有启发性:
你应该尝试使用常规文件。
seq 10000 >/tmp/nums; yourscript </tmp/nums
其行为如您所料的原因是head
尝试将当前读取点重新定位到使用它输出的读取点之后的行lseek()
。这适用于普通文件、重定向文件等,但不适用于管道:
The lseek() function shall fail if:
...
ESPIPE The fildes argument is associated with a pipe, FIFO, or socket.
可以看出使用strace
:
~ seq 10000 | strace -fe lseek ./foo.sh
...
Process 18561 attached
[pid 18561] lseek(0, -8171, SEEK_CUR) = -1 ESPIPE (Illegal seek)
[pid 18561] +++ exited with 0 +++
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=18561, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
Process 18562 attached
[pid 18562] lseek(0, -8146, SEEK_CUR) = -1 ESPIPE (Illegal seek)
...