как определить последнюю строку в awk до конца

Я пытаюсь добавить последнюю строку в файл, который я создаю. как можно обнаружить последнюю строку файла в awk до END ? мне нужно это сделать, потому что переменные не работают в END блок, поэтому я пытаюсь избежать использования END.

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

до END, Я не хочу этого:

awk 'END{print "somethins new" >> "newfile.txt"}'

большое спасибо заранее..

6 ответов


один из вариантов-использовать getline функция для обработки файла. Он возвращается 1 на успех, 0 в конце файла и -1 на ошибку.

awk '
    FNR == 1 {

        ## Process first line.
        print FNR ": " ;

        while ( getline == 1 ) {
            ## Process from second to last line.
            print FNR ": " ;
        }

        ## Here all lines have been processed.
        print "After last line";
    }
' infile

предполагая, что infile С такими данными:

one
two
three
four
five

выход будет:

1: one                                                                                                                                                                                                                                       
2: two                                                                                                                                                                                                                                       
3: three
4: four
5: five
After last line

$ cat file 
1
2
3
4
5

прочитав один и тот же файл дважды ( рекомендуется )

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

используя getline

$ awk 'BEGIN{while((getline t < ARGV[1]) > 0)last++;close(ARGV[1])}{print , ((last==FNR)?"I am Last":"")}' file
1
2
3
4
5 I am Last

печать предыдущей строки. Когда текущая строка 2, строка печати 1, если текущая строка равна 3, выведите строку 2. .... до конца

awk '{
    if (NR>1) {
        # process str
        print str;
    }
    str=;
}
END {
    # process whatever needed before printing the last line and then print the last line.
    print str;
}'

Вы можете получить количество строк в файл с помощью "wc -l" | getline filesize в блоке begin и использовать NR == filesize для проверки последней строки в теле скрипта.


можно использовать ENDFILE, он выполняет перед END:

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

ENDFILE существует в последней версии awk (>4.0, я думаю).


Я знаю, что ответ был принят, но это просто неправильно.

потому что вы хотите использовать awk в парсер, а не как код.

Awk должен использоваться в некоторых каналах unix, и он не должен использоваться ни в одной логике.

у меня была такая же проблема и я решил ее в awk и такой:

nlines=wc -l <file>

cat / awk-v nl=${nlines} ' {if (nl != NR) {печать $0,",","\";} else {print;}}' >> ${someout}

есть важный момент здесь: трубы, смыв и ОЗУ.

Если вы сделаете awk, чтобы выплюнуть его выход, вы можете передать его следующему процессору.

Если вы используете getline, и в частности в цикле, вы можете не увидеть конец.

getline следует использовать только для строки и возможной зависимости от следующей строки.

Я люблю awk, но мы не можем делать все это!

редактировать:

за кого проголосовал ответ, я просто хочу представьте этот скрипт:

#! /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 ": " ;

        while ( getline == 1 ) {
            ## Process from second to last line.
            print FNR ": " ;
        }
    }
' x.r
#
EOF
#
chmod +x awkGetline.sh
#
cat <<"EOF" > awkPlain.sh
#! /bin/sh
#
awk '
    {print FNR ": " ;}
' 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+; print , 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+; print , 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+; print , 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+; print , 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+; print , 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+; print , 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+; print , 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+; print , sum/10;} END {print "SUM Plain", sum;}' | grep SUM
#

и, конечно, первые результаты:

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

где вы экономите около 10% времени только из-за getline.

рассмотрим это в более сложной логике, и вы можете получить даже худшую картину. В этой простой версии учет памяти не учитывается. И, кажется, они не играют роли для этой простой версии. Но память также может сыграть свою роль, если вы попадете в более сложную логику ...

конечно попробовать это на твоем автоответчике.

вот почему я предлагал рассмотреть другие варианты в целом.