one-liner: print all lines except the last 3?

拟墨画扇 提交于 2019-12-09 03:34:14

问题


I would like to simulate GNU's head -n -3, which prints all lines except the last 3, because head on FreeBSD doesn't have this feature. So I am thinking of something like

seq 1 10 | perl -ne ...

Here I have used 10 lines, but it can be any number larger than 3.

Can it be done in Perl or some other way on FreeBSD in BASH?

A super primitive solution would be

seq 1 10 | sed '$d' | sed '$d' | sed '$d'

回答1:


This works with a pipe as well as an input file:

seq 1 10 | perl -e'@x=<>;print@x[0..$#x-3]'



回答2:


seq 1 10 | perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}'

or

perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}' file

or

command | perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x'
perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x' file



回答3:


seq 1 10 | perl -ne 'push @l, $_; print shift @l if @l > 3'



回答4:


Pure bash and simple tools (wc and cut):

head -n $(($(wc -l file | cut -c-8)-3)) file

Disclaimer - I don't have access to FreeBSD right now, but this does work on OSX bash.




回答5:


Nobody seems to have use sed and tac, so here's one:

$ seq 10 | tac | sed '1,3d' | tac
1
2
3
4
5
6
7



回答6:


how about :

 seq 1 10 | perl -ne 'print if ( !eof  )' | perl -ne 'print if ( !eof  )' | perl -ne 'print if ( !eof  )' 



回答7:


This awk one-liner seems to do the job:

awk '{a[NR%4]=$0}NR>3{print a[(NR-3)%4]}' file



回答8:


Or do it with bash alone if you have version 4.0 or newer:

seq 1 10 | (readarray -t LINES; printf '%s\n' "${LINES[@]:(-3)}")

Update: This one would remove the last three lines instead of showing only them.

seq 1 10 | (readarray -t L; C=${#L[@]}; printf '%s\n' "${L[@]:0:(C > 3 ? C - 3 : 0)}")

For convenience it could be placed on a function:

function exclude_last_three {
    local L C
    readarray -t L; C=${#L[@]}
    printf '%s\n' "${L[@]:0:(C > 3 ? C - 3 : 0)}"
}

seq 1 10  | exclude_last_three
seq 11 20 | exclude_last_three



回答9:


Here's a late answer, because I was running into something like this yesterday.

This solution is:

  • pure bash
  • one-liner
  • reads the input stream only once
  • reads the input stream line-by-line, not all at once

Tested on Ubuntu, Redhat and OSX.

$ seq 1 10 | { n=3; i=1; while IFS= read -r ln; do [ $i -gt $n ] && cat <<< "${buf[$((i%n))]}"; buf[$((i%n))]="$ln"; ((i++)); done; }
1
2
3
4
5
6
7
$ 

It works by reading lines into a circular buffer implemented as an n-element array.

n is the number of lines to cut off the end of the file.

For every line i we read, we can echo the line i-n from the circular buffer, then store the line i in the circular buffer. Nothing is echoed until the first n lines are read. (i mod n) is the index into the array which implements the circular buffer.

Because the requirement is for a one-liner, I tried to make it fairly brief, unfortunately at the expense of readability.




回答10:


Another Awk solution that only uses minimal amount of buffers and prints lines quickly without needing to read all the lines first. It can also be used with pipes and large files.

awk 'BEGIN{X = 3; for(i = 0; i < X; ++i)getline a[i]}{i %= X; print a[i]; a[i++] = $0}'


来源:https://stackoverflow.com/questions/18923097/one-liner-print-all-lines-except-the-last-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!