如何反转文件中的行顺序?

时间:2009-04-12 21:34:54

标签: shell unix command-line

我想颠倒文本文件(或标准输入)中行的顺序,保留每行的内容。

所以,即从:

开始
foo
bar
baz

我想以

结束
baz
bar
foo

是否有针对此的标准UNIX命令行实用程序?

25 个答案:

答案 0 :(得分:1246)

另外值得一提的是:tac(嗯,哼,cat的反面)。 coreutils的一部分。

将一个文件翻转到另一个

tac a.txt > b.txt

答案 1 :(得分:394)

BSD尾巴:

tail -r myfile.txt

参考:FreeBSDNetBSDOpenBSDOS X手册页。

答案 2 :(得分:147)

well-known sed tricks

# reverse order of lines (emulates "tac")
# bug/feature in HHsed v1.5 causes blank lines to be deleted
sed '1!G;h;$!d'               # method 1
sed -n '1!G;h;$p'             # method 2

(解释:在非初始行前加上保持缓冲区,交换行和保持缓冲区,在末尾打印出行)

或者(执行速度更快)from the awk one-liners

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

如果你不记得了,

perl -e 'print reverse <>'

在具有GNU实用程序的系统上,其他答案更简单,但并非全世界都是GNU / Linux ......

答案 3 :(得分:53)

如果您恰好在vim使用

:g/^/m0

答案 4 :(得分:38)

$ (tac 2> /dev/null || tail -r)

尝试使用适用于Linux的tac,如果不起作用,请使用适用于BSD和OSX的tail -r

答案 5 :(得分:37)

tac <file_name>

示例:

$ cat file1.txt
1
2
3
4
5

$ tac file1.txt
5
4
3
2
1

答案 6 :(得分:26)

在命令末尾放置: | tac

tac完全满足您的要求,它“将每个FILE写入标准输出,最后一行在前。”

tac与cat :-)相反。

答案 7 :(得分:24)

尝试以下命令:

grep -n "" myfile.txt | sort -r -n | gawk -F : "{ print $2 }"

答案 8 :(得分:15)

Just Bash :)(4.0 +)

