gpt4 book ai didi

bash - 在 Bash 中转置文件的有效方法

转载 作者:行者123 更新时间:2023-11-29 08:38:49 25 4
gpt4 key购买 nike

我有一个巨大的制表符分隔文件,格式如下

X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11

我想只使用 bash 命令以一种有效的方式转置它(我可以编写十行左右的 Perl 脚本来做到这一点,但它的执行速度应该比本地 bash 慢功能)。所以输出应该是这样的

X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

我想到了这样的解决方案

cols=`head -n 1 input | wc -w`
for (( i=1; i <= $cols; i++))
do cut -f $i input | tr $'\n' $'\t' | sed -e "s/\t$/\n/g" >> output
done

但它很慢而且似乎不是最有效的解决方案。我在 this post 中看到了 vi 的解决方案,但它仍然太慢了。有什么想法/建议/好主意吗? :-)

最佳答案

awk '
{
for (i=1; i<=NF; i++) {
a[NR,i] = $i
}
}
NF>p { p = NF }
END {
for(j=1; j<=p; j++) {
str=a[1,j]
for(i=2; i<=NR; i++){
str=str" "a[i,j];
}
print str
}
}' file

输出

$ more file
0 1 2
3 4 5
6 7 8
9 10 11

$ ./shell.sh
0 3 6 9
1 4 7 10
2 5 8 11

Jonathan 在 10000 行文件上针对 Perl 解决方案的性能

$ head -5 file
1 0 1 2
2 3 4 5
3 6 7 8
4 9 10 11
1 0 1 2

$ wc -l < file
10000

$ time perl test.pl file >/dev/null

real 0m0.480s
user 0m0.442s
sys 0m0.026s

$ time awk -f test.awk file >/dev/null

real 0m0.382s
user 0m0.367s
sys 0m0.011s

$ time perl test.pl file >/dev/null

real 0m0.481s
user 0m0.431s
sys 0m0.022s

$ time awk -f test.awk file >/dev/null

real 0m0.390s
user 0m0.370s
sys 0m0.010s

Ed Morton 编辑(@ghostdog74 如果您不同意,请随时删除)。

也许这个带有一些更明确的变量名的版本将有助于回答下面的一些问题,并大致阐明脚本在做什么。它还使用制表符作为 OP 最初要求的分隔符,因此它可以处理空字段,并且巧合地为这种特殊情况稍微美化了输出。

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{
for (rowNr=1;rowNr<=NF;rowNr++) {
cell[rowNr,NR] = $rowNr
}
maxRows = (NF > maxRows ? NF : maxRows)
maxCols = NR
}
END {
for (rowNr=1;rowNr<=maxRows;rowNr++) {
for (colNr=1;colNr<=maxCols;colNr++) {
printf "%s%s", cell[rowNr,colNr], (colNr < maxCols ? OFS : ORS)
}
}
}

$ awk -f tst.awk file
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

上述解决方案适用于任何 awk(当然,旧的、损坏的 awk 除外 - YMMV)。

虽然上述解决方案确实将整个文件读入内存 - 如果输入文件太大,那么您可以这样做:

$ cat tst.awk
BEGIN { FS=OFS="\t" }
{ printf "%s%s", (FNR>1 ? OFS : ""), $ARGIND }
ENDFILE {
print ""
if (ARGIND < NF) {
ARGV[ARGC] = FILENAME
ARGC++
}
}
$ awk -f tst.awk file
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11

它几乎不使用内存,但每行中的字段数读取一次输入文件,因此它比将整个文件读入内存的版本慢得多。它还假定每行的字段数相同,并且它使用 GNU awk 来处理 ENDFILEARGIND 但任何 awk 都可以对 FNR 进行测试==1END

关于bash - 在 Bash 中转置文件的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1729824/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com