Использование sed-скрипта:
#!/bin/sed -nf
: loop
H
s/\([^_]*_[^_]*\)_.*/\1/g
t clear_flag
: clear_flag
$! {
N
s/^\([^_]*_[^\n]*\)\n\(\1[^\n]*\)$/\2/
t loop
}
x
s/^\n//
s/\([^_]*_[^_]*\)_/\1=\1_/
s/\n/ /gp
s/.*//
x
D
Я постараюсь все объяснить. Во-первых, у нас есть цикл для объединения всех файлов, начинающихся с одного и того же префикса. Я определил префикс на основе ваших примеров, и он определяется как строка, заканчивающаяся вторым символом подчеркивания. Цикл определяется меткой с помощью команды «:». Здесь мы обозначили нашу петлю как «петля». Далее, при необходимости, мы «прыгаем» обратно к началу цикла с помощью тестовой команды «t».
Первая команда — добавить строку в Hold-space (вспомогательный буфер). Строка имеет префикс новой строки ('\n') автоматически с помощью sed, прежде чем она будет добавлена.
Вторая команда извлекает префикс. Мы делаем это, захватывая последовательность символов, не являющихся символами подчеркивания ([^_]*
), затем символ подчеркивания, а затем другие символы, не являющиеся символами подчеркивания. Поскольку этот шаблон находится между скобками с обратной косой чертой (\(
и \)
), sed захватит ввод, соответствующий этому шаблону, и сохранит его во вспомогательной переменной с именем \1
(поскольку это первый захват в этой строке). Затем мы пропускаем символ подчеркивания, за которым следует последовательность любых символов. Замена — это то, что мы захватили, поэтому на самом деле мы просто удалили все после второго подчеркивания, включая его.
Теперь мы используем обходной путь для очистки внутреннего флага seds, указывающего, произошла ли успешная замена с момента последней команды «t» или с момента запуска скрипта. Тестовая команда ("t") разветвится (перейдет) к метке, если команда подстановки завершится успешно, а затем очистит внутренний флаг. Это необходимо для нашей второй команды «t» ниже. В случае успеха или неудачи (т. е. если он разветвляется или нет), он все равно продолжит выполнение после метки «clear_flag».
Теперь мы используем команду «{», чтобы запустить группу команд. Однако у нас есть префикс адреса перед ним, который sed использует, чтобы определить, следует ли запускать эти команды или нет. В нашем случае группа выполняется только в том случае, если последняя прочитанная строка ввода не была последней строкой (символ доллара «$» представляет последнюю строку ввода, а «!» представляет отрицание).
Первая команда в группе добавит следующую строку из ввода в текущее пространство шаблонов (т.е. рабочий буфер). Предыдущая строка и новая строка разделяются символом новой строки (\n
).
Третья команда проверит, начинается ли новая прочитанная строка с нашего префикса, и удалит изолированный префикс (т.е. предыдущую строку). Поскольку мы удалили второе подчеркивание из префикса, который мы оставили в предыдущей строке, и поскольку мы добавили новую строку, изолированный префикс теперь заканчивается перед символом новой строки. Поэтому захваченный шаблон теперь читает символы, которые не являются новой строкой ([^\n]*
) после подчеркивания. После того, как мы захватили изолированный префикс, мы пропускаем символ новой строки, разделяющий предыдущую и новую строку, затем начинаем другой захват (который будет сохранен в \2
, потому что это второй захват в этой строке). Этот захват будет (надеюсь) соответствовать второй строке. Надеюсь, потому что мы требуем, чтобы совпадение начиналось точно так же, как то, что было сопоставлено в первом захвате (поэтому первая вещь во втором захвате — это обратная ссылка на первый захват, т. е. \1
). После этого мы сопоставляем последовательность символов, не являющихся символами новой строки, и после второго захвата ожидаем конец строки.
Если эта последняя команда подстановки успешна, мы обнаружили, что вновь прочитанная строка также имеет тот же префикс, поэтому теперь мы должны вернуться к началу цикла. Это функция команды "t". Он проверит, были ли выполнены какие-либо команды подстановки после последней команды «t», и если да, то перейдет к заданной метке. В нашем случае мы переходим (прыгаем) обратно к метке «loop». Теперь мы можем понять, почему нам понадобился предыдущий обходной путь «t». Без него первая подстановочная команда может завершиться успешно, в то время как та, которая нас действительно интересует, может завершиться ошибкой, а «t» все равно вернется к метке «loop».
Если он выходит из цикла, это означает, что новая прочитанная строка не имеет того же префикса. Поэтому теперь мы можем напечатать то, что было сопоставлено ранее.
Начнем с того, что заменим содержимое пространства шаблонов на содержимое пространства хранения с помощью команды exchange ("x"). Теперь наше пространство шаблонов содержит все файлы с одинаковым префиксом, а наше пространство хранения содержит текущий префикс в изолированной строке, а затем строку с первым файлом, который не использует тот же самый префикс.
Так как ранее мы добавили все имена файлов в область хранения, все имена файлов разделяются символами новой строки, и поскольку первое имя файла также было добавлено, первый байт в текущем пространстве шаблонов является символом новой строки. Чтобы удалить его, мы просто заменяем его ничем.
Теперь нам нужно сгенерировать формат задания. Вот почему у нас есть знакомая команда замены, мы снова извлекаем префикс, за исключением того, что теперь мы удалили .*
, чтобы сохранить остальную часть строки нетронутой. Замена включает в себя префикс (захвачено), знак равенства, а также восстанавливаем то, что мы удалили из первого файла в пространстве шаблона: его префикс и его подчеркивание.
Мы почти готовы распечатать строку, но имена файлов все еще разделены символами новой строки. Поэтому мы заменяем все новые строки (флаг g
указывает sed повторять команду замены в строке ввода столько раз, сколько это возможно) пробелами. Поскольку теперь строка готова, мы можем добавить префикс p
, чтобы указать sed напечатать ее.
Последние шаги — подготовка к повторному запуску скрипта для следующего префикса. Пространство хранения должно быть пустым, чтобы его можно было использовать для хранения имен файлов с новым префиксом. Вот у нас есть команда заменить каждый символ в пространстве шаблона ничем, за которым следует команда обмена.
Трюмное пространство готово. Теперь мы должны подготовить пространство для шаблона. Он должен содержать только первую строку имени файла с новым префиксом. Чтобы оказаться в этом состоянии, все, что нам нужно сделать, это удалить старый префикс, который хранится в первой строке. Мы могли бы сделать что-то вроде s/.*\n//
, чтобы заменить все символы, кроме символов последней строки (которая содержит имя файла с новым префиксом), но команда D
сделает это и заставит скрипт начать выполнение снова, не читая другую строку, так что это избавляет нас от необходимости печатать.
Хотя сценарий может быть немного загадочным, а описание громоздким, как только вы поймете, что происходит, все станет просто(r) =)
Что-то, что необходимо упомянуть: входные данные должны быть отсортированы (или, по крайней мере, файлы с одинаковыми префиксами должны быть сгруппированы вместе).
Надеюсь это поможет!
person
Janito Vaqueiro Ferreira Filho
schedule
02.10.2012