Как отформатировать CSS в одну строку с помощью awk macOS (POSIX)

Входной файл представляет собой минимизированный файл css:

.class{margin:0px}.class1,.class2{margin 0px}@media{.class{color:blue}.class1,.class2{color:red}}@media{.classA.classB,.classC{margin:0px}}@media{.classD,.classE{color:blue}.class1,.class2{color:red}}@media only screen and (min-width: 1441px){.classX{color:blue}}@media only screen and (min-width: 1441px){.class{color:blue}.class1,.class2{color:red}}@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}}@media only screen and (inverted-colors){.classD,.classE{color:blue}.class1,.class2{color:red}.classV{color:red}.classR{color:red}.classU{color:red}.classS{color:red}.classT{color:red}}.classNew{margin: 10px}

Ожидаемый результат:

.class{margin:0px}
.class1,.class2{margin 0px}
@media{.class{color:blue}.class1,.class2{color:red}}
@media{.classA.classB,.classC{margin:0px}}
@media{.classD,.classE{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classX(color:blue}}
@media only screen and (min-width: 1441px){.class{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}}
@media only screen and (inverted-colors){.classD,.classE{color:blue}.class1,.class2{color:red}.classV{color:red}.classR{color:red}.classU{color:red}.classS{color:red}.classT{color:red}}
.classNew{margin: 10px}

Когда я использую это:

awk '{gsub(/\t?}/, "}\n"); print}'

Это дает следующий результат, который не соответствует ожидаемому результату выше:

.class{margin:0px}
.class1,.class2{margin 0px}
@media{.class{color:blue}
.class1,.class2{color:red}
}
@media{.classA.classB,.classC{margin:0px}
}
@media{.classD,.classE{color:blue}
.class1,.class2{color:red}
}
@media only screen and (min-width: 1441px){.classX(color:blue}
}
@media only screen and (min-width: 1441px){.class{color:blue}
.class1,.class2{color:red}
}
@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}
}
@media only screen and (inverted-colors){.classD,.classE{color:blue}
.class1,.class2{color:red}
.classV{color:red}
.classR{color:red}
.classU{color:red}
.classS{color:red}
.classT{color:red}
}
.classNew{margin: 10px}

Вот моя идея получить ожидаемый результат:

найти {
тогда
после этого проверьте, является ли следующий символ } или {
если }
тогда
добавить новую строку после }
если {
после этого проверьте, является ли следующий символ }}
если }}
тогда
добавить новую строку после }}
пройтись по входному файлу

Если предложенный вами подход не работает так, как вам хотелось бы, не могли бы вы сказать, почему он не делает то, что вы хотите?

Simon 27.11.2022 04:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Использование GNU sed

$ sed -E 's/[^}]*}}?([^}]*}})?/&\n/g' input_file
.class{margin:0px}
.class1,.class2{margin 0px}
@media{.class{color:blue}.class1,.class2{color:red}}
@media{.classA.classB,.classC{margin:0px}}
@media{.classD,.classE{color:blue}.class1,.class2{color:red}}

спасибо .. это не работает для большого количества классов или большого файла CSS.

Sharad Shrestha 27.11.2022 05:46

например: экран только @media и (минимальная ширина: 1441px){.classX(цвет:синий}}экран только @media и (min-width: 1441px){.class{color:blue}.class1,.class2{ color:red}}@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}}@media only screen and (inverted-colors){.classD,.classE{color: синий}.class1,.class2‌​{цвет:красный}.classV{c‌​цвет:красный}.classR{цвет‌​или:красный}.classU{цвет‌​:красный}.classS{цвет:r‌​ed}.classT {цвет: красный‌​}}.classNew{поле: 10px}

Sharad Shrestha 27.11.2022 05:46

@SharadShrestha Каков ожидаемый результат для образца? Кажется, это работает с моей стороны, хотя фигурная скобка неуместна

HatLess 27.11.2022 06:14

Я обновил входной файл и ожидаемый результат.

Sharad Shrestha 27.11.2022 06:30
Ответ принят как подходящий

двухэтапная awk процедура

У меня есть процедура, которая работает для вашего примера, но мне пришлось заново интерпретировать правила, и, возможно, она может не работать для каждого случая, который вы хотите обработать, если мои правила неверны (извините, если это так).

интерпретация

Процесс, к которому я стремился:

  1. Если первый открытый блок стиля { в строке закрывается (}) до открытия другого, новая строка начинается сразу после закрытия этого блока.

  2. Правило @media может быть только в начале строки.

  3. для блока, содержащего другие блоки и, таким образом, заканчивающегося на }}, новая строка всегда следует за двойным замыканием.

обзор подхода

Процедура использует две процедуры awk, первая получает входные данные из файла данных, а вторая принимает свои входные данные из выходных данных первой. Сначала я объясню их работу по отдельности, а затем объясню комбинированную процедуру, объединенную с помощью конвейера.

первый неловкий шаг

Этот первый шаг awk изменяет строку ввода, чтобы учесть правило 3 выше (что }} может быть только в конце строки). Это достигается путем установки разделителя полей awk на двойное закрытие с помощью FS = "}}". Цикл используется для печати каждого поля (за которым следует }}, которое в противном случае теряется), в результате чего awk разделяет поля на свои собственные строки с разделителем выходных записей по умолчанию ORS (новая строка):

