Przede wszystkim przepraszam, jeśli tytuł nie jest najjaśniejszy, nie był naprawdę pewien, jak lepiej wyrazić problem.

Zasadniczo otrzymuję dane w skrypcie bash (nie mam żadnej kontroli nad formatem wspomnianych danych), które przybywa w następujący format:

(Name: Foo bar; UUID: <blah-blah-0101>; AnotherField: Some text; TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; ) ; NumericalData: 4; MoreInfo: Some Information) ;

Teraz, co chcę zrobić, to pętla przez każdą parę kluczy / wartości, abyśmy mogli przetworzyć informacje. Oczywiście usuwanie wiodące / końcowe "();" jest prosty. Potem pomyślałem, że może wymienić ";" z Newlines, ale to pęka z powodu różnych poziomów.

W odniesieniu do poziomów, nie martwiam się zapętlącymi się w nich, jestem tylko zainteresowany najwyższym poziomem, aby mówić. Tak więc:

TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; )

Jest jedną prostą parą, o ile jestem zaniepokojony.

Spodziewany rezultat :

Name: Foo bar
UUID: 
AnotherField: Some text
TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; )
NumericalData: 4
MoreInfo: Some Information

Ponieważ zapoznam się z zapętleniem przez linie bloku tekstowego, przekształcając oryginalny ciąg do powyższego wyniku byłoby wystarczające, chociaż odpowiedź, która bezpośrednio pętla przez każdą z powyższych linii również działała.

Nie jesteś pewien, jak się zbliża, więc każdy kierunek byłby doceniony.

2
Louis M. 4 czerwiec 2018, 11:59

3 odpowiedzi

Najlepsza odpowiedź

Oto skrypt

  • Przeczytaj oryginalny plik wejściowy jednorazowy (INPUT.TXT)
  • wytwarza plik wyjściowy (wyjściem.txt)

Jeszcze:

  • Początkowo usuwa zewnętrzne dwa szelki
  • używa licznika, aby policzyć wewnętrzne szelki
  • Zmiana IFS, aby przeczytać wszystkie znaki (w tym białe znaki)

#!/bin/bash

WITHOUT_OUTER="`cat input.txt | cut -d"(" -f2- | rev | cut -d")" -f2- | rev`;"
PAIR=''
CNT=0
NEWLINE=0
OLD_IFS=$IFS
IFS=''
while read -n1 C
do
  if [ "$C" == '(' ]
  then
    CNT=$((CNT+1))
  elif [ "$C" == ')' ]
  then
    CNT=$((CNT-1))
  fi
  if [ $CNT -eq 0 ]
  then
    if [ "$C" == ';' ]
    then
      PAIR="$PAIR\n"
      NEWLINE=1
    fi
  elif [ "$C" == ';' ]
  then
    PAIR="$PAIR$C"
  fi
  if [ "$C" != ";" ]
  then
    if [ ! $NEWLINE -eq 1 ]
    then
      PAIR="$PAIR$C"
    else
      NEWLINE=0
    fi
  fi
done < <(echo $WITHOUT_OUTER)
echo -e "$PAIR" > output.txt

Sformatowane wartości są włączone. Tekst. cat output.txt pokaże Ci wynik:

Name: Foo bar
UUID: <blah-blah-0101>
AnotherField: Some text
TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; )
NumericalData: 4
MoreInfo: Some Information
1
Rene Knop 4 czerwiec 2018, 12:27

To działa:

# strip stdin up until first '(' is read
cut -d '(' -f2- | while read -r -n1 c; do
        case $c in
        ')') break; ;;
        # if read any char, this is field name, just print it
        [a-zA-Z]) echo -n "$c"; ;;
        # doublescore separates names from values
        :)
                echo -n ': '
                l=0
                while read -n1 c; do
                        case "$c" in
                        # we need to count levels of '(' ')'
                        '(') ((l++)); echo -n '('; ;;
                        ')') ((l--)); 
                             # if level gets under zero, break from here, look at `MoreInfo:` case
                             if ((l<0)); then 
                                 echo; break; 
                             else 
                                 echo -n ')'; 
                                 if ((l==0)); then 
                                     echo; break; 
                                 fi;
                             fi;
                             ;;
                        # ';' separetes the next field, but only if level is zero, cause otherwise those are nested fields
                        ';') 
                                if ((l==0)); then 
                                        echo; 
                                        break;
                                else 
                                        echo -n "$c"; 
                                fi;
                                ;;
                        *) echo -n "$c"; ;;
                        esac
                done;
                # if level is lower then zero, braek, look at `MoreInfo:` case
                if ((l<0)); then break; fi;
                ;;
        " ") ;;
        esac
done; 
cat >/dev/null

Dla następującego wejścia:

(Name: Foo bar; UUID: <blah-blah-0101>; AnotherField: Some text; TieredField: (Number: 123; Text: More Text; YetAnotherTier: (Name: somename; IP: 125.214.21.4) ; ) ; NumericalData: 4; MoreInfo: Some Information) ;

Wytwarza wyjście:

Name: Foobar
UUID: <blah-blah-0101>
AnotherField: Sometext
TieredField: (Number:123;Text:MoreText;YetAnotherTier:(Name:somename;IP:125.214.21.4);)
NumericalData: 4
MoreInfo: SomeInformation
2
KamilCuk 4 czerwiec 2018, 09:45

Jest to strasznie nieefektywne, ale będzie działać - ta pętla szuka pierwszego "(" i ostatnia ")" przed wydrukowaniem niczego między nimi jako jeden ciąg (również zakładam znak "_" nie jest używany ...):

t=''
n=0
oIFS=$IFS
IFS=';'
for f in $(sed -e 's/^(//' -e 's/) ;$//')
do
    if [[ $f = *'('* ]]; then
        t="${t}_ $f"
        let n++
    elif [[ $f = *')'* ]]; then
        t="${t}_ $f"
        let n--
        [[ $n -eq '0' ]] && echo ${t##_  }
    elif [[ $n -ne '0' ]]; then
        t="${t}_ $f"
    else
        echo ${f## }
    fi

done | IFS=$oIFS sed 's/_/;/g'

Wynik to:

Name: Foo bar
UUID: <blah-blah-0101>
AnotherField: Some text
TieredField: (Number: 123;  Text: More Text;  YetAnotherTier: (Name: somename;  IP: 125.214.21.4) ;  )
NumericalData: 4
MoreInfo: Some Information
0
towel 4 czerwiec 2018, 10:22