У меня есть следующий текстовый файл:
#unimportant comment
#possible more unimportant comments
#info1 info2 info3 ,importantname1
importanttext1
#info1 info2 info3 ,importantname2
importanttext2
#info1 info2 info3 ,importantname3
importanttext3
Я хочу разбить каждый файл на отдельные файлы. Все, что мне действительно нужно, это извлечь не прокомментированные URL-адреса, сохранение комментариев необязательно. Я хочу, чтобы каждый файл был назван, например, importantname1.txt или именем, следующим за запятой в конце каждой строки комментария, с добавлением .txt
поэтому importantname1.txt будет иметь следующее содержимое:
importanttext1
или возможно
#info1 info2 info3 ,importantname1
importanttext1
поэтому строка будет извлечена и сохранена с именем файла после комментария и добавлена с расширением .txt, в данном случае filename importantname1.txt
Мне нужно сделать это для каждого набора строк в файле примера. Сохранение комментариев неважно, но мне нужно, чтобы это можно было использовать в скриптах. Мне также нужно учесть неизвестное количество строк комментариев в заголовке. Строка комментария всегда будет перед каждой строкой importanttextX
решение1
Пытаться:
awk -F, '/^#/{f=$NF".txt";cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
Пример
Применительно к вашему образцу входных данных:
$ awk -F, '/^#/{f=$NF".txt";cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
После выполнения вышеуказанного в каталоге появятся следующие файлы:
$ ls
file importantname1.txt importantname2.txt importantname3.txt
Содержимое новых файлов:
$ cat importantname1.txt
#info1 info2 info3 ,importantname1
importanttext1
$ cat importantname2.txt
#info1 info2 info3 ,importantname2
importanttext2
$ cat importantname3.txt
#info1 info2 info3 ,importantname3
importanttext3
Как это работает
Awk читает входной файл построчно. Наш скрипт классифицирует эти строки как комментарии или не-комментарии. Для строк комментариев имя файла и комментарий сохраняются. Для не-комментариев создается новый файл и печатается
`-Ф,
Это говорит awk использовать запятую в качестве разделителя полей при вводе. Таким образом, имя файла всегда будет последним полем.
/^#/{f=$NF".txt";cmt=$0; next}
Если строка начинается с
#
, мы сохраняем последнее поле,$NF
, плюс.txt
как имя файлаf
. Вся строка комментария сохраняется какcmt
. Затем мы говорим awk пропустить оставшиеся команды и перейти к началу строки зановоnext
.printf "%s\n%s\n",cmt,$0 >f; close(f)
Для строк, не являющихся комментариями, мы печатаем последний просмотренный комментарий,
cmt
, и текущую строку,$0
, в имя файла последнего просмотраf
. Затем мы закрываем дескриптор файла дляf
.
Защита от плохих имен файлов
Если поля, которые должны использоваться в качестве имен файлов, содержат /
, ОС будет интерпретировать имена файлов как включающие каталоги. Чтобы избежать этого, мы можем заменить все /
на -
using gsub(/\//, "-", f)
следующим образом:
awk -F, '/^#/{f=$NF".txt";gsub(/\//, "-", f); cmt=$0; next} {printf "%s\n%s\n",cmt,$0 >f; close(f)}' file
решение2
Комбинация grep
и csplit
может выполнить эту работу, путем а) grep
пинга всех не прокомментированных строк плюс предыдущей информационной строки и б) разделения вывода на основе информационной строки комментария:
grep -v -B1 '^#' file | csplit -z - '/^#/' '{*}'
Т.е. не -v
извлекать строки, которые имеют # в начале, ^#
а одну строку, предшествующую таким строкам -B1
. Затем разделить входящий конвейерный ввод -
по каждому # в начале строки, игнорировать пустые файлы -z
и делать это как можно чаще {*}
.
Переименование должно быть отдельным шагом ( csplit
автоматически присваивает выходному отверстию имена xx00, xx01 ... - измените префикс и суффикс с помощью опций -f
и -b
соответственно)
#/bin/bash
for f in xx* ; do
mv "$f" "$( sed -n '2p' "$f" )".txt
done