Come rilevare l’ultima riga prima della FINE awk

Sto cercando di aggiungere ultima riga del file che sto creando. Come è possibile rilevare l’ultima riga di un file in awk prima END ? Ho bisogno di fare questo perché le variabili non funzionano nel END blocco,
quindi sto cercando di evitare di utilizzare END.

awk ' { do some things..; add a new last line into file;}'

prima END, non voglio che questo:

awk 'END{print "something new" >> "newfile.txt"}'
Le variabili che in particolare non è necessario che non sono disponibili alla FINE del blocco? La maggior parte delle variabili (NR, NF, FNR, etc. ) sono molto ragionevoli valori alla FINE del blocco.
le variabili di cui ho bisogno sono alcune variabili locali che svolgono il ruolo principale nella creazione del file.. ad esempio: print $0 >> sprintf("%s/%s_%s.txt", user, mode, FILENAME) quelli user e mode non sono disponibili in END che stanno diventando il nome del file…
Questa domanda non ha alcun senso. Awk non sono variabili locali, oltre i parametri della funzione. Se si calcola un nome di file e metterlo in fname, quindi fname manterrà il suo valore, fino a quando Awk termina o viene assegnato un nuovo valore in fname.
Quando hai l’elaborazione di tutti i record di input, si supponga che potrebbe sarà l’ultimo), e calcolare il nome del file che verrà richiesto in questo caso, la mette in una variabile chiamata fname. Se il record è l’ultimo, poi il END blocco viene eseguito, e fname saranno allora disponibili.

OriginaleL’autore doniyor | 2012-08-27

