В комментариях к этому вопросу возник случай, когда различные реализации 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
s macOS и Solaris 10 выводят этот вывод, как и non-POSIX sed
в Solaris и BSD sed
в целом.
GNU sed, с другой стороны, печатает
b
b
указывая , что он имеет интерпретирован диапазон. Это происходит как в режиме POSIX, так и нет. Sed 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 и Solaris sed производят
b
c
d
e
для этого, как я и ожидал (GNU sed выдает пустой вывод при удалении неопределенного диапазона; Busybox sed печатает только d
и e
, что явно неверно, несмотря ни на что). Как правило, я предполагаю, что их прохождение сертификационных тестов на соответствие означает, что их поведение правильное, но достаточно людей предположили, что в противном случае я не уверен, текст спецификации не совсем убедителен, и набор тестов не может быть совершенно всеобъемлющий
Очевидно, что на сегодняшний день этот код практически не переносится, учитывая несоответствие, но теоретически он должен быть везде эквивалентен с одним или другим значением. Я думаю, что это ошибка, но я не знаю, против какой реализации сообщать об этом. В настоящее время я считаю, что поведение GNU и Busybox sed несовместимо со спецификацией, но я могу ошибаться в этом.
Что здесь требуется POSIX?
ed
, в обходsed
вообще?