function print_reversed {
    local lines i
    readarray -t lines

    for (( i = ${#lines[@]}; i--; )); do
        printf '%s\n' "${lines[i]}"
    done
}

print_reversed < file

答案 9 :(得分:11)

最简单的方法是使用tac命令。 tac cat是反向的。 例如:

$ cat order.txt
roger shah 
armin van buuren
fpga vhdl arduino c++ java gridgain
$ tac order.txt > inverted_file.txt
$ cat inverted_file.txt
fpga vhdl arduino c++ java gridgain
armin van buuren
roger shah 

答案 10 :(得分:9)

我非常喜欢“ tail -r ”的答案,但我最喜欢的gawk答案是....

gawk '{ L[n++] = $0 } 
  END { while(n--) 
        print L[n] }' file

答案 11 :(得分:2)

修改 以下内容生成一个从1到10的随机排序的数字列表:

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') **...**

其中点被替换为反转列表的实际命令

<强> TAC

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(tac)

python:在sys.stdin上使用[:: - 1]

seq 1 10 | sort -R | tee /tmp/lst |cat <(cat /tmp/lst) <(echo '-------') \
<(python -c "import sys; print(''.join(([line for line in sys.stdin])[::-1]))")

答案 12 :(得分:2)

对于可能在shell脚本中使用tac的跨操作系统(即OSX,Linux)解决方案,如上所述,使用自制软件,那么就像这样使用别名:

brew install coreutils
echo "alias tac='gtac'" >> ~/.bash_aliases (or wherever you load aliases)
source ~/.bash_aliases
tac myfile.txt

答案 13 :(得分:2)

这适用于BSD和GNU。

awk '{arr[i++]=$0} END {while (i>0) print arr[--i] }' filename

答案 14 :(得分:1)

我碰巧要高效地获取非常大的文本文件的最后n行。

我尝试的第一件事是tail -n 10000000 file.txt > ans.txt,但是我发现它非常慢,因为tail必须寻找位置,然后再移回以打印结果。

当我意识到这一点时,便切换到另一种解决方案:tac file.txt | head -n 10000000 > ans.txt。这次,搜索位置只需从末端移动到所需位置即可,它可以节省50%的时间

带回家消息:

如果您的tac file.txt | head -n n没有tail选项,请使用-r

答案 15 :(得分:1)

如果要修改文件,可以运行

sed -i '1!G;h;$!d' filename

这样就无需创建临时文件,然后删除或重命名原始文件,并且结果相同。例如:

$tac file > file2
$sed -i '1!G;h;$!d' file
$diff file file2
$

基于answer by ephemient,它几​​乎但不是完全满足我的要求。

答案 16 :(得分:1)

最佳解决方案:

tail -n20 file.txt | tac

答案 17 :(得分:0)

对于Emacs用户:C-x h(选择整个文件),然后M-x reverse-region。也仅适用于选择零件或线并将其还原。

答案 18 :(得分:0)

我看到很多有趣的想法。但是尝试我的想法。将文本输入以下内容:

  

rev | tr'\ n''〜'|转速| tr'〜''\ n'

假定字符“〜”不在文件中。自1961年以来,这在所有UNIX shell上都应适用。或者类似的东西。

答案 19 :(得分:0)

您可以在命令行上使用Perl:

perl -e 'my @b=(); while(<>) {push(@b, $_);}; print join("", reverse(@b));' orig > rev

答案 20 :(得分:-1)

tail -r 适用于大多数Linux和MacOS系统

seq 1 20 |尾巴-r

答案 21 :(得分:-1)

您可以使用vim stdinstdout来完成此操作。您也可以ex使用POSIX compliantvim只是ex的可视模式。实际上,您可以ex使用vim -evim -E(改进ex模式)。 vim非常有用,因为与sed之类的工具不同,它会缓存文件以进行编辑,而sed则用于流。您可能可以使用awk,但您必须手动缓冲变量中的所有内容。

我们的想法是:

  1. 从stdin阅读
  2. 对于每一行,将其移至第1行(反转)。命令为g/^/m0。这意味着全局,每行g;匹配行的开头,匹配任何^;在地址0之后移动它,即第1行m0
  3. 打印一切。命令为%p。这意味着所有行%的范围;打印行p
  4. 强行退出而不保存文件。命令为q!。这意味着退出q;强有力地!
  5. # Generate a newline delimited sequence of 1 to 10
    $ seq 10
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    # Use - to read from stdin.
    # vim has a delay and annoying 'Vim: Reading from stdin...' output
    # if you use - to read from stdin. Use --not-a-term to hide output.
    # --not-a-term requires vim 8.0.1308 (Nov 2017)
    # Use -E for improved ex mode. -e would work here too since I'm not
    # using any improved ex mode features.
    # each of the commands I explained above are specified with a + sign
    # and are run sequentially.
    $ seq 10 | vim - --not-a-term -Es +'g/^/m0' +'%p' +'q!'
    10
    9
    8
    7
    6
    5
    4
    3
    2
    1
    # non improved ex mode works here too, -e.
    $ seq 10 | vim - --not-a-term -es +'g/^/m0' +'%p' +'q!'
    
    # If you don't have --not-a-term, use /dev/stdin
    seq 10 | vim -E +'g/^/m0' +'%p' +'q!' /dev/stdin
    
    # POSIX compliant (maybe)
    # POSIX compliant ex doesn't allow using + sign to specify commands.
    # It also might not allow running multiple commands sequentially.
    # The docs say "Implementations may support more than a single -c"
    # If yours does support multiple -c
    $ seq 10 | ex -c "execute -c 'g/^/m0' -c '%p' -c 'q!' /dev/stdin
    
    # If not, you can chain them with the bar, |. This is same as shell
    # piping. It's more like shell semi-colon, ;.
    # The g command consumes the |, so you can use execute to prevent that.
    # Not sure if execute and | is POSIX compliant.
    seq 10 | ex -c "execute 'g/^/m0' | %p | q!" /dev/stdin
    

    如何使此可重复使用

    我使用我调用ved的脚本(vim编辑器,如sed)来使用vim编辑stdin。将其添加到路径中名为ved的文件中:

    #!/usr/bin/env sh
    
    vim - --not-a-term -Es "$@" +'%p | q!'
    

    我使用一个+命令而不是+'%p' +'q!',因为vim限制你使用10个命令。因此合并它们允许"$@"具有9个+命令而不是8个。

    然后你可以这样做:

    seq 10 | ved +'g/^/m0'
    

    如果您没有vim 8,请将其放在ved中:

    #!/usr/bin/env sh
    
    vim -E "$@" +'%p | q!' /dev/stdin
    

答案 22 :(得分:-1)

我有同样的问题,但我也希望第一行(标题)保持最佳状态。所以我需要使用awk的力量

cat dax-weekly.csv | awk '1 { last = NR; line[last] = $0; } END { print line[1]; for (i = last; i > 1; i--) { print line[i]; } }'

PS也适用于cygwin或gitbash

答案 23 :(得分:-4)

rev
text here

rev <file>

rev texthere

答案 24 :(得分:-6)

sort -r < filename

rev < filename