我想模拟 GNU's head -n -3
,它会打印除最后 3 行之外的所有行,因为head
在 FreeBSD 上没有此功能。所以我在想类似的东西
seq 1 10 | perl -ne ...
这里我使用了 10 行,但它可以是任何大于 3 的数字。
可以用 Perl 或其他方式在 BASH 中的 FreeBSD 上完成吗?
一个超级原始的解决方案是
seq 1 10 | sed '$d' | sed '$d' | sed '$d'
seq 1 10 | perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}'
或者
perl -e '@x=("")x3;while(<>){print shift @x;push @x,$_}' file
或者
command | perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x'
perl -pe 'BEGIN{@x=("")x3}push @x,$_;$_=shift @x' file
seq 1 10 | perl -ne 'push @l, $_; print shift @l if @l > 3'
纯 bash 和简单的工具(wc 和 cut):
head -n $(($(wc -l file | cut -c-8)-3)) file
免责声明 - 我现在无法访问 FreeBSD,但这确实适用于 OSX bash。
这适用于管道以及输入文件:
seq 1 10 | perl -e'@x=<>;print@x[0..$#x-3]'
似乎没有人使用sed
and tac
,所以这里有一个:
$ seq 10 | tac | sed '1,3d' | tac
1
2
3
4
5
6
7
怎么样 :
seq 1 10 | perl -ne 'print if ( !eof )' | perl -ne 'print if ( !eof )' | perl -ne 'print if ( !eof )'
这个 awk 单线似乎可以完成这项工作:
awk '{a[NR%4]=$0}NR>3{print a[(NR-3)%4]}' file
如果您有 4.0 或更高版本,或者单独使用 bash:
seq 1 10 | (readarray -t LINES; printf '%s\n' "${LINES[@]:(-3)}")
更新:这一行将删除最后三行,而不是只显示它们。
seq 1 10 | (readarray -t L; C=${#L[@]}; printf '%s\n' "${L[@]:0:(C > 3 ? C - 3 : 0)}")
为方便起见,它可以放在一个函数上:
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
这是一个迟到的答案,因为我昨天遇到了这样的事情。
这个解决方案是:
在 Ubuntu、Redhat 和 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
$
它通过将行读入实现为 n 元素数组的循环缓冲区来工作。
n 是要切断文件末尾的行数。
对于我们读取的每一行 i,我们可以从循环缓冲区中回显该行,然后将行 i 存储在循环缓冲区中。在读取前 n 行之前,不会回显任何内容。(i mod n) 是实现循环缓冲区的数组的索引。
因为要求是单行,所以我试图让它相当简短,不幸的是以牺牲可读性为代价。
另一种 awk 解决方案只使用最少量的缓冲区并快速打印行,而无需先读取所有行。它也可以用于管道和大文件。
awk 'BEGIN{X = 3; for(i = 0; i < X; ++i)getline a[i]}{i %= X; print a[i]; a[i++] = $0}'