我正在寻找一种优雅的方法来逆转文本文件内容,不仅仅是逐行(如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 |
-------- -----发布于 2013-09-08 13:50:12
一个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; ) < fileRuby的另一个真正的衬线:
ruby -e '$stdin.readlines().each_slice(7).entries.reverse.each { |b| puts b; }' < file发布于 2013-09-09 10:45:49
您可以使用标准命令,使用临时文件来完成此操作。在我看来,它更短、更易读:)
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为每一行生成一个临时文件,名为blocknnnn,其中nnnn是序列号。cat将这些块放在一起,并按照ls -r给出的顺序排列文件列表。
优点:文件没有加载到内存中,因此在这方面没有限制其大小。
缺点:将数据的完整副本复制到磁盘,因此需要两倍的空间。
发布于 2013-09-08 14:08:26
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不是做这种操作的正确方法:)
编辑:
更短的巴什!
blockSize=7
readarray lines < file.txt
for (( i=${#lines[@]}-blockSize; i>=0; i-=blockSize )); do
( IFS=''; echo -n "${lines[*]:i:blockSize}" )
donehttps://stackoverflow.com/questions/18684359
复制相似问题