Как фильтровать пару скобок

Рассматриваемая строка:

RUN echo '[local]\nlocalhost\n' > /etc/ansible/hosts

, похоже, запускает команду echo и используйте символ перенаправления оболочки > , чтобы отправить вывод команды echo в файл / etc / ansible / hosts .

Здесь следует отметить два момента:

  • В команде echo последовательность \ n обозначает символ новой строки ( Enter ). Таким образом, [local] и localhost фактически отражаются в файле в последовательных строках. Это соответствует правильному формату строфы в / etc / ansible / hosts , как описано здесь . (Я недостаточно знаю о возможности доступа, чтобы сказать вам, почему здесь используется имя хоста localhost вместо IP-адреса 127.0.0.1 .)

  • Символ перенаправления оболочки > перезапишет (то есть сотрет и заменит) содержимое / etc / ansible / hosts , а не добавит эту новую строфу в конец файла. (Для перенаправления и добавления в файл вместо этого используйте >> .)

2
07.10.2016, 00:01
3 ответа

Здесь используется GNU awk , использование POSIX awk было бы очень неприятным (отсутствие gensub ], которым я пользуюсь не раз).

#!/usr/bin/env gawk

function join(array, result, i)
{
    result = array[0];
    end = length(array) - 1;
    for (i = 1; i <= end; i++)
        result = result "," array[i];
    return result;
}
function push(arr, elem)
{
    arr[length(arr)] = elem;
}

# split("", arr) is a horribly unreadable way to clear an array
BEGIN { split("", arr); }

/{part}|{chapter}/ {
    l = gensub(".*{(.+)}{(.+)}{([0-9]+)}$", "\\1,\\3,\\2", "g");
    if ("part" == substr(l, 0, 4)) {
        if (length(arr) > 0) { print join(arr); }
        split("", arr);
        push(arr, gensub("^(.*),(.*),(.*)$", "\\2,\\3","g", l));
    } else {
        push(arr, gensub("^(.*),(.*),(.*)$", "\\3","g", l));
    }
}

END { print join(arr); }

Здесь используется тот факт, что регулярные выражения являются «жадными», поэтому при каждом совпадении будет отображаться полная строка. На это потребовалось больше усилий, чем я думал поначалу.

Со следующим вводом:

\contentsline {part}{Some title here\hfil }{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {part}{Some title here\hfil }{7}
\contentsline {chapter}{\numberline {}Person name here}{7}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{7}
blah blah
\contentsline {part}{Some title here\hfil }{9}
blah blah
blah blah
\contentsline {chapter}{\numberline {}Person name here}{9}

Мы производим с помощью cat input | awk -f the_above_script.awk :

5,Some title here\hfil ,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here,\numberline {}Person name here
7,Some title here\hfil ,\numberline {}Person name here,\numberline {}Person name here
9,Some title here\hfil ,\numberline {}Person name here

Номер страницы берется из {part} , затем из любого {chapter} , который происходит после {part} включен. Это позволяет использовать несколько глав внутри отдельных частей книги.

1
27.01.2020, 22:04

С модулем Perl Text :: Balanced содержимое верхнего уровня {} может быть извлечено следующим образом:

#!/usr/bin/env perl
use strict;
use warnings;
use Text::Balanced qw(extract_bracketed);

# this will of course fail if the input is one multiple lines, as this
# is only a line-by-line parser of standard input or the filenames
# passed to this script
while ( my $line = readline ) {
    if ( $line =~ m/\\contentsline / ) {
        my @parts = extract_contents($line);
        # emit as CSV (though ideally instead use Text::CSV module)
        print join( ",", @parts ), "\n";
    } else {
        #print "NO MATCH ON $line";
    }
}

sub extract_contents {
    my $line = shift;
    my @parts;
    # while we can get a {} bit out of the input line, anywhere in the
    # input line
    while ( my $part = extract_bracketed( $line, '{}', qr/[^{]*/ ) ) {
        # trim off the delimiters
        $part = substr $part, 1, length($part) - 2;
        push @parts, $part;
    }
    return @parts;
}

С помощью некоторый ввод:

% < input 
not content line
\contentsline {chapter}{\numberline {}Person name here}{5}
\contentsline {part}{Title with math $\frac{a}{b}$\hfil }{15}
also not content line
% perl parser input
chapter,\numberline {}Person name here,5
part,Title with math $\frac{a}{b}$\hfil ,15
% 
1
27.01.2020, 22:04

В TXR

@(repeat)
\contentsline {part}{@title\hfil }{@page}
@  (trailer)
@  (skip)
\contentsline {chapter}{\numberline {}@author}{@page}
@  (do (put-line `@title,@author,@page`))
@(end)

Sample data:

\lorem{ipsum}
\contentsline {part}{The Art of The Meringue\hfil }{5}
a
b
c
j
\contentsline {chapter}{\numberline {}Doug LeMonjello}{5}


\contentsline {part}{Parachuting Primer\hfil }{16}

\contentsline {chapter}{\numberline {}Hugo Phirst}{16}

\contentsline {part}{Making Sense of $\frac{a}{b}$\hfil }{19}

\contentsline {part}{War and Peace\hfil }{27}

\contentsline {chapter}{\numberline {}D. Vide}{19}

\contentsline {part}{War and Peace\hfil }{19}

Run:

$ txr title-auth.txr data
The Art of The Meringue,Doug LeMonjello,5
Parachuting Primer,Hugo Phirst,16
Making Sense of $\frac{a}{b}$,D. Vide,19

Notes:

  • Поскольку используется @(trailer) строки, в которых указан автор, не обязаны строго следовать своей части. Данные могут вводить несколько \contentsline {part} элементов, за которыми следуют chapter строки, совпадающие по номеру страницы.
  • @(skip) подразумевает поиск по всем оставшимся данным. Производительность можно повысить, ограничив диапазон путем добавления числового аргумента. Если можно предположить, что совпадение {глава} всегда находится в пределах 50 строк после {часть}, можно использовать @(skip 50).
1
27.01.2020, 22:04

Теги

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