Чому "sed q" працює по-різному, коли читаєш з труби?


25

Я створив тестовий файл під назвою 'test', який містить таке:

xxx
yyy
zzz

Я запустив команду:

(sed '/y/ q'; echo aaa; cat) < test

і я отримав:

xxx
yyy
aaa
zzz

Потім я побіг:

cat test | (sed '/y/ q'; echo aaa; cat)

і отримав:

xxx
yyy
aaa

Питання

sedчитає і друкує, поки не зустріне рядок з 'y', а потім зупиниться. У першому випадку, але не у другому, кішка читає та друкує решту.

Чи може хтось пояснити, яке явище стоїть за цією різницею в поведінці?

Я також помітив, що це працює так в Ubuntu 16.04 і Centos 6, але в Centos 7 жодна команда не друкує "zzz".


Моя здогадка полягає в тому, що cat(в підшарці) в першому випадку можна повторно використовувати дескриптор файлів, оскільки stdin пов'язаний з реальним файлом. У другому випадку stdin - це з труби, а не з реального файлу. Зверніть увагу, що також (sed '/y/ q'; echo aaa; cat) < <(cat test)не друкується zzz.
Мартін Ньольт

1
Простіший приклад: (head -n1; head -n1) < testіcat test | (head -n1; head -n1)
Мартін Ньольт

Відповіді:


22

Коли вхідний файл можна шукати (як читання з звичайного файлу) або не шукати (наприклад, читати з труби), sed(та інші стандартні утиліти) будуть вести себе по-різному (Прочитайте INPUT FILESрозділ за цим посиланням ).

Цитата від doc:

Коли стандартна утиліта зчитує шуканий вхідний файл і закінчується без помилки до того, як він дійде до кінця файлу, утиліта повинна гарантувати, що зміщення файлу у відкритому описі файлу належним чином розміщене біля останнього байта, обробленого утилітою.

Так у:

(sed '/y/ q'; echo aaa; cat) < test

sedвиконується qкоманда uit до досягнення EOF, тому вона залишає зсув файлу на початку zzzрядка, тому catможе продовжувати друкувати залишкові рядки (GNU sed не є POSIX сумісним у певних умовах, див. нижче).

І продовжуючи від doc:

Для файлів, які не можна знайти, стан зсуву файлу у відкритому описі цього файлу не визначено

У цьому випадку поведінка не визначено. Більшість стандартних інструментів, включають sed, споживають вхід максимально. Він читає, що проходить yyyрядок, і quit без відновлення зміщення файлу, тому нічого не залишається cat.


GNU sedне відповідає стандарту, залежить від впровадження stdio системи та версії glibc:

$ (gsed '/y/ q'; echo aaa; cat) < test
xxx
yyy
aaa

Тут результат був отриманий від Mac OSX 10.11.6, віртуальних машин Centos 7.2 - glibc 2.17, Ubuntu 14.04 - glibc 2.19, які запускаються на Openstack з бекенда CEPH.

У цих системах ви можете використовувати -uпараметр для досягнення стандартної поведінки:

(gsed -u '/y/ q'; echo aaa; cat) </tmp/test

і для труби:

$ cat test | (gsed -u '/y/ q'; echo aaa; cat)
xxx
yyy
aaa
zzz

що призводить до жахливо неефективної продуктивності, тому що sedдоводиться читати один байт за один раз. Частковий вихід strace:

$ strace -fe read sh -c '{ sed -u "/y/q"; echo aaa; cat; } <test'
...
[pid  5248] read(3, "", 4096)           = 0
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "x", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
xxx
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "y", 1)             = 1
[pid  5248] read(0, "\n", 1)            = 1
yyy
...

1
Для GNU sedце залежить від впровадження stdio системи. У системах GNU (з GNU libc) GNU sedбуде сумісний, як exit()і назад для файлів, якими керує stdio.
Стефан Шазелас

@ StéphaneChazelas: Як це підтвердити? З моїм Centos 7.2, Ubuntu 14.04 VM, sedне сумісний, у мого ноутбука манджаро - у всіх однакова sed версія 4.2.2
cuonglm

@ StéphaneChazelas: Звучить так, що щось сталося під капотом. На моїх віртуальних машинах strace -f sh -c '{ sed "/y/q"; echo aaa; cat; } <test'покажіть, що жодного lseek()не виконували, тоді як в моєму манджаро lseek()раніше називали exit_group().
cuonglm

Я припускаю, що це до версії GNU libc. Ви можете протестувати за допомогою main() { char buf[999]; gets(buf); }'програми.
Стефан Шазелас

1
@ StéphaneChazelas: підтверджено. В обох моїх вітринах є 2,17 і 2,19, а у мого манджаро - 2,23. Це вважають помилкою glibc? Чи є у вас інформація про зміну між версіями glibc
cuonglm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.