У коментарях до цього питання з’явився випадок, коли різні впровадження sed не погоджувались у досить простій програмі, і ми (або принаймні я) не змогли визначити, що саме для цього вимагає специфікація.
Проблема полягає в поведінці діапазону, що починається з видаленого рядка:
1d;1,2d
Чи слід видалити рядок 2, навіть якщо початок діапазону було видалено до досягнення цієї команди? Моє первісне очікування було "ні" відповідно до BSD sed, тоді як GNU sed каже "так", і перевірка тексту специфікації не повністю вирішує питання.
Мої очікування відповідають (принаймні), macOS і Solaris sed
, і BSD sed
. Не погоджуються (принаймні) GNU та Busybox sed
, і численні люди тут. Перші два мають сертифікат SUS, тоді як інші, мабуть, мають більш широке поширення. Яка поведінка правильна?
Текст специфікації для діапазонів з двома адресами говорить:
Потім утиліта sed повинна послідовно застосовувати всі команди, адреси яких вибирають простір шаблону, доки команда не запустить наступний цикл або не завершить роботу.
і
Команда редагування з двома адресами повинна вибирати діапазон включення з першого простору шаблону, який відповідає першій адресі, через наступний простір шаблону, що відповідає другому. [...] Починаючи з першого рядка після обраного діапазону, sed знову шукатиме першу адресу. Після цього процес повторюється.
Можливо, рядок 2 знаходиться в межах "включеного діапазону від першого простору шаблону, який відповідає першій адресі, до наступного простору шаблону, який відповідає другому", незалежно від того, чи була видалена початкова точка. З іншого боку, я очікував, що перший d
перейде до наступного циклу і не дасть діапазону шансів на старт. Сертифіковані UNIX ™ виконання виконують те, що я очікував, але потенційно не те, що вимагає специфікація.
Деякі ілюстративні експерименти йдуть, але ключове питання: що потрібно sed
робити , коли діапазон починається на віддаленій лінії?
Експерименти та приклади
Спрощена демонстрація проблеми полягає в цьому, що друкує додаткові копії рядків, а не видаляє їх:
printf 'a\nb\n' | sed -e '1d;1,2p'
Це забезпечує sed
два рядки введення a
та b
. Програма робить дві речі:
Видаляє перший рядок за допомогою
1d
.d
команда будеВидаліть простір шаблону і починайте наступний цикл. і
- Виберіть діапазон ліній від 1 до 2 і чітко їх роздруковує, крім автоматичного друку, який отримує кожен рядок. Рядок, включений до діапазону, повинен з'являтися двічі.
Моє сподівання було, що це слід надрукувати
b
тільки, якщо діапазон не застосовується, оскільки 1,2
він ніколи не досягається протягом 1-го рядка (тому що вже d
перейшов до наступного циклу / рядка), і тому включення діапазону ніколи не починається, поки a
його було видалено. Відповідні Unix sed
з macOS і Solaris 10 дають цей вихід, як і не-POSIX sed
в Solaris і BSD sed
в цілому.
GNU sed, з іншого боку, друкує
b
b
вказуючи , що він має інтерпретований діапазон. Це відбувається як в режимі POSIX, так і не. Сед Busybox має таку саму поведінку (але не однакова поведінка завжди, тому, схоже, це не є результатом спільного коду).
Подальші експерименти з
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/c/p'
printf 'a\nb\nc\nd\ne\n' | sed -e '2d;2,/d/p'
виявляє, що видається, що діапазон починається з видаленого рядка так, ніби він починається з наступного рядка. Це видно, оскільки /c/
не відповідає кінцевому діапазону. Використання /b/
для запуску діапазону веде себе не так, як 2
.
Початковий робочий приклад, який я використовував, був
printf '%s\n' a b c d e | sed -e '1{/a/d;};1,//d'
як спосіб видалити всі рядки до першого /a/
збігу, навіть якщо це знаходиться на першому рядку (для чого GNU sed використовував би 0,/a/d
- це було спробою сумісного POSIX-видання).
Запропоновано, що замість цього слід видалити до другого збігу, /a/
якщо перший рядок збігається (або весь файл, якщо немає другого збігу), що здається правдоподібним - але знову ж таки, це робить лише GNU sed. Випускаються як macOS sed, так і sedlar Solaris
b
c
d
e
для цього, як я і очікував (GNU sed видає порожній вихід, видаляючи неперерваний діапазон; Busybox sed друкує просто, d
і e
це явно неправильно незалежно від того, що). Як правило, я припускаю, що те, що вони пройшли тести на відповідність сертифікації, означають, що їх поведінка правильна, але достатньо людей підказало інакше, що я не впевнений, текст специфікації не є абсолютно переконливим, і тестовий набір не може бути ідеально всебічний.
Зрозуміло, що писати цей код сьогодні, зважаючи на невідповідність, практично не переносно, але теоретично він повинен бути скрізь еквівалентний тому чи іншому значенню. Я думаю, що це помилка, але я не знаю, проти якої реалізації (-ів) повідомити про це. Наразі я вважаю, що поведінка GNU та Busybox sed не відповідає специфікації, але я можу помилитися з цим.
Що тут вимагає POSIX?
ed
,sed
повністю обходячи його ?