尾部反转/打印除最后n行外的所有内容?

54

有没有一种(POSIX命令行)方法可以打印除最后n行之外的所有文件?使用情况是,我将拥有多个大小未知的文件,其中都包含已知大小的锅炉板页脚,我想要移除它。我想知道是否已经有工具在自己编写之前执行此操作。

9个回答

56

大多数版本的head(1) - 尤其是GNU派生的,但不是BSD派生的 - 都有一个功能可以做到这一点。如果您使用负数来打印行数,则它将显示文件顶部,但不包括末尾。

像这样:

head -n -10 textfile

10
在POSIX中,-n必须是一个正整数。 - choroba
2
对于严格的POSIX来说是正确的。我应该在我的“大多数”中更清楚。如果你想要一个更快、更简单的POSIX方法,我会假设wc -l加上一些数学运算可以得到一个值传递给head - Adam B
大多数head(1)的实现都缺乏这个功能。我所知道的唯一实现有这个非可移植代码。 - Good Person
7
在Mac OS X中使用 ghead 命令(需要通过 brew install coreutils 安装)。 - jchook

16

可能不如“wc”+“算数”+“tail”方法高效,但更易于查看:

tail -r file.txt | tail +NUM | tail -r

其中NUM比您想要删除的行数多一个,例如+11将打印除了最后10行以外的所有行。这适用于不支持head -n -NUM语法的BSD系统。


我认为你的意思是... tac | tail -n+5 | tac - anthony
1
@anthony - 我需要一个在BSD上也能工作的解决方案 - 在BSD上没有“tac”,你必须使用“tail -r”。 - user9645
所以这两种解决方案都不是可移植/符合POSIX标准的。 - saulius2

8

head 实用工具是你的朋友。

head 的 man 页面中可以了解到:

-n, --lines=[-]K
     print the first K lines instead of the first 10;
       with the leading `-', print all but the last K lines of each file

6
在POSIX中,-n必须是一个正整数。 - choroba
7
很遗憾,在派生自BSD系统(例如OSX)中,负值不是“-n”命令的有效参数。 - Tossrock

5

没有标准命令可以做到这一点,但是您可以使用awk或sed来填充一个N行的缓冲区,并在其满员后从头部进行打印。例如,使用awk:

awk -v n=5 '{if(NR>n) print a[NR%n]; a[NR%n]=$0}' file

4
cat <filename> | head -n -10 # Everything except last 10 lines of a file
cat <filename> | tail -n +10 # Everything except 1st 10 lines of a file

1
如果页脚以不在其他地方出现的一致行开头,您可以使用 sed 命令:
sed '/FIRST_LINE_OF_FOOTER/q' filename

这将打印页脚的第一行;如果您想避免这种情况:

sed -n '/FIRST_LINE_OF_FOOTER/q;p' filename

如果页脚的大小在未来发生变化,那么这种方法可能比计算行数更加健壮。(或者如果第一行发生变化,则可能不够健壮。)

另一个选择是,如果您的系统的head命令不支持head -n -10,则可以预先计算要显示的行数。以下内容依赖于bash特定的语法:

lines=$(wc -l < filename) ; (( lines -= 10 )) ; head -$lines filename

请注意,一些版本的head支持head -NUMBER语法以实现向后兼容性;POSIX仅允许使用head -n NUMBER形式。POSIX还仅允许-n参数的参数为正十进制整数;head -n 0不一定是无操作。
一个符合POSIX标准的解决方案是:
lines=$(wc -l < filename) ; lines=$(($lines - 10)) ; head -n $lines filename

如果你需要处理古老的 pre-POSIX shell,你可以考虑这个:
lines=`wc -l < filename` ; lines=`expr $lines - 10` ; head -n $lines filename

如果文件只有10行或更少,任何一个都可能出现奇怪的问题。


POSIX除了古老的反引号命令替换语法外,还有更合理的$(...)语法,并且它还具有$((...))算术扩展。因此,不需要使用古老的反引号和expr。[POSIX命令替换](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_03) - geirha
1
@geirha:说得好,问题确实特别提到了POSIX。但是知道古老的语法可能会有用,以防仍在使用任何早于POSIX的shell。 - Keith Thompson

0

tac file.txt | tail +[n+1] | tac

这个答案类似于user9645的,但是它避免了tail -r命令,该命令在许多系统中也不是一个有效的选项。例如,参见https://ubuntuforums.org/showthread.php?t=1346596&s=4246c451162feff4e519ef2f5cb1a45f&p=8444785#post8444785

请注意,在我尝试测试的系统上,括号中的+1是必需的,但在您的系统上可能不需要。因此,为了删除最后一行,我必须在括号中放置2。这可能与您需要将最后一行以常规换行符结尾有关。可以说,这使得最后一行成为空行。如果您不这样做,那么tac命令将合并最后两行,因此删除“最后”一行(或第一个到tail命令)实际上将删除最后两行。
我的答案也应该是列出的解决方案中对于缺乏改进版head的系统来说最快的解决方案。因此,我认为它既是最强大的,也是所有列出答案中最快的。

0
head -n $((`(wc -l < Windows_Terminal.json)`)) Windows_Terminal.json

这在Linux和MacOS上可以运行,但请注意,Mac不支持负值,所以这非常方便。

注意:将Windows_Terminal.json替换为您的文件名。


-2

很简单。您需要将“+”添加到要避免的行数。

此示例向您提供除前9行之外的所有行

tail -n +10 inputfile

(是的,不是前10行...因为它计数方式不同...如果您想要10行,请键入 tail -n 11 inputfile)


不是用户所要求的! - anthony

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接