Shell-Script

頭吃多餘的字元

  • December 8, 2017

下面的 shell 命令應該只列印輸入流的奇數行:

echo -e "aaa\nbbb\nccc\nddd\n" | (while true; do head -n 1; head -n 1 >/dev/null; done)

但它只是列印第一行:aaa.

-c與( --bytes) 選項一起使用時不會發生同樣的情況:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 >/dev/null; done)

此命令1234512345按預期輸出。但這僅適用於該實用程序的coreutils實現。busyboxhead實現仍然吃額外的字元,所以輸出只是.12345

我猜這種特定的實現方式是為了優化目的。你不知道行在哪裡結束,所以你不知道你需要閱讀多少個字元。不消耗輸入流中額外字元的唯一方法是逐字節讀取流。但是一次從流中讀取一個字節可能會很慢。所以我猜想head將輸入流讀取到一個足夠大的緩衝區,然後計算該緩衝區中的行數。

--bytes對於使用選項的情況,情況並非如此。在這種情況下,您知道需要讀取多少字節。所以你可以準確地讀取這個字節數,而不是更多。corelibs實現利用這個機會,但busybox沒有,它仍然讀取比需要更多的字節到緩衝區中。這樣做可能是為了簡化實現。

所以這個問題。head實用程序從輸入流中消耗的字元多於要求的字元是否正確?Unix實用程序有某種標準嗎?如果有,它是否指定了這種行為?

附言

您必須按下Ctrl+C才能停止上述命令。Unix 實用程序在讀取超出EOF. 如果不想按,可以使用更複雜的命令:

echo 12345678901234567890 | (while true; do head -c 5; head -c 5 | [ `wc -c` -eq 0 ] && break >/dev/null; done)

為了簡單起見,我沒有使用它。

head 實用程序從輸入流中消耗的字元多於要求的字元是否正確?

是的,這是允許的(見下文)。

Unix實用程序有某種標準嗎?

是的,POSIX 第 3 卷,Shell & Utilities

如果有,它是否指定了這種行為?

它確實,在它的介紹中:

當標準實用程序讀取可查找的輸入文件並在到達文件結尾之前無錯誤地終止時,該實用程序應確保打開文件描述中的文件偏移量正確定位在該實用程序處理的最後一個字節之後。對於不可查找的文件,未指定該文件的打開文件描述中的文件偏移狀態。

head標準實用程序之一,因此符合 POSIX 的實現必須實現上述行為。

GNUhead 確實嘗試將文件描述符保留在正確的位置,但是不可能在管道上尋找,因此在您的測試中它無法恢復該位置。你可以看到這個使用strace

$ echo -e "aaa\nbbb\nccc\nddd\n" | strace head -n 1
...
read(0, "aaa\nbbb\nccc\nddd\n\n", 8192) = 17
lseek(0, -13, SEEK_CUR)                 = -1 ESPIPE (Illegal seek)
...

read返回 17 個字節(所有可用的輸入),處理head其中的四個,然後嘗試移回 13 個字節,但它不能。(您還可以在此處看到 GNUhead使用 8 KiB 緩衝區。)

當您告訴head計算字節數(這是非標準的)時,它知道要讀取多少字節,因此它可以(如果以這種方式實現)相應地限制其讀取。這就是您的head -c 5測試有效的原因:GNUhead僅讀取五個字節,因此不需要尋求恢復文件描述符的位置。

如果您將文件寫入文件並改用它,您將獲得您所追求的行為:

$ echo -e "aaa\nbbb\nccc\nddd\n" > file
$ < file (while true; do head -n 1; head -n 1 >/dev/null; done)
aaa
ccc

引用自:https://unix.stackexchange.com/questions/409462