我有一个大的ascii文件,我想重新格式化它。输入格式为:
a ; 1 ; 2
b ; 2 ; 3
c ; 4 ; 5
d ; 6 ; 7
e ; 8 ; 9
f ; 10 ; 11
它有N=4行。输出格式应为
a ; 1 ; 2; c ; 4 ; 5; e ; 8 ; 9
b ; 2 ; 3; d ; 6 ; 7; f ; 10 ; 11
所以我想剪切n=2个连续的行,并将它们水平粘贴到一个新的结果文件中。
如何使用bash完成此操作?
您可以使用awk。
Awk逐行处理其输入文件,并允许您每次执行各种操作。它支持数组,因此对于手头的问题,我们可以使用一个数组,在该数组中,我们以正确的格式为最终输出准备数据。
最初,数组为空。对于第一个n
行,我们所要做的就是将该行存储在二维数组的新行中。例如,这给了我们:
| a ; 1 ; 2 |
| b ; 2 ; 3 |
我们是如何在awk中构建的?为了方便起见,awk提供了一个特殊的变量NR
,它始终保持输入文件中当前行的行号。所以我们可以使用该变量来索引数组的第一个维度,除了NR
是基于1的,所以我们需要减去1来进行基于0的索引:
a[NR-1] = $0
这里,$0
包含awk中当前行的内容。
在第一行n
之后,我们希望将每一行新行连接到数组中已经存储的内容,始终从顶部开始。所以我们需要注意两件事:
- 计算数组
a
的正确索引 - 执行串联操作
以下行同时执行这两项操作:
a[(NR-1)%n] = a[(NR-1)%n] "; " $0
请注意,数组索引的计算现在不再是NR-1
,而是使用mod
运算符%
的(NR-1)%n
。连接是微不足道的:我们只写三个部分来按顺序连接:1(上一个数组条目2(分隔符字符串;
,3(再次连接当前行。
然而,我们观察到了一些有趣的事情:由于awk处理未初始化变量的方式,我们几乎可以对第一行n
使用上面的表达式,因为mod
不会更改这些值,而a[(NR-1)%n]
在第一次使用时只是空字符串。唯一的问题是分隔符字符串:我们不希望它出现在行的开头。
但有一个简单的方法:我们可以简单地选择最后不打印。那么剩下要做的就是:
- 每行:
a[(NR-1)%n] = a[(NR-1)%n] "; " $0
- 最后:打印
a
的内容,但抑制前两个字符
这基本上就是下面的脚本所做的,除了还修剪了每行开头和结尾的空白(使用gsub
(,并通过使用命令行参数增加了一点便利:
#!/bin/sh
if [ $# -lt 2 ]; then
echo "USAGE: $(basename "${0}") <n> <file>+"
exit 1
fi
n=${1}
shift
awk -v "n=${n}" '
{ gsub(/^[ t]+|[ t]+$/, "", $0); a[(NR-1)%n] = a[(NR-1)%n] "; " $0 }
END {
for(i=0; i<n; i++) {
print substr(a[i],3)
}
}' ${@}
这是我的"非优雅解决方案";使用@Thomas的部分答案:
#!/bin/bash
in_file=data.txt
# start at line 2
n=2
begin=$n
num_iterations=2
# first paste 2 starting lines to result file
head -n $n $in_file > result.txt
# then paste remaining
for i in $(seq $num_iterations); do
# increment starting line by 2
let "begin+=$n"
# cut 2 lines and paste to temporary file
head -n $begin $in_file | tail -n $n | sed 's/ *# */ ; /' > tmp.txt
# concat results horizontally
paste -d '; ' result.txt tmp.txt > result_tmp.txt;
# update result file
mv result_tmp.txt result.txt
done
更好地尝试理解@oguz-ismail 未解释的解决方案
awk -v n=2 '++i>n{i=1} {r[i]=r[i]s[i]$0;s[i]="; "} END{for(i=1;i<=n;i++)print r[i]}' file