przetwarzanie rozproszone - boinc

FORUM BOINC

Zaloguj się lub zarejestruj.

Zaloguj się podając nazwę użytkownika, hasło i długość sesji
Witaj na forum poświęconemu wspieraniu nauki poprzez platformę BOINC. Pobierz i zacznij zmieniać świat od teraz
Nasza strona na Facebooku - poleć znajomym.
Strony: [1]

Potrzebny skrypt (Przeczytany 280 razy)

krzyszp

  • Wszechstronny dyletant
  • Norway
  • Kalkulator
  • ***
  • Offline Offline
  • Wiadomości: 7 457
  • Avatar forum naukowego

    Potrzebny skrypt

    05 Czerwiec 2017, 18:51
    Szukam pomocy w napisaniu skryptu, który dla wszystkich plików w katalogu wg wzorca nazwy podmieni co 7 linię (ta linia jest pusta) na jakiś znak.

    Generalnie, w plikach wynikowych Universe dane wyglądają tak:
    14.048961 13 13 1.108065 1.593916 19.555800 0.598142 13.140188 14.048960 1
    22.742557 0.000000 21.118602 16.010919 -100000 673
    9.087724e-001 9.613467e+005 1.491 -0.039 0.232 21.848 3.207 -10.463
    1.309 7.358 -1 7 8.928 0.000 13.140 1.108 0.000 0
    1.108 6.232 13 8 10.854 0.157 14.049 1.594 0.000 0
    5 MT1(1-1) MT1(2-1) CE2(8-2;13-7) SN1 SN2

    10.220098 13 14 1.270304 7.411970 13.987911 0.094854 8.868781 10.220097 1
    32.717507 0.000000 26.523457 22.632275 -100000 39457
    1.351316e+000 6.818403e+004 -1.748 -0.178 0.386 -2.244 -0.215 0.713
    4.284 32.105 8 1 353.330 0.000 8.869 1.233 0.037 0
    1.270 8.236 13 8 12.661 0.000 10.220 7.412 0.000 0
    6 MT1(1-1) MT1(2-1) MT1(4-1) SN1 CE2(13-2;13-7) SN2

    33.506120 13 13 1.159634 1.107936 3.329772 0.780745 25.299691 33.506119 1
    161.207612 0.596501 11.037217 8.659463 -100000 42576
    8.206429e+000 2.425411e+002 -0.959 0.174 -0.418 1.195 -1.851 9.031
    2.365 12.857 8 1 408.865 0.000 25.300 1.108 0.051 0
    1.160 1.950 13 -1 0.734 0.000 33.506 1.108 0.000 0
    5 MT1(2-1) SN1 CE2(13-4;13-7) CE2(13-8;13-13) SN2

    38.589364 13 13 1.274492 1.107936 1827.260819 0.999000 35.938763 38.589363 1
    134.238697 0.498906 9.136170 8.890891 -100000 46017
    2.650600e+000 3.021633e+005 0.038 -0.030 -0.038 2.259 5.572 -11.232
    1.401 12.513 9 1 911.060 0.000 35.939 1.260 0.014 1
    1.274 3.070 13 8 50.522 0.000 38.589 1.108 0.000 0
    6 MT1(2-1) MT1(8-1) MT1(9-1) SN1 CE2(13-4;13-7) SN2

    Jak widać, dane są w blokach po 6 linijek. Te linijki muszę połączyć w jedną linię, jako jeden rekord, następnie drugi blok w następną linię itd...
    Takich plików mam grube miliony, więc szybkość operacji ma niebagatelne znaczenie...

    apohawk

    • Leń
    • Newsmani
    • BOINC Fanatyk
    • *
    • Offline Offline
    • Wiadomości: 3 005
    • Avatar forum naukowego
    • Ask no questions, hear no lies.

      Potrzebny skrypt

      Odpowiedź #1 06 Czerwiec 2017, 00:09
      Nie mam teraz dostępu do linuksa w domu, ale spróbowałbym z awk. Jeśli dobrze pamiętam, to jak mu podać znak nowej linii jako separator pola, to na pustej linii skończy rekord. Takie specjalne, dziwne zachowanie.
      Coś takiego:awk -F'\n' -- '{ print $0 }'

      albo bardziej topornie:awk -- 'BEGIN { i=0 }
      { if($0=="") {
        print x[0] " " x[1] " " x[2] " " x[3] " " x[4] " " x[5] " " x[6]
        i=0
      }
      else {
      x[i]=$0
      i=i+1
      }
      END { print x[0] " " x[1] " " x[2] " " x[3] " " x[4] " " x[5] " " x[6] }'
      Do zweryfikowania, czy tablice się tak robiło w awk.

      [EDIT]
      Przeczytałem jeszcze raz. Jak chcesz po prostu zamienić pustą linię na coś innego, to może
      sed -i 's/^$/moje znaki/g' moje pliki wg wzorca

      krzyszp

      • Wszechstronny dyletant
      • Norway
      • Kalkulator
      • ***
      • Offline Offline
      • Wiadomości: 7 457
      • Avatar forum naukowego

        Potrzebny skrypt

        Odpowiedź #2 06 Czerwiec 2017, 01:04
        Spłodziłem taki skrypt:

        #!/bin/bash
        cd data
        now=$(date +"%T")
        echo "Current time : $now"

        echo 'przenosze'
        for f in *; do mv "$f" "$f.gz"; done
        echo 'rozpakowuje'
        gunzip *.gz
        rm *.gz

        echo 'rozpakowane, zaczynam czytac parametry'
        now=$(date +"%T")
        echo "Current time : $now"

        for filename in *_3
        do
        name=${filename##*/}
        #name=${name::-1}
        #echo $name >> $filename
                tr '\n' ',' < $filename > plik.txt
        echo $name >> plik.txt
                mysql -u root -pXXX --local-infile universe-results -e "LOAD DATA LOCAL INFILE 'plik.txt'  INTO TABLE bhspin2param  FIELDS TERMINATED BY ',' Lines TERMINATED BY '\n'"
        rm plik.txt
        done
        echo 'parametry wczytane, wgrywam dane'
        now=$(date +"%T")
        echo "Current time : $now"

        for filename in *_0
        do
        name=${filename##*/}
        base=${name%1}
        #base2=${name::-1}
        cat $filename | tr -s ' ' >> "${base}.txt"
        awk 'NR % 7 !=0 {printf $0;printf ""} NR % 6 ==0 {print "END"}' ${base}.txt >> ${base}P.txt
        tr -d "\n\r" < ${base}P.txt >> ${base}C.txt
        sed s/END/\\n/g ${base}C.txt >> ${base}R.txt
        # tutaj wstawic nazwe pliku do kazdej linii
        mysql -u root -pXXX --local-infile universe-results -e "LOAD DATA LOCAL INFILE '${base}R.txt'  INTO TABLE bhspin2data  FIELDS TERMINATED BY ' ' LINES TERMINATED BY '\n'"
        rm ${base}R.txt
        rm ${base}P.txt
        rm ${base}C.txt
        rm ${base}.txt
        done
        echo 'koniec wgrywania'
        now=$(date +"%T")
        echo "Current time : $now"
        No i teraz zależy mi, żeby po linii
        # tutaj wstawic nazwe pliku do kazdej liniimuszę wstawić nazwę pliku aktualnie obrabianego, z uciętym ostatnim znakiem w nazwie (bez ścieżki dostępu).

        Pierwsza pętla obrabia inny plik, ale właśnie "styczną" dla nich przy zapisie do bazy jest nazwa pliku.

        apohawk

        • Leń
        • Newsmani
        • BOINC Fanatyk
        • *
        • Offline Offline
        • Wiadomości: 3 005
        • Avatar forum naukowego
        • Ask no questions, hear no lies.

          Potrzebny skrypt

          Odpowiedź #3 06 Czerwiec 2017, 22:36
          Jak patrzę na ten skrypt, to nie do końca ogarniam, co z tym robisz  %) więc wracam do tego:
          Cytuj
          Jak widać, dane są w blokach po 6 linijek. Te linijki muszę połączyć w jedną linię, jako jeden rekord, następnie drugi blok w następną linię itd...
          Po sprawdzeniu proponuję fragment podobny do tego z mojej pierwszej odpowiedzi:
          awk -- 'BEGIN { FS="\n"; RS="" } { $1=$1; print $0 }'Dla danych 11
          12
          13
          14
          15
          16

          21
          22
          23
          24
          25
          26

          Zwróci
          11 12 13 14 15 16
          21 22 23 24 25 26
          Sprawdzałem dla miliona takich bloków/rekordów, wykonywało się ok. 0.5s.

          RS to record separator, a ustawiony na pusty oznacza, że separatorem rekordu jest pusta linia. Konieczne jest to $1=$1, aby awk zamieniło \n na spacje (IFS na OFS).

          Jak patrzę na twój skrypt, to mam wrażenie, że więcej tam się dzieje, niż piszesz  %)
          Np. ten END. Jakby wstawiany w 6. linii, a potem zamieniany na nową linię. Nie możesz tego END od razu zamienić na nazwę pliku z usuniętym końcowym / (np. sed s=/$== lub basename filename, nie pamiętam, jak to poprawnie zrobić w zmiennej basha)? np. sed "s/END/\n`basename ${filename}`/g"?

          Może będzie łatwiej, jak dla tego zbioru danych z 1. posta podasz format, jakiego oczekujesz? :) Chętnie spróbuję pomóc. Lubię pomagać ze skryptami bash.  %)

          krzyszp

          • Wszechstronny dyletant
          • Norway
          • Kalkulator
          • ***
          • Offline Offline
          • Wiadomości: 7 457
          • Avatar forum naukowego

            Potrzebny skrypt

            Odpowiedź #4 06 Czerwiec 2017, 22:55
            Interesujące, czyli moja kobyła zbyt optymalna nie jest :)

            A generalnie, to już sobie poradziłem, całość wygląda tak:
            #!/bin/bash
            cd data
            now=$(date +"%T")
            echo "Current time : $now"

            echo 'przenosze'
            for f in *; do mv "$f" "$f.gz"; done
            echo 'rozpakowuje'
            gunzip *.gz
            rm *.gz

            echo 'rozpakowane, zaczynam czytac parametry'
            now=$(date +"%T")
            echo "Current time : $now"

            for filename in *_3
            do
            name=${filename##*/}
                    tr '\n' ',' < $filename > plik.txt
            name2=${name::-2}
            echo $name2 >> plik.txt
                    mysql -u root -pXXX --local-infile universe-results -e "LOAD DATA LOCAL INFILE 'plik.txt'  INTO TABLE bhspin2param  FIELDS TERMINATED BY ',' Lines TERMINATED BY '\n'"
            rm plik.txt
            done
            echo 'parametry wczytane, wgrywam dane'
            now=$(date +"%T")
            echo "Current time : $now"

            for filename in *_0
            do
            name=${filename##*/}
            base=${name%1}
            name2=${name::-2}
                    cat $filename | tr -s ' ' >> "${base}.txt"
            awk 'NR % 7 !=0 {printf $0;printf ""} NR % 6 ==0 {print "END"}' ${base}.txt >> ${base}P.txt
            tr -d "\n\r" < ${base}P.txt >> ${base}C.txt
            sed s/END/\\n/g ${base}C.txt >> ${base}R.txt
            sed "s/^/$name2 /" ${base}R.txt > ${base}D.txt
            mysql -u root -pXXX --local-infile universe-results -e "LOAD DATA LOCAL INFILE '${base}D.txt'  INTO TABLE bhspin2data  FIELDS TERMINATED BY ' ' LINES TERMINATED BY '\n'"
            rm ${base}R.txt
            rm ${base}P.txt
            rm ${base}.txt
            rm ${base}D.txt
            rm ${base}C.txt
            done
            echo 'koniec wgrywania'
            now=$(date +"%T")

            echo "Current time : $now"
            Generalnie, w całości chodzi o 2 pliki - jeden zawierający zmienne startowe dla apki, drugi to właściwy plik z wynikami.
            W pierwszym pliku doklejam na końcu jego nazwę i importuję dane do jednej tabeli w mysql, w drugim jest trochę więcej grzebania (oczywiście, dla obu plików doklejam rozszerzenie .gz, bo nie chce mi rozpakowywać bez tej operacji.
            W drugim pliku z tego ciągu tworzę plik w formacie csv, w którym każda 6tka linii z danymi to jeden rekord. Na początku każdej linijki dodają nazwę pliku, z którego pochodzą dane.

            Generalnie, jak by to się dało zoptymalizować, to byłbym szczęśliwy, bo tych plików naprawdę jest strasznie dużo.

            apohawk

            • Leń
            • Newsmani
            • BOINC Fanatyk
            • *
            • Offline Offline
            • Wiadomości: 3 005
            • Avatar forum naukowego
            • Ask no questions, hear no lies.

              Potrzebny skrypt

              Odpowiedź #5 06 Czerwiec 2017, 23:38
              Jak dla mnie za dużo jest tych plików tymczasowych. Szkoda HDD. Czemu nie w pipe'a? O ile można ogarnąć, co chcesz osiągnąc z tymi plikami :) No chyba, że zapisujesz to na tmpfs, to róbta co chceta.
              Rozpakowanie .gz:
              gzip -d < spakowany_plik > rozpakowany_plikalbo
              gzip -d < spakowany_plik | pipe do obróbki :P
              A wydajnościowo to i tak to pewnie klęknie na mysqlu.

              Jeśli w nazwach plików źródłowych kiedykolwiek pojawią się białe znaki, to for f in * się rozleci. Ja robię ls| while read f; do ...; done. Jak masz tych plików dużo, to wszelkie * tym bardziej bym odradzał. find albo ls | while read f; do gzip -d < $f > $f.ungzipped; done
              Jakby całą obróbkę zamknąć w pętli find albo ls | while read f; do ...; done, to nie trzeba by się martwić o miejsce na dysku na wszystkie rozpakowane pliki jednocześnie.

              krzyszp

              • Wszechstronny dyletant
              • Norway
              • Kalkulator
              • ***
              • Offline Offline
              • Wiadomości: 7 457
              • Avatar forum naukowego

                Potrzebny skrypt

                Odpowiedź #6 07 Czerwiec 2017, 00:40
                Białych znaków w nazwie nie ma i nie będzie (to ja generuję te pliki - tzn za pomocą serwera BOINC nazwy są ustalane), więc ten problem nie wystąpi.
                Natomiast co do obróbki przez pipe - to po prostu za mało wiem na ten temat, żeby się pokusić (tzn - jak dalej to obrabiać po przesłaniu do pipe).

                apohawk

                • Leń
                • Newsmani
                • BOINC Fanatyk
                • *
                • Offline Offline
                • Wiadomości: 3 005
                • Avatar forum naukowego
                • Ask no questions, hear no lies.

                  Potrzebny skrypt

                  Odpowiedź #7 07 Czerwiec 2017, 09:33
                  Zamiast awk 'NR % 7 !=0 {printf $0;printf ""} NR % 6 ==0 {print "END"}' ${base}.txt >> ${base}P.txt
                  tr -d "\n\r" < ${base}P.txt >> ${base}C.txt
                  sed s/END/\\n/g ${base}C.txt >> ${base}R.txt
                  np.
                  awk 'NR % 7 !=0 {printf $0;printf ""} NR % 6 ==0 {print "END"}' ${base}.txt | tr -d "\n\r" | sed s/END/\\n/g >> ${base}R.txt
                  Strony: [1]   Do góry

                  GoogleTagged


                  Hosting dzięki uprzejmości InnerVision sp. z o.o.
                  SMF © 2011, Simple Machines