1

我正在寻找一种优雅的方法来反转文本文件内容,不仅仅是逐行(如 tac 那样),而是逐行(部分)。以下示例应该是详尽的:

原始文件内容:

--------    -----
    time        |
--------        | 
10:00:00        |--- section 1 
10:00:10        | 
10:00:20        |
--------    -----
--------    -----
    time        |
--------        | 
10:01:00        |--- section 2 
10:01:10        | 
10:01:20        |
--------    -----

期望的输出:

--------    -----
    time        |
--------        | 
10:01:00        |--- section 2 
10:01:10        | 
10:01:20        |
--------    -----
--------    -----
    time        |
--------        | 
10:00:00        |--- section 1 
10:00:10        | 
10:00:20        |
--------    -----
4

4 回答 4

2

一个 awk 可以做到:

awk '{a[i++]=$0};/-----/{++j};j==3{t="";for(k=0;k<i;++k)t=t a[k]"\n";b[++l]=t;i=j=0}END{for(i=l;i;--i)printf "%s", b[i]}' file

输出:

----------------------------------------------------------------
date        time        hostname    cpu     ram     /       /opt
----------------------------------------------------------------
2013/09/08  15:40:00    server1     20%     30%     50%     70%
2013/09/08  15:40:00    server2     15%     21%     49%     72%
2013/09/08  15:40:00    server3     20%     40%     40%     75%
----------------------------------------------------------------
----------------------------------------------------------------
date        time        hostname    cpu     ram     /       /opt
----------------------------------------------------------------
2013/09/08  15:35:00    server1     15%     30%     50%     70%
2013/09/08  15:35:00    server2     18%     21%     49%     72%
2013/09/08  15:35:00    server3     15%     40%     40%     75%
----------------------------------------------------------------
----------------------------------------------------------------
date        time        hostname    cpu     ram     /       /opt
----------------------------------------------------------------
2013/09/08  15:30:00    server1     20%     30%     50%     70%
2013/09/08  15:30:00    server2     10%     21%     49%     72%
2013/09/08  15:30:00    server3     15%     40%     40%     75%
----------------------------------------------------------------

另一个可以排除无效数据的更安全的变体:

awk '/^-----+$/{++j};!j{next};{a[i++]=$0}j==3{t="";for(k=0;k<i;++k)t=t a[k]"\n";b[++l]=t;i=j=0}END{for(i=l;i;--i)printf "%s", b[i]}' file

另一个基于行数的:

awk '{a[i++]=$0}i==7{t="";for(k=0;k<i;++k)t=t a[k]"\n";b[++l]=t;i=0}END{for(i=l;i;--i)printf "%s", b[i]}' file

简单一点:

awk '{a[i++]=$0}i==7{t="";for(i=0;i<7;++i)t=t a[i]"\n";b[++j]=t;i=0}END{for(;j;--j)printf "%s", b[j]}' file

另一种在 Bash 中使用相同概念的简单方法:

( IFS=$'\n'; while read -r A[I++]; do [[ I -eq 7 ]] && { B[++J]="${A[*]}"; I=0; }; done; for ((;J;--J)); do echo "${B[J]}"; done; ) < file

Ruby 的另一个真正的单行代码:

ruby -e '$stdin.readlines().each_slice(7).entries.reverse.each { |b| puts b; }' < file
于 2013-09-08T13:50:12.337 回答
1

您可以使用标准命令,使用临时文件来做到这一点。在我看来,它甚至更短,更具可读性:)

split -a 4 -d -l 7 data.txt block
cat $(ls -r block*) > reversed-data.txt
rm block*

对于split命令:

  • -a 4用于 4 个数字的后缀
  • -d用于数字后缀
  • -l 7按此行数拆分源文件
  • data.txt要读取的文件
  • block临时文件前缀

split为每个行块生成一个临时文件,命名为blocknnnnwherennnn是一个序列号。cat将块放在一起,文件列表以相反的顺序排列,就像ls -r给出的那样。

优点:文件没有加载到内存中,所以在这方面对其大小没有限制

缺点:将数据的完整副本复制到磁盘,因此需要两倍的空间

于 2013-09-09T10:45:49.893 回答
0

awk绝对是正确的方法,但这里有一个 bash 替代方案:

#!/bin/bash

separator='----------------------------------------------------------------' # each block must end with a separator
blockSeparators=3 # number of separators in each block. Could be 1 as well

dataArr=()
current=0
subCounter=0
while read -r curLine; do
    dataArr[current]+=$curLine$'\n'
    if [[ $curLine == "$separator" ]]; then
        (( ++subCounter == blockSeparators )) && (( current++ , subCounter=0 ))
    fi
done < file.txt

for (( i=${#dataArr[@]}; i>=0; i-- )); do
    echo -n "${dataArr[i]}"
done

如果你知道你所有的块正好有 7 行:

#!/bin/bash

blockLines=7

dataArr=()
current=0
lineCounter=0
while read -r curLine; do
    dataArr[current]+=$curLine$'\n'
    (( ++lineCounter == blockLines )) && (( current++ , lineCounter=0 ))
done < file.txt

for (( i=${#dataArr[@]}; i>=0; i-- )); do
    echo -n "${dataArr[i]}"
done

但如前所述,使用awk解决方案。Bash 做这样的操作是不正确的:)

编辑:

在 bash 中甚至更短!

blockSize=7

readarray lines < file.txt
for (( i=${#lines[@]}-blockSize; i>=0; i-=blockSize )); do
    ( IFS=''; echo -n "${lines[*]:i:blockSize}" )
done
于 2013-09-08T14:08:26.443 回答
0

这是我的 bash 解决方案

tmparr=()
blockSize=7
i=$blockSize
tac file | while read line; do  
tmparr[$i]="$line" && ((i--))  
[ $i -eq 0 ] && i=$blockSize && for j in "${tmparr[@]}"; do echo "$j"; done
done
于 2013-09-16T01:49:19.330 回答