如何从文件向后打印行(不使用“ tac”)?


Answers:


33

使用sed效仿tac

sed '1!G;h;$!d' ${inputfile}

3
这是一个很好的解决方案,但是对它如何工作的一些解释会更好。
陈征费

10
这是著名sed的单线飞机。请参见“ 36.行的逆序(模拟“ tac” Unix命令”)。在著名桑达单行解释了它是如何工作的完整说明。
Johnsyweb 2011年

6
也许值得注意的是:“这两个单行实际上使用了很多内存,因为它们在打印出来之前将整个文件以相反的顺序保存在保持缓冲区中。对于大文件,请避免使用这些单行。”
Shickadance先生2011年

其他所有答案也都这样做(可能使用的答案除外sort-可能会使用一个临时文件)。
Random832

1
请注意,tac对于常规文件更快,因为它会向后读取文件。对于管道,它必须与其他解决方案(保存在内存或临时文件中)相同,因此并没有明显更快。
斯特凡Chazelas


6

awk '{a[i++]=$0} END {for (j=i-1; j>=0;) print a[j--] }' file.txt

通过awk一班轮


4
较短的一个:awk 'a=$0RS a{}END{printf a}'
ninjalj 2011年

@ninjalj:它可能更短,但是随着文件大小变大,它变得非常慢。等待2分钟30秒后我放弃了。but your first perl reverse <>`它是页面上最好/最快的答案(对我来说),比该awk答案快10倍(所有awk答案在时间上都
差不多

1
或者awk '{a[NR]=$0} END {while (NR) print a[NR--]}'
斯特凡Chazelas

5

当您要求在bash中进行操作时,这是一个不使用awk,sed或perl的解决方案,而只是使用bash函数:

reverse ()
{
    local line
    if IFS= read -r line
    then
        reverse
        printf '%s\n' "$line"
    fi
}

输出

echo 'a
b
c
d
' | reverse

d
c
b
a

如预期的那样。

但是要注意,行存储在内存中,在每个递归调用的函数实例中只有一行。大文件时要格外小心。


1
与其他答案中最慢的一个相比,随着文件大小的增加,它很快变得不切实际地变慢,并且正如您所建议的,它非常容易消耗内存:* bash递归函数...但这是一个有趣的想法。分段错误*
Peter.O 2012年

4

您可以通过以下方式传递它:

awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

awk每行的前缀是行号,后跟一个空格。通过以sort相反的顺序(数字样式)在第一个字段(行号)上排序来反转行的顺序。然后sed删除行号。

下面的示例演示了此操作:

pax$ echo 'a
b
c
d
e
f
g
h
i
j
k
l' | awk '{print NR" "$0}' | sort -k1 -n -r | sed 's/^[^ ]* //g'

它输出:

l
k
j
i
h
g
f
e
d
c
b
a

好。谢谢!我会尝试的。并感谢您的评论!

5
cat -n行为很像awk '{print NR" "$0}'
Chen Levy

2
我认为这称为Schwartzian转换,或decorate-sort-
unecorate

这种通用方法很好,因为如果任务太大而无法在内存中合理地进行处理,则排序可以处理磁盘上的文件。但是,如果您在步骤之间使用临时文件而不是管道,则可能会使内存更缓和。
mc0e

1

在perl中:

cat <somefile> | perl -e 'while(<>) { push @a, $_; } foreach (reverse(@a)) { print; }'

2
或更短的时间perl -e 'print reverse<>'
ninjalj 2011年

2
(实际上,有一个较短的,但它确实难看,见证了这场可怕:perl -pe '$\=$_.$\}{'
ninjalj

@Frederik Deweerdt:很快,但是却输掉了第一行... // @ ninjalj:reverse<)很快:很好!但是随着行数的增加,“真的很丑”的速度非常慢。!! ....
Peter.O 2012年

@ Peter.O,-n那里多余,谢谢。
Frederik Deweerdt 2012年

关于这一点的好处是,文件的内容不一定要读到内存中(除非可能以块方式读入sort)。
库萨兰达

0

仅BASH解决方案

将文件读入bash数组(一行=数组的一个元素)并以相反的顺序打印出数组:

i=0 

while read line[$i] ; do
    i=$(($i+1))
done < FILE


for (( i=${#line[@]}-1 ; i>=0 ; i-- )) ; do
    echo ${line[$i]}
done

可以使用缩进的行来尝试... philfr的版本稍微好一点,但仍然是slooooow,所以实际上,在涉及文本处理时,不要使用while..read
don_crissti 2015年

使用IFS=''read -r防止各种逃脱和尾随IFS的拆卸将其拧紧。我认为mapfile ARRAY_NAME内置的bash 是读取数组的更好解决方案。
Arthur2e5

0

Bash,mapfile在Fiximan的评论中提到过,实际上是一个可能更好的版本:

# last [LINES=50]
_last_flush(){ BUF=("${BUF[@]:$(($1-LINES)):$1}"); } # flush the lines, can be slow.
last(){
  local LINES="${1:-10}" BUF
  ((LINES)) || return 2
  mapfile -C _last_flush -c $(( (LINES<5000) ? 5000 : LINES+5 )) BUF
  BUF=("${BUF[@]}") # Make sure the array subscripts make sence, can be slow.
  ((LINES="${#BUF[@]}" > LINES ? LINES : "${#BUF[@]}"))
  for ((i="${#BUF[@]}"; i>"${#BUF[@]}"-LINES; i--)); do echo -n "${BUF[i]}"; done
}

其性能基本上可与 sed解决方案,并且随着请求的行数减少而变得更快。



0
  • 用nl编号行
  • 按数字反向排序
  • 用sed删除数字

如下所示:

echo 'e
> f
> a
> c
> ' | nl | sort -nr | sed -r 's/^ *[0-9]+\t//'

结果:

c
a
f
e

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.