Jak filtrować wiele lini poprzez wyrażenie regularne?

0
  • wykonuję komendę mvn install która trwa 5 minut, caly czas printując output
  • chce zrobic mnv clean install | mojprogram
  • mojprogram umialbym przefiltrowac wszystkie te linie ze standard output poprzed wyrazenie regularne
  • mialby wykonywac jedna akcje jak linijka pasuje do wyrazenia, i inną kiedy nie pasuje

probowałem zrobić:

progress() {
    while IFS= read -r line; do
        if [[ $line =~ $pattern ]];
    done
}

ale niestety =~ nie rozpoznaje PCRE, a tego "extended regular exprs" nie umiem. Chciałem nawet w nim napisac swoje wyrazenie ale na internecie nie ma nigdzie sensownego debugera do tego (tak jak jest np regex101 do pcre) - chyba że ktoś mi pomoże przerobić moj wzorzec z PCRE na extended shell, wtedy sprawa zalationa.

Znalazłem grep -P w którym można ładnie to zrobić, ale flaga -P nie jest wspierana na macu :/ grep -E jest, ale to ma ten sam problem co =~. Chciałem też przepipe'ować do innego programu który rozumie wyrażenia regularne, np:

pcre() {
    python3 -c 'import sys, re; s = sys.stdin.read(); s=re.sub("Hello", "Good morning", s); print(s);'
}
while IFS= read -r line; do
      echo $line | pcre
done

