问题
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