Переупорядочить несколько линейных блоков с помощью Sed

Я пытаюсь изменить порядок сгенерированного вывода следующим образом; учитывая фрагмент tex следующим образом, я хочу переместить /^Constant/ down ниже /Dummies/ , с линией до и после /^Constant/ . Кроме того, мне бы хотелось получить стабильный результат, который не вносит изменений, если Constant уже находится ниже последней строки Dummies (он работает как часть скрипта, чтобы исправить мои результаты исследований, и до сих пор этот сценарий стабилен).

Это вход фрагмента tex:

 [1em] Application Grade& & & -0.0857\sym{***}& -0.00412\sym{***}\\ & & & (0.0149) &(0.00107) \\ [1em] Constant & -3.701\sym{***}& -0.311\sym{***}& 0 & 0 \\ & (1.130) & (0.0853) & (.) & (.) \\ [1em] Major Dummies & No & No & Yes & Yes \\ [1em] Semester Dummies & No & No & Yes & Yes \\ \hline 

Это желаемый результат, который остается стабильным (возвращает то же самое), если он задан как вход:

 [1em] Application Grade& & & -0.0857\sym{***}& -0.00412\sym{***}\\ & & & (0.0149) &(0.00107) \\ [1em] Major Dummies & No & No & Yes & Yes \\ [1em] Semester Dummies & No & No & Yes & Yes \\ [1em] Constant & -3.701\sym{***}& -0.311\sym{***}& 0 & 0 \\ & (1.130) & (0.0853) & (.) & (.) \\ \hline 

Следующий код будет делать это, но он нестабилен. В основном он совпадает с Constant , капли и повторные вставки [1em] (что он должен делать на отдельный вызов, так как i\ влияет на результат, а не на пространство рисунка) и пасты ниже цели. Он нестабилен, потому что он добавит фиктивные [1em] s после Semester Dummies . Честно говоря, его немного уродливое (требуя отдельных вызовов sed ).

 sed -E -i'' -e'/^Constant/ {N;h;N;d;}; /^Semester Dummies/ {G;};' fragment.tex sed -E -i'' -e '/^Semester Dummies/ {a\ \[1em\] };' fragment.tex 

Я считаю, что один лайнер сделает это, сопоставляя многострочный шаблон / ^\[ /^\[1em\]\nConstant.*/ (после вызова N;N; ), помещая его в h старый буфер, а затем вставляя его ( G; ) после /Semester Dummies/ . Но после многих часов, man-страниц, веб-поиска и обновления для gnused для хорошей меры, я не могу заставить такой скрипт работать. Это выражает основную идею, но недействительно (s не может условно h как я здесь):

 sed -E -i'' -e'/\[1em\]/ {N;N;s/^\[1em\]\nConstant.*//h;}; /Semester Dummies/ {G;}' fragment.tex 

Я пробовал много вариантов, полагаясь на N;P;D;h;G; с различной адресацией, но я все еще не понимаю порядок выполнения для многократно адресуемых команд, когда вы возитесь с пространством шаблонов (вызывая N; ). N и P работают красиво, но, насколько я могу судить, D совершенно бесполезен.

Да, я знаю, что проще с awk и perl ; на самом деле, мне было бы интересно увидеть все три решения для сравнения – но здесь я специально спрашиваю, как это сделать в sed .

  • Извлечение нескольких экземпляров текста между теми же двумя строками текста
  • Почему это совмещение регулярных выражений?
  • Сопоставьте два слова, которые находятся на одной строке
  • Удаление записи пользователя из / etc / passwd
  • Как включить все до Colon в Sed / Grep / ...?
  • удаление строк между строками в текстовом файле с помощью awk или sed
  • Извлечь определенный текст из переменной в сценарии оболочки
  • sed шаблон, за которым следует один одиночный символ один или несколько раз
  • 2 Solutions collect form web for “Переупорядочить несколько линейных блоков с помощью Sed”

    Вот уродливый кусок awk, который играет трюки с разделителем записей

     awk -v RS='\\[1em\\]\n|\\\\hline' ' !/[^[:space:]]/ {next} /^Constant/ {c=$0; next} {printf "[1em]\n%s", $0} END {printf "[1em]\n%s\\hline\n", c} ' 

    Поскольку текст начинается и заканчивается «разделителем записи», есть некоторые ложные записи с пустыми-иш, следовательно, первое правило

    Вот один из способов сделать это с помощью sed с помощью буфера удержания:

     sed '/\[1em\]/{N;/Constant/{N;x;/^$/d;x};/Semester/{x;/^$/!{H;x};//g}}' file 

    В каждой строке, соответствующей [1em] она читается в строке N ext, а затем

    1. Если пространство шаблонов соответствует Constant она читает в другой строке, e x изменяет буферы, и если пространство шаблонов – это просто пустая строка (это означает, что буфер удержания был пуст перед обменом), он будет изменен. Если пространство шаблонов не является пустой линией (что означает, что в пространстве удержания было что- то из-за 2 ), e x снова меняет буферы, возвращая строки в пространстве шаблонов.
    2. Если пространство шаблонов совпадает с Semester e x изменяет буферы, тогда, если пространство [1em]\nConstant.* не пусто, это означает, что строки [1em]\nConstant.* Были в буфере удержания, поэтому он добавляет их в [1em]\nSemester.* (которые теперь находятся в буфере удержания), а затем e x снова изменяет буферы. Если пространство шаблонов пуст, это означает, что строки [1em]\nConstant.* Находятся после строки Semester поэтому он просто копирует пространство удержания над пространством шаблона. Таким образом восстанавливаются строки [1em]\nSemester.* , Но теперь есть что-то в буфере удержания, когда оно достигает 1 .

    Таким образом, строки [1em]\nConstant.* только в том случае, если они есть до [1em]\nSemester.* , В противном случае ничего не происходит.

     sed '/\[1em\]/{ # if line matches [1em] N # read in the next line /Constant/{ # if pattern space matches Constant N # read in another line x # exchange buffers /^$/d # if pattern space is now empty, delete it x # otherwise, exchange again } /Semester/{ # if pattern space matches Semester x # exchange buffers /^$/!{ # if pattern space is not empty H # append it to hold space x # then exchange buffers } //g # if pattern space is currently empty } # copy the hold space over the pattern space }' infile # so now the hold space is no longer empty 

    Это проще с ed . Просто выберите диапазон строк и переместите его после строки, соответствующей Semester :

     ed -s infile <<<$'/Constant/-1,/Constant/+1m/Semester/\n,p\nq' 

    замените ,p на w чтобы записать изменения в файл:

     ed -s infile <<IN /Constant/-1,/Constant/+1m/Semester/ w q IN 
    Linux и Unix - лучшая ОС в мире.