ale tutaj jest taki problem że boje się że znaki specjalne wzorca (/, ., - zostaną najpierw zinterpretowane przez ten jezyk, i będę musiał je jakoś zaencodować, a nie umiem tego).
Próbowałem też z perl i z awk ale one nie zwracają true/false albo 1/0, tak jakbym chciał (żeby wsadzić do if [[) tylko zwracają albo całą linijkę lub pusty string jak się nie udało znaleźć wystąpenia.

Chciałbym po prostu funkcję którą można wywołać

if  [[ 'jakis tekst' | match '/moj (wzorzec)/' ]] ; then
    echo "znalazlem wystapienie $wynik razem z capturing group $1"
else 
    echo 'nie ma wystapienia'
fi

pomoże ktoś?

0

Ale po kiego grzyba Ci to całe read -r? Zamiast tego przefiltruj grepem (czy czymkolwiek innym) bezpośrednio. Większość narzędzi (a zwłaszcza grep) wspiera czytanie ze stdin i pisanie na stdout.

Podaj nam wyrażenie jakie chcesz użyć a damy Ci rozwiązanie.

Przykładowo jeśli chcesz przy pomocy Perla wyfiltrować linie, które są dla Ciebie istotne:

mvn clean install | perl -wln -e '/re/ and print'

Dodatkową rzeczą jest to, że [[ jest nie potrzebne w Twoim przypadku.


W skrócie, podaj nam co chcesz zrobić, bo strasznie kręcisz się w kółko.

0

Wyrażenie to

\[INFO\].+\[(\d+)/(\d+)\]

Zeby np pasowały teksty (każdy w innej linii)

[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
[INFO] APPLICATION-2.4-SNAPSHOT [6/10]
Some bullshit 

Żebym mógł z tego zrobić

Znalazłem 5 na 10
Nie znalazłem 
Nie znalazłem 
Znalazłem 6 na 10
Nie znalazłem 

To ostatnie to capturing Group żeby użyć tych danych gdzie indziej.

0

Faktycznie!

mvn clean install | perl -wln -e '/re/ and print'

już jest całkiem blisko tego co bym chciał! jeszcze tylko jakiś if, ze jak pasuje do wzorca to wywołaj dla tych linijek jedna komendę, tylko żeby przekazać capturing group z wzorca.

0
mvn clean install | sed -nE 's/\[INFO\].*\[([[:digit:]]+).([[:digit:]]+)\]/\1 \2/p' | xargs -L 1 ./script

W ten sposób skrypt ./script będzie dopalony dla każdej linii z pasującymi argumentami w postaci ./script <1 liczba> <2 liczba>.

0
hauleth napisał(a):
mvn clean install | sed -nE 's/\[INFO\].*\[([[:digit:]]+).([[:digit:]]+)\]/\1 \2/p' | xargs -L 1 ./script

W ten sposób skrypt ./script będzie dopalony dla każdej linii z pasującymi argumentami w postaci ./script <1 liczba> <2 liczba>.

Ale chcę też odpalić inny skrypt kiedy linijka nie spełnia wzorca

0

Może powiedz dokładnie co chcesz zrobić?

0
hauleth napisał(a):

Może powiedz dokładnie co chcesz zrobić?

@hauleth Ok, maven clean install wypluwa taki output

[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
[INFO] APPLICATION-2.4-SNAPSHOT [6/10]
Some bullshit 
Więcej bullshitu 
[INFO] SomeProgram.2.5.7 [10/10]
Success 

Chciałbym zrobić tak, żeby po każdej linijce wyświetli się progress bar, który pokazuje ile z kroków już się wykonało ([6/10]). Potem wystawiałbym \r żeby cofnąć karetkę i następna linijka z logów zastąpiłaby progress bar, a poniżej pojawiłby się nowy.

Nie byłoby to specjalnie trudne, gdyby nie to że muszę pamiętać dwie wartości (current i total) wyciągnięte z ostatniej linijki zmatchowanej przez wzorzec.

Chciałbym uzyskać taki efekt:

[INFO] SomeProgram.2.5.7 [5/10]
|====>    | 5 out of 10
[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
|====>    | 5 out of 10
[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
|====>    | 5 out of 10
[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
[INFO] APPLICATION-2.4-SNAPSHOT [6/10]
|=====>   | 6 out of 10
[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
[INFO] APPLICATION-2.4-SNAPSHOT [6/10]
Some bullshit 
Więcej bullshitu 
|=====>   | 6 out of 10
[INFO] SomeProgram.2.5.7 [5/10]
Some bullshit 
Some bullshit 
[INFO] APPLICATION-2.4-SNAPSHOT [6/10]
Some bullshit 
Więcej bullshitu 
[INFO] SomeProgram.2.5.7 [10/10]
|=========| 10 out of 10 << koniec 
1

Nie wiem jak na macu, na linuxie korzystam z czegoś takiego, aby sprawdzać status pewnych operacji:

#!/usr/bin/perl -w

sub progress{
    my $length_progress = 10;
    my $state = shift;
    my $limit = shift;
    return if(!defined($state) || !defined($limit));
    my $actual_state = int($state / $limit * $length_progress);
    my $com = '|'.'='x$actual_state;
    if($actual_state < $length_progress){
        $com .= '>';
        $com .= ' 'x($length_progress-$actual_state-1);
    }
    return $com .= "| $state out of $limit";
}
$|=1;
my $t = 0;
my $com;
while(<>){
    if($t == 1){
        print "\r\033[2K";
    }
    if($_ =~ /[INFO].*\[(\d+)\/(\d+)\]/){
        print "$_";
        $com = progress($1,$2);
        $t=1;
    }else{
        print "$_";
    }
    print $com if(defined($com));
}

Lekko go pozmieniałem, aby był zgodny z tym co tam chcesz uzyskać.

Należy go zapisać jako plik na przykład progress.pl

Następnie nadać mu flagę wykonywalności.

Użyć w poleceniu:

$ twoja_komenda | ./progress.pl
0

Ja napisałem coś takiego:

#!/usr/bin/env bash

print_progress() {
    printf -v var "|"
    printf -v var '%0.s=' $(seq 1 $1)
    printf -v var ">"
    printf -v var '%0.s ' $(seq 1 $2)
    printf -v var  "| ($1 / $3)\r"
}

progress() {
    started=false
    finished=false
    current=0
    total=0
    while IFS= read -r line; do
        result=$(echo $line | sed -nE 's/\[INFO\].*\[([[:digit:]]+).([[:digit:]]+)\]/\1 \2/p')
        results=($result)
        if [[ ! -z $result ]] ; then
            current=${results[0]}
            total=${results[1]}
            started=true
        fi
        if [[ $current = $total && $current != 0 ]] ; then
            started=false
            finished=true
        fi

        printf '%-90s\n' "$line"

        if $finished ; then
            print 'Success\n'
        elif $started ; then
            REMAINING=`expr $total - $current`
            print_progress $current $REMAINING $total
        fi
    done
}

Działa tak jak powinien, ale progress bar "miga" w terminalu. Wydaje mi się że miga dlatego że bash robi flush wiele razy (po każdym print), i np gdybym zamiast robić 7 printów, zapisał to do zmiennej i potem zrobił 1x print - wtedy byłoby lepiej. Ktoś pomoże z manipulacją stringów, zamiast od razu printować?

0
woki napisał(a):

Nie wiem jak na macu, na linuxie korzystam z czegoś takiego, aby sprawdzać status pewnych operacji:

// ...kod

Lekko go pozmieniałem, aby był zgodny z tym co tam chcesz uzyskać.

Należy go zapisać jako plik na przykład progress.pl

Następnie nadać mu flagę wykonywalności.

Użyć w poleceniu:

$ twoja_komenda | ./progress.pl

No Twój kod działa wyśmienicie - tak jak chciałem i nie miga w terminalu tak jak mój kod :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1