Самый длинный общий префикс строк

Ваша проблема в том, что вы перенаправляете stderr до перенаправления stdout. Это должно сработать, если вы поменяете их местами. Для иллюстрации рассмотрим следующие сценарии. foo.sh выводит на stderr и stdout:

#!/usr/bin/env bash
## foo.sh

## Print "out" to standard output
echo out
## Print "error" to standard error
echo error >&2

bar.sh читает ввод:

#!/usr/bin/env bash
## bar.sh

read input
## Print your input
echo "bar.sh read: $input"

И также baz.sh :

#!/usr/bin/env bash
## baz.sh

read input
## Print your input
echo "baz.sh read: $input"

Теперь, если я запустите эти три, как вы, кажется, пытаетесь сделать, и передать их вывод в цикл while , он работает, как ожидалось:

$ foo.sh 2> >(bar.sh) > >(baz.sh) | while read line; do echo "$line"; done
bar.sh read: error
baz.sh read: out

Однако это не удастся, если вы сделаете это наоборот:

$ foo.sh > >(bar.sh) 2> >(baz.sh) | while read line; do echo "$line"; done
bar.sh read: out
1
05.05.2018, 11:14
1 ответ

Вы можете сделать что-то вроде:

<file LC_ALL=C sort |
   sed -n 'N;h;s/^\(.*\).*\n\1.*/\1/p;g;D' |
   awk '{l = length}
        l > max {max = l; s = $0}
        END {print s}'

Мы сортируем ввод, используя сравнение байтов -и -байтов(sortв локали C ), что гарантирует, что строки с самым длинным общим префиксом являются смежными.

sedнаходит самый длинный общий префикс между одной строкой и следующей, используя обратную связь BRE -ссылки (\(.*\).*\n\1, представляющие собой захваченную последовательность символов \(.*\), за которой следует любое количество символов .*, символ новой строки \nи та же последовательность символов, что была зафиксирована ранее \1), которую мы печатаем.

awkнаходит самый длинный из них (выбирает первый во входных данных, если их несколько, поэтому он будет первым в лексическом порядке. Используйте >=вместо >, чтобы получить последний ).

Обратите внимание, что он находит самый длинный общий префикс с точки зрения символов . Чтобы получить его в виде байтов , установите $LC_ALLна Cдля всех 3 команд, а не только sort. Тогда, например, в UTF -8 локалей, вместо того, чтобы найти 2 символа Stкак самый длинный общий префикс между Stéphaneи Stábat, он найдет 3 байта,St<0xc3>, где <0xc3>является первой половиной символов áи é.

В терминах расширенного графемного кластера . Например, так что между Stepsи Stéphane(, где éвыражается как двухсимвольный кластер графем e\u0301), он находит Stвместо Ste, вы можете прибегнуть кperl:

<file LC_ALL=C sort |
  perl -Mopen=locale -ne '
    BEGIN{$prev = <>}
    if ("$prev$_" =~ /^(\X*).*\n\1\b{g}/) {
      $l = length($1);
      if ($l > $max) {$max = $l; $s = $1}
    }
    $prev = $_;
    END{print "$s\n"}'

(, где \Xсоответствует расширенному кластеру графемы , а \b{g}— границе расширенного кластера графемы (, для которой вам нужен perl 5.22.1 или новее )).

Если вы хотите найти самый длинный общий префикс всех строк во вводе (, а не только любых 2 строк во вводе ), как я изначально думал вы спрашивали, это ответили на другие вопросы и ответы здесь .

4
27.01.2020, 23:18

Теги

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