awk 'BEGIN{FS = "}}"} {for(i=1;i<NF;i++) print $i"}}"; print $NF}' mini.css

Этот первый awk шаг приводит к следующему результату (его не нужно сохранять в файл, так как он будет «перенаправлен» во вторую awk процедуру позже):

.class{margin:0px}.class1,.class2{margin 0px}@media{.class{color:blue}.class1,.class2{color:red}}
@media{.classA.classB,.classC{margin:0px}}
@media{.classD,.classE{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classX(color:blue}}
@media only screen and (min-width: 1441px){.class{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}}
@media only screen and (inverted-colors){.classD,.classE{color:blue}.class1,.class2{color:red}.classV{color:red}.classR{color:red}.classU{color:red}.classS{color:red}.classT{color:red}}
.classNew{margin: 10px}

Обратите внимание, что на этом этапе все строки, кроме последней, заканчиваются на }}, а }} никогда не существует внутри строки. Правило 3 выше удовлетворяется этим шагом.

второй неправильный шаг Второй шаг awk получает вышеупомянутый вывод от первого шага через канал. Он выполняет две функции:

Во-первых, процедура проверяет, закрыт ли первый открывающий блок { в строке } перед открытием другого. Если он закрыт, новая строка вводится для оставшейся части строки, если только для конкретного случая, когда вторая } происходит только после одного открытия {, как в третьей строке вывода выше @media only screen and (min-width: 1441px){.classX(color:blue}} (это может быть опечатка в исходном файле, где ( следующее .classX должно было быть {). Проверка станет неактуальной, но безвредной, если это была опечатка и ее исправят. (см. заключительное примечание для исправления).

Проверка производится путем разделения строки ввода на поля, разделенные знаком {. Разделитель полей устанавливается в блоке BEGIN. В то же время разделитель полей вывода устанавливается на ту же открывающую фигурную скобку, чтобы заменить { при печати полей:

BEGIN{FS = "{"; OFS = "{"}

Теперь, если в поле 2 встречается }, то он закрывает первый блок до того, как был открыт другой, и поэтому после закрытия с помощью замены вставляется разрыв строки:

$2~"}"{if (NF>2) sub("}","}\n",$2);}

Обратите внимание на использование условия pattern$2~"}", которое применяет действие, только если } появляется в поле 2. Это удовлетворяет правилу 1 выше.

Во-вторых, процедура проверяет каждое поле после первого, чтобы увидеть, содержит ли оно медиа-запрос, если присутствует @, выполняется замена, чтобы вставить разрыв строки перед ним, чтобы медиа-запрос начинался с новой строки (правило 2).

Последний блок action во второй awk процедуре печатает измененную строку.

Вторая процедура полностью следует (обратите внимание, что это не будет работать без передачи в нее вывода первой процедуры или в виде файла:

awk 'BEGIN{FS = "{"; OFS = "{"} $2~"}"{if (NF>2) sub("}","}\n",$2);} {for(i=2;i<=NF;++i) sub("@","\n@",$i)} {print}' 

вся процедура

Вот две процедуры, объединенные трубой:

awk 'BEGIN{FS = "}}"} {for(i=1;i<NF;i++) print $i"}}"; print $NF}' mini.css | awk 'BEGIN{FS = "{"; OFS = "{"} $2~"}"{if (NF>2) sub("}","}\n",$2);} {for(i=2;i<=NF;++i) sub("@","\n@",$i)} {print}'

Обратите внимание, что файл данных, названный в данном случае mini.css, является аргументом первой процедуры awk. Выходные данные первой процедуры передаются во вторую.

результаты теста

(Протестировано на терминале Mac с использованием GNU Awk 5.2.0)

Это вывод данных примера одной строки в вопросе, сохраненных как mini.css и обработанных с использованием комбинированных скриптов awk выше в рамках всей процедуры:

.class{margin:0px}
.class1,.class2{margin 0px}
@media{.class{color:blue}.class1,.class2{color:red}}
@media{.classA.classB,.classC{margin:0px}}
@media{.classD,.classE{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classX(color:blue}}
@media only screen and (min-width: 1441px){.class{color:blue}.class1,.class2{color:red}}
@media only screen and (min-width: 1441px){.classA.classB,.classC{margin:0px}}
@media only screen and (inverted-colors){.classD,.classE{color:blue}.class1,.class2{color:red}.classV{color:red}.classR{color:red}.classU{color:red}.classS{color:red}.classT{color:red}}
.classNew{margin: 10px}

примечание относительно возможной опечатки Если эта часть исходных данных:

@media only screen and (min-width: 1441px){.classX(color:blue}}

должен был быть:

@media only screen and (min-width: 1441px){.classX{color:blue}}

тогда if (NF>2) во второй процедуре становится лишним. Рабочие объединенные процедуры будут тогда:

awk 'BEGIN{FS = "}}"} {for(i=1;i<NF;i++) print $i"}}"; print $NF}' mini.css | awk 'BEGIN{FS = "{"; OFS = "{"} $2~"}"{sub("}","}\n",$2);} {for(i=2;i<=NF;++i) sub("@","\n@",$i)} {print}'

Другие вопросы по теме