在bash中反转行组
我正在寻找一种优雅的方法来反转文本文件内容,不仅仅是逐行(像tac一样),而是按行(节)分组。以下示例应详尽无遗: 原始文件内容:在bash中反转行组,bash,reverse,Bash,Reverse,我正在寻找一种优雅的方法来反转文本文件内容,不仅仅是逐行(像tac一样),而是按行(节)分组。以下示例应详尽无遗: 原始文件内容: -------- ----- time | -------- | 10:00:00 |--- section 1 10:00:10 | 10:00:20 | -------- ----- -------- ----- time | ------
-------- -----
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 |
-------- -----
一个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
另一个可能排除无效数据的更安全的变体:
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'/^-----+$/{++j};!j{next};{a[i++]=$0}j==3{t=”“;for(k=0;kawk
绝对是正确的选择,但这里有一个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
您可以使用标准命令,使用临时文件来实现这一点。在我看来,它甚至更短,更具可读性:)
对于split
命令:
-4
表示4个数字的后缀
-d
表示数字后缀
-l 7
按此行数拆分源文件
data.txt
要读取的文件
块
临时文件的前缀
split
为每个行块生成一个名为blocknnnn
的临时文件,其中nnnn
是一个序列号。cat
将这些块放在一起,文件列表的顺序与ls-r
相反
优点:该文件不加载到内存中,因此在这方面它的大小没有限制
缺点:数据的完整副本被复制到磁盘,因此需要两倍的空间这是我的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
我希望有一个不链接到特定间隔符的解决方案,输入应该只是属于该节的行数…@Tom我对此进行了更新。@Tom我也添加了一个Bash解决方案。如前所述,请考虑分隔符(-----------)作为文件的普通行。解决方案应取多行并反转。更准确地说,解决方案应将行数作为输入并反转。例如,tac逐行反转,就像传递的输入为1一样。
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
( 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 -e '$stdin.readlines().each_slice(7).entries.reverse.each { |b| puts b; }' < file
#!/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
#!/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
blockSize=7
readarray lines < file.txt
for (( i=${#lines[@]}-blockSize; i>=0; i-=blockSize )); do
( IFS=''; echo -n "${lines[*]:i:blockSize}" )
done
split -a 4 -d -l 7 data.txt block
cat $(ls -r block*) > reversed-data.txt
rm block*
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