Совпадение любого символа (включая символы новой строки) в sed

У меня есть команда sed, которую я хочу запустить на огромном, ужасном, уродливом HTML файле, который был создан из документа Microsoft Word. Все, что он должен сделать, это удалить любой экземпляр строки

style='text-align:center; color:blue;
exampleStyle:exampleValue'

Команда sed, которую я пытаюсь изменить,

sed "s/ style='[^']*'//" fileA > fileB

Это отлично работает, за исключением того, что всякий раз, когда в соответствующем тексте есть новая строка, она не совпадает. Есть ли модификатор для sed или что-то, что я могу сделать, чтобы принудительно выполнить сопоставление любого символа, включая символы новой строки?

Я понимаю, что регулярные выражения ужасны в XML и HTML, blah blah blah, но в этом случае строковые шаблоны хорошо сформированы в том, что атрибуты стиля всегда начинаются с одной цитаты и заканчиваются одной цитатой. Поэтому, если бы я мог просто решить проблему новой строки, я мог бы сократить размер HTML более чем на 50% только с помощью одной команды.


В итоге оказалось, что Sinan Ünür perl script работал лучше всего. Это было почти мгновенно, и это уменьшило размер файла от 2,3 МБ до 850 тыс. Хороший ol 'Perl...

Ответ 1

sed просматривает строку входного файла по строке, что означает, что, как я понимаю, то, что вы хотите, невозможно в sed.

Вы можете использовать следующий Perl script (untested), хотя:

#!/usr/bin/perl

use strict;
use warnings;

{
    local $/; # slurp mode
    my $html = <>;
    $html =~ s/ style='[^']*'//g;
    print $html;
}

__END__

Один лайнер будет:

$ perl -e 'local $/; $_ = <>; s/ style=\047[^\047]*\047//g; print' fileA > fileB

Ответ 2

Sed считывает входные строки за строкой, поэтому выполнить непростую обработку по одной строке... но это тоже не невозможно, вам нужно использовать ветвление sed. Следующее будет работать, я прокомментировал его, чтобы объяснить, что происходит (не самый читаемый синтаксис!):

sed "# if the line matches 'style='', then branch to label, 
     # otherwise process next line
     /style='/b style
     b
     # the line contains 'style', try to do a replace
     : style
     s/ style='[^']*'//
     # if the replace worked, then process next line
     t
     # otherwise append the next line to the pattern space and try again.
     N
     b style
 " fileA > fileB

Ответ 3

Вы можете удалить все CR/LF с помощью tr, запустить sed, а затем импортировать в редактор, который автоматически форматируется.

Ответ 4

Вы можете попробовать следующее:

awk '/style/&&/exampleValue/{
    gsub(/style.*exampleValue\047/,"")
}
/style/&&!/exampleValue/{     
    gsub(/style.* /,"")
    f=1        
}
f &&/exampleValue/{  
  gsub(/.*exampleValue\047 /,"")
  f=0
}
1
' file

Вывод:

# more file
this is a line
    style='text-align:center; color:blue; exampleStyle:exampleValue'
this is a line
blah
blah
style='text-align:center; color:blue;
exampleStyle:exampleValue' blah blah....

# ./test.sh
this is a line

this is a line
blah
blah
blah blah....

Ответ 5

Другой способ:

$ cat toreplace.txt 
I want to make \
this into one line

I also want to \
merge this line

$ sed -e 'N;N;s/\\\n//g;P;D;' toreplace.txt 

Вывод:

I want to make this into one line

I also want to merge this line

N загружает другую строку, P печатает пространство шаблонов до первой новой строки, а D удаляет пространство шаблонов до первой новой строки.

Ответ 6

Удалить элементы XML через несколько строк

Мой вариант использования был почти таким же, но мне нужно было сопоставить открывающие и закрывающие теги из элементов XML и полностью удалять их --including, что бы ни было внутри.

<xmlTag whatever="parameter that holds in the tag header">
    <whatever_is_inside/>
    <InWhicheverFormat>
        <AcrossSeveralLines/>
    </InWhicheverFormat>
</xmlTag>

Тем не менее, sed работает в одной строке. Здесь мы обманываем его, чтобы добавить последующие строки к текущей, чтобы мы могли редактировать все строки, которые нам нравятся, а затем переписать вывод (\n - это допустимый символ, который можно вывести с помощью sed, чтобы снова разделить строки).

Вдохновленный ответом @beano и другим ответом в Unix stackExchange, я создал свою рабочую "программу" sed:

 sed -s --in-place=.back -e '/\(^[ ]*\)<xmlTag/{  # whenever you encounter the xmlTag
       $! {                                       # do
            :begin                                # label to return to
            N;                                    # append next line
            s/\(^[ ]*\)<\(xmlTag\)[^·]\+<\/\2>//; # Attempt substitution (elimination) of pattern
            t end                                 # if substitution succeeds, jump to :end
            b begin                               # unconditional jump to :begin to append yet another line
            :end                                  # label to mark the end
          }
       }'  myxmlfile.xml

Некоторые объяснения:

  • Я сопоставляю <xmlTag, не закрывая >, потому что мой элемент XML содержит параметры.
  • То, что предшествует <xmlTag, является очень полезным элементом RegExp для соответствия любому существующему отступу: \(^[ ]*\), так что вы можете позже вывести его только с помощью \1 (даже если в этот раз он не был нужен).
  • Добавление ; в нескольких местах так, что sed поймет, что команда (N, s или что-то еще) заканчивается там, а следующие символы являются другой командой.
  • большая часть моей проблемы заключалась в попытке найти RegExp, который соответствовал бы "чему-либо промежуточному". В конце концов я согласился на что угодно, кроме · (т.е. [^·]\+), рассчитывая на то, что этот символ не будет ни в одном из файлов данных. Мне нужно было убрать +, потому что он специально для GNU sed.
  • мои исходные файлы остаются как .back, на случай, если что-то пойдет не так --tests, но после modification-- все-таки произойдет сбой, и они легко помечаются контролем версий для массового удаления.

Я использую этот вид sed-автоматизации для развития файлов .XML, которые мы используем с сериализованными данными для запуска наших модульных и интеграционных тестов. Всякий раз, когда наши классы меняются (свободные поля или поля усиления), данные должны обновляться. Я делаю это с одним "find", который выполняет sed-автоматизацию в файлах, которые содержат измененный класс. Мы храним сотни XML файлов данных.