6 Replies
  1. 10

    Una possibilità è quella di utilizzare getline funzione di elaborare il file. Restituisce 1 sul successo, 0 sulla fine del file e -1 su un errore.

    awk '
        FNR == 1 {
    
            ## Process first line.
            print FNR ": " $0;
    
            while ( getline == 1 ) {
                ## Process from second to last line.
                print FNR ": " $0;
            }
    
            ## Here all lines have been processed.
            print "After last line";
        }
    ' infile

    Assumendo infile con questi dati:

    one
    two
    three
    four
    five

    Output sarà:

    1: one                                                                                                                                                                                                                                       
    2: two                                                                                                                                                                                                                                       
    3: three
    4: four
    5: five
    After last line
    grande, questo è quello che voglio, penso. permettetemi di dare un colpo
    Quindi, non accetto la risposta ancora, perché potrebbe scoraggiare gli altri utenti a postare soluzioni diverse. Farlo quando si è sicuri. Il tempo non è un problema, no?
    hai ragione :). ho deselezionato indietro.
    Ben fatto; se non c’è un bisogno per il trattamento di prima linea speciale, si potrebbe riscrivere il ciclo di utilizzare do { ... } while (getline == 1), nel qual caso non è necessario il print dichiarazione che precedono il ciclo.
    Questo è stato accettato, ma non capisco il punto, come può semplicemente essere fatto da: cat <file>; echo “riga” >> <file> .. e l’aggiunta di cose usare awk può essere fatto semplicemente con ciò che “non voglio”. Utilizzando getline suona come si desidera utilizzare qualcosa di diverso in quanto non è possibile il tubo fino a che il processo è fatto.

    OriginaleL’autore Birei

  2. 6
    $ cat file 
    1
    2
    3
    4
    5

    Leggendo stesso file due volte ( Raccomandato )

    $ awk 'FNR==NR{last++;next}{print $0, ((last==FNR)?"I am Last":"")}' file file
    1
    2
    3
    4
    5 I am Last

    Utilizzando getline

    $ awk 'BEGIN{while((getline t < ARGV[1]) > 0)last++;close(ARGV[1])}{print $0, ((last==FNR)?"I am Last":"")}' file
    1
    2
    3
    4
    5 I am Last
    Questo è strano. A me funziona su Cygwin riga di comando bash, se io uso l’esatta del file di input che hai postato. Ma se io uso un altro file di input, non. Il mio file è composto di linee “linea x x”, dove x da 1 a 8.
    grazie per la segnalazione, ho notato che non funziona con anche il n ° di righe, modificato ora
    Grazie. Cercando come hai postato, non stampa l’ultima riga del contenuto (8 line 8). Solo il messaggio.
    Penso ci sia un equivoco – quello che ho indicato come “non funzionante”, è stato il fatto che l’ultima riga del file di input non viene stampato prima di “i am Ultimo messaggio”. In caso contrario, il tuo script rileva l’ultima riga correttamente, e stampa il messaggio.
    Oh, bene, per questo si può utilizzare RS variabile o "\n" sarebbe simile a questa print $0 RS (() ? : )

    OriginaleL’autore Akshay Hegde

  3. 3

    Stampa della riga precedente.
    Quando la corrente è 2, riga di stampa 1,
    quando la corrente di linea è di 3, riga di stampa 2.
    ….
    fino alla fine

    awk '{
        if (NR>1) {
            # process str
            print str;
        }
        str=$0;
    }
    END {
        # process whatever needed before printing the last line and then print the last line.
        print str;
    }'
    In generale evitare di solo codice risposte. Considerare l’aggiunta di un description che aiuta a spiegare il vostro codice. Grazie

    OriginaleL’autore Prasanth Pennepalli

  4. 2

    Si può ottenere il numero di righe in un file utilizzando "wc -l" | getline filesize in blocco begin e l’uso NR == filesize per testare l’ultima riga dello script corpo.

    grazie, ma il mio caso è questo: ho un file enorme che dovrei ordinare in più file di piccole dimensioni. quelle più file di piccole dimensioni che sto creando al momento dovrebbe essere in formato rtf. così, non so in anticipo il numero di linee del nuovo file.. 🙁
    perché non li converte in formato rtf dopo awk?
    poiché i file sono la creazione e la distribuzione in directory diverse. dopo awk devo cercare il file creati, poi, è anche più lavoro.
    Premessa: Come scritto, il wca base di pipeline di comando funziona solo con stdin ingresso a causa della mancanza di esplicita di ingresso – che è un catch-22: il comando di scarico stdin ingresso, causando successivi blocchi nel programma awk avere nessun ingresso a sinistra. Per utilizzare questo approccio, (a) assicurarsi che un nome di file reale è stato specificato e (b) si riferiscono esplicitamente: "wc -l < \""ARGV[1]"\"" | getline lineCount; lineCount+=0 (Il lineCount+=0 parte è per piattaforme come OSX dove wc -l uscite principali spazi; si assicura che awk tratta lineCount come un numero.)
    grazie per la versione aggiornata. Questa soluzione mi ha permesso di utilizzare in modo efficace l’ rewind() funzione con un non meglio specificato (cioè, file di dati dipendente) numero di ripetizioni. Così, invece di goffamente utilizzando getline, I test per l’EOF come qui proposto.

    OriginaleL’autore perreal

  5. 2

    È possibile utilizzare ENDFILE, viene eseguito prima di END:

    $ awk 'END {print "end"} ENDFILE{print "last line"}'  /dev/null /dev/null
    last line
    last line
    end

    ENDFILE esiste nella versione più recente di awk (>4.0 penso).

    OriginaleL’autore Dmitry Ch.

  6. 0

    So che la risposta è stata accettata, ma è semplicemente sbagliato.

    Perché si vuole usare awk come un parser e non come un codice.

    Awk deve essere utilizzato entro alcuni tubi unix e non deve essere utilizzato all’interno di qualsiasi logica.

    Ho avuto lo stesso problema e ho risolto entro awk come questo:

    nlines=wc -l <file>

    gatto | awk -v nl=${nlines} ‘{if (nl != NR) {print $0,”,”,”\”;} else {print;}}’ >> ${someout}

    C’è un punto importante: i tubi a filo, e la RAM.

    Se si fanno awk a sputare fuori la sua uscita del tubo per il prossimo processore.

    Se si utilizza getline, e, in particolare, all’interno di un ciclo, si potrebbe non vedere la fine.

    getline deve essere utilizzato solo per una linea e di un eventuale dipendenza nella riga successiva.

    Amo awk, ma non si può fare tutto!

    MODIFICA:

    Per cui giù votato la risposta, voglio solo presentare questo script:

    #! /bin/sh
    #
    # Generate random strings
    cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 100000 > x.r.100000
    cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1000000 > x.r.1000000
    cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 5000000 > x.r.5000000
    #
    # To save you time in case
    #cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 10000000 > x.r.10000000
    #
    # Generate awk files
    cat <<"EOF" > awkGetline.sh
    #! /bin/sh
    #
    awk '
        FNR == 1 {
    
            ## Process first line.
            print FNR ": " $0;
    
            while ( getline == 1 ) {
                ## Process from second to last line.
                print FNR ": " $0;
            }
        }
    ' x.r
    #
    EOF
    #
    chmod +x awkGetline.sh
    #
    cat <<"EOF" > awkPlain.sh
    #! /bin/sh
    #
    awk '
        {print FNR ": " $0;}
    ' x.r
    #
    EOF
    #
    # x.r.100000
    #
    chmod +x awkPlain.sh
    #
    # Execute awkGetline.sh 10 times on x.r.100000
    rm -f x.t
    cp x.r.100000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkGetline.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Getln", sum;}' | grep SUM
    #
    
    #
    # Execute awkPlain.sh 10 times on x.r.100000
    rm -f x.t
    cp x.r.100000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkPlain.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Plain", sum;}' | grep SUM
    #
    
    #
    # x.r.1000000
    #
    chmod +x awkPlain.sh
    #
    # Execute awkGetline.sh 10 times on x.r.1000000
    rm -f x.t
    cp x.r.1000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkGetline.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Getln", sum;}' | grep SUM
    #
    
    #
    # Execute awkPlain.sh 10 times on x.r.1000000
    rm -f x.t
    cp x.r.1000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkPlain.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Plain", sum;}' | grep SUM
    #
    
    
    #
    # x.r.5000000
    #
    chmod +x awkPlain.sh
    #
    # Execute awkGetline.sh 10 times on x.r.5000000
    rm -f x.t
    cp x.r.5000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkGetline.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Getln", sum;}' | grep SUM
    #
    
    #
    # Execute awkPlain.sh 10 times on x.r.5000000
    rm -f x.t
    cp x.r.5000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkPlain.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Plain", sum;}' | grep SUM
    #
    
    exit;
    # To save you time in case
    
    #
    # x.r.10000000
    #
    chmod +x awkPlain.sh
    #
    # Execute awkGetline.sh 10 times on x.r.10000000
    rm -f x.t
    cp x.r.10000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkGetline.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Getln", sum;}' | grep SUM
    #
    
    #
    # Execute awkPlain.sh 10 times on x.r.10000000
    rm -f x.t
    cp x.r.10000000 x.r
    for runInstance in 1 2 3 4 5 6 7 8 9 10;
      do
      /usr/bin/time -p -a -o x.t ./awkPlain.sh > x.1.out;
    done;
    #
    cat x.t | grep real | awk 'BEGIN {sum=0.0} {sum=sum+$2; print $2, sum/10;} END {print "SUM Plain", sum;}' | grep SUM
    #

    E, naturalmente, i primi risultati:

    tmp]$ ./awkRun.sh 
    SUM Getln 0.78
    SUM Plain 0.71
    SUM Getln 7.2
    SUM Plain 6.49
    SUM Getln 35.91
    SUM Plain 32.92

    Dove è il risparmio di circa il 10% del tempo solo a causa della getline.

    Considerare questo all’interno di una logica più complessa e si potrebbe ottenere anche una cattiva immagine. In questa versione normale, la memoria considerazione non sono contabilizzati.
    E sembra che non giocano un ruolo per questa versione semplice. Ma la memoria potrebbe anche svolgere un ruolo se si entra in una logica più complessa …

    Naturalmente, provare sulla vostra macchina.

    Questo è il motivo per cui ho suggerito di prendere in considerazione altre opzioni, in generale.

    Si prega di down voto la risposta o ti verrà chiesto di mem problemi…
    Una nota: l’Utilizzo di tubi ho potuto gestire grandi volumi di dati, un po ‘ di tempo in effetti, ma ho potuto ottenere risultati. Usare awk troppo. Delle materie prime macchine. Si prega di non fare awk un mostro, non utilizzare getline.
    Sono d’accordo con l’utilizzo di wc invece getline, ma ho due piccoli cavilli. In primo luogo, utilizzare nlines=$(wc -l < filename) (notare il reindirizzamento di stdin) in modo che nlines non sembra: 80 filename. Secondo, non credo che il tuo punto di riferimento valido. In x.r si deve aggiungere il tempo necessario per fare il wc e il test per FNR==nlines. La mia ipotesi è che la getline soluzione è effettivamente più veloce, perché si sta solo l’analisi filename una volta.

    OriginaleL’autore mariotti

Lascia un commento