Скрипт для grep xml-файла и перенаправления вывода в файлы на основе результатов

rm *отлично работает, если ваш каталог не содержит подкаталогов (см. ответ @kos). Однако, если вам нужно рекурсивное решение, которое также удаляет все подкаталоги, то (, как указал @meesern ,)rm -r *не то, что вам нужно, поскольку действительно удаляет скрытые файлы и подкаталоги.

Вместо этого вы можете использовать findдля отображения и удаления не -скрытых файлов и подкаталогов (, адаптированных из этого ответа):

find YOUR_DIRECTORY -not -path '*/\.*' -delete

Это приведет к удалению всех не-скрытых файлов в YOUR_DIRECTORYи всех подкаталогах, которые стали пустыми после удаления не-скрытых файлов.

Он не будет удалять содержимое внутри скрытого каталога (, например. YOUR_DIRECTORY/.foo/bar/baz). В результате вы можете получить некоторые предупреждения, такие как

find: cannot delete 'some/directory/here': Directory not empty

Но вы можете смело игнорировать их.

1
26.01.2020, 02:23
3 ответа

Рассматривали ли вы цикл по каждой категории? Вот так:

for i in *.xml; do
    for category in $(sed -rn '/^<category>/{s/[^>]*> *([^ <]*).*/\1/p}' "$i"); do
        sed -rn "/^<category> *$category/,/^<category>/{s/<url> *([^ <]*).*/\1/p}" "$i" > "$category.log"
    done
done

Обновление :с помощью awk

awk -v 'RS=<' -v 'cat=none' -F '>' \
'$1 ~ /^category$/ {gsub(/^ *| *$/,"",$2); cat=$2} \
$1 ~ /^url$/ {print $2 >> cat".log"}' \
*.xml
  • Это позволяет избежать зацикливания входных файлов и добавит к файлу .logлюбую категорию.

  • Использование назначения разделителя записей awk -v 'RS=<'будет означать, что тег категории/url будет найден где угодно (, а не только в начале строки ). Новая строка может появиться в любом месте данных xml.

  • Сочетание этого с установкой разделителя полей на '>'означает, что первое поле каждой записи будет эквивалентно имени тега xml.

  • Каждый раз, когда awk встречает запись, где первым полем является «категория», переменная catустанавливается на имя этой категории.

  • Когда awk встречает запись, где первым полем является «url», он добавит этот URL в файл cat.log.

  • catбудет определено как noneдля начала. Это предотвращает сбой в случае, если <url>встречается без какого-либо предшествующего <category>.

  • Замена gsub(/^ *| *$/,"",$2)предназначена для удаления начальных/конечных пробелов для имен категорий, которые появляются в вашем примере входного файла .xml.


Примечание:

Ни один из вышеперечисленных способов не является надежным. Для правильных входных файлов xml лучше использовать реальный парсер xml, например xmlstarlet . Но для этого также потребуются правильно сформированные xml-файлы (, в примере ввода нет соответствующих тегов <category>, например ).

2
27.01.2020, 23:29

Я приготовил это решение:

grep -hP "<category.*>|<url.*>" *.xml | cut -d ">" -f 2 | cut -d "<" -f 1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | gawk 'BEGIN { category = ""; } { if (!length($0)) { next; } if (length(category)) { printf("\necho -e \"%s\" >> \"%s.log\"", $0, category); category = ""; } else { category = $0; } } END { printf("\n"); }' | bash

Он ищет все файлы.xml в текущем каталоге и добавляет URL-адрес к файлу, названному в соответствии с категорией, найденной в строке, предшествующей URL-адресу (вы можете проверить вывод, удалив | bash в конце ).

Извлекать узлы XML только для интересующих нас данных

Разрешив, например, grep ()искать шаблон в файлах с именами*.xml , нам не нужно перебирать имена файлов. Опция-h для grep подавляет имена файлов в выводе. Шаблон, переданный grep, является Perl -совместимым регулярным выражением(-P)

Извлечь значения для интересующих нас узлов

Строки, возвращаемые командой grep, явно выглядят так, как показано ниже:

    <category> MyMusic </category>
    <url>https://www.youtube.com/watch?v=waAlgFq9Xq8123</url>
    <category> MyMovies </category>
    <url>https://www.youtube.com/watch?v=g4U4BQW9OEk456</url>
    <category>Music</category>
    <url>https://www.youtube.com/watch?v=waAlg</url>
    <category>              Music </category>
    <url>https://www.youtube.com/watch?v=waAlgFq9Xq8</url>
    <category> Movies </category>
    <url>https://www.youtube.com/watch?v=g4U4BQW9OEk</url>

мы только что отфильтровали строки с данными, которые нам не нужны. Теперь нам нужно извлечь значения внутри узлов, что сводится к извлечению данных между открывающим и закрывающим тегами, т.е. между знаками > и <(нам все равно какой узел это так, поэтому мы используем «общий» подход ).

Этого можно легко добиться с помощью| cut -d ">" -f 2 | cut -d "<" -f 1

что практически означает брать все с правой стороны (-f 2 )знака > затем, с новым результатом, который у нас есть, возьмите все в левой части (-f 1 )знака <

Что оставляет нас ниже

 MyMusic 
https://www.youtube.com/watch?v=waAlgFq9Xq8123
 MyMovies 
https://www.youtube.com/watch?v=g4U4BQW9OEk456
Music
https://www.youtube.com/watch?v=waAlg
                Music 
https://www.youtube.com/watch?v=waAlgFq9Xq8
 Movies 
https://www.youtube.com/watch?v=g4U4BQW9OEk

Теперь нам нужно обрезать значения, вот небольшой корректирующий шаг.

Обрезать значения

Обрезка начальных и конечных пробелов с помощью| sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'

С помощью-e sed может выполнять сценарии в том порядке, в котором они заданы, без необходимости передавать дополнительную команду sed (или несколько команд sed для других сценариев ).

Первый скрипт, переданный в sed, обрезает начальный пробел (, т. е. любые [:пробел :] символы в начале строки (@ каждой строки )), а второй обрезает конечный пробел. (т.е. любые [:пробелы :] символы перед концом строки (в каждой строке ).

Теперь у нас есть что-то вроде следующего, и мы почти закончили:

MyMusic
https://www.youtube.com/watch?v=waAlgFq9Xq8123
MyMovies
https://www.youtube.com/watch?v=g4U4BQW9OEk456
Music
https://www.youtube.com/watch?v=waAlg
Music
https://www.youtube.com/watch?v=waAlgFq9Xq8
Movies
https://www.youtube.com/watch?v=g4U4BQW9OEk

Запись команд добавления файла в стандартный вывод

Точно так же, как мы пишем эхо-команды для добавления данных в файл, нам нужно что-то, что автоматизирует процесс. Я решил продолжить с gawk. gawk считывает данные построчно и записывает категорию в переменную. Когда он читает другую строку, если переменная категории не пуста, то строка содержит URL-адрес. Используя эту технику, мы можем просто вводить такие команды, как echo -e "current url" >> current _category.log

Обратите внимание на императивное использование >> для добавления новых данных в файл. Использование > запишет только последний URL, и мы получим одну строку для каждой категории!

В результате мы только что записали данные, как показано ниже, в стандартный вывод:

echo -e "https://www.youtube.com/watch?v=waAlgFq9Xq8123" >> "MyMusic.log"
echo -e "https://www.youtube.com/watch?v=g4U4BQW9OEk456" >> "MyMovies.log"
echo -e "https://www.youtube.com/watch?v=waAlg" >> "Music.log"
echo -e "https://www.youtube.com/watch?v=waAlgFq9Xq8" >> "Music.log"
echo -e "https://www.youtube.com/watch?v=g4U4BQW9OEk" >> "Movies.log"

Передать команды добавления данных в bash для выполнения

Последний элемент конвейера | bashобеспечивает передачу эхо-команд в bash для выполнения.

Обратите внимание, что gawk может записывать/добавлять данные в файлы. Но я намеренно хотел иметь наименьший возможный gawk-скрипт.

0
27.01.2020, 23:29
Tested with Below command and Worked fine

команда

for i in `awk '/\<category\>/{print $2}' filename`; do sed -n '/'$i'/{n;p}' filename | awk -F ">" '{gsub(/<.*/,"",$2);print $2}' > $i.log; done

выход

 cat Music.log 
https://www.youtube.com/watch?v=waAlgFq9Xq8

cat Movies.log 
https://www.youtube.com/watch?v=g4U4BQW9OEk
0
27.01.2020, 23:29

Теги

Похожие вопросы