如何输出文件的对角线?
例如,我得到了一个包含以下内容的文件。
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
输出应该看起来像:1 7 3 9 5
或类似的东西。
我可以通过 cut (cut -d "," -f5 filename) 输出一列,但我不确定要写什么才能仅输出对角线。
答案1
awk 解决方案,不如 @don_chrissti 解决方案那么优雅,但可以在非正方形的地方工作。
awk -F, '{a=a$++n" "}END{print a}' file
答案2
Python 和 numpy
我们正在查看的输入数据可以被视为矩阵或二维数组。现在,如果我们从这个角度来处理这个问题,就会发现有几种计算工具可用于操作矩阵。特别是,Python 的numpy
模块允许这样做。因此,我们可以使用两个东西 - 函数loadtxt()
和diagonal()
提取所需的数据:
$ python -c 'import sys,numpy;a=numpy.loadtxt(sys.argv[1],dtype=int,delimiter=",");print( a.diagonal() )' input.txt
[1 7 3 9 5]
现在,这就是已完成的大部分工作。为了使输出漂亮,我们只需将获得的数据转换为字符串,并从各个数据中创建一个以空格分隔的字符串。就像这样:
$ python -c 'import sys,numpy;a=numpy.loadtxt(sys.argv[1],delimiter=",");print(" ".join( [ str(int(i)) for i in a.diagonal() ]))' input.txt
1 7 3 9 5
当然,这一切并不一定要靠一句话来完成。为了便于阅读,我们可以制作一个小脚本,它还允许我们处理在命令行上作为参数给出的所有文件名:
#!/usr/bin/env python
import sys
import numpy as np
for filename in sys.argv[1:]:
data=np.loadtxt(filename,delimiter=",")
diag = data.diagonal()
result_string = " ".join( [ str(int(i)) for i in diag ] )
print(result_string)
答案3
sed -ne '
y/,/\n/;G;s/\n$//
y/\n_/_\n/;:loop
/_$/{
s///
s/^[^_]*_//
bloop
}
y/\n_/_\n/;P
s/.*//;H
' input.file | paste -sd' '
我们在逗号分隔字段上维护一个分类帐,以便在保留空间的模式空间中跳过。
循环从两端切掉图案空间,以达到最左边的打印时机成熟的情况。我们可以想象它是从两端燃烧一根蜡烛(但是燃烧的速度不同)。从前面,我们砍掉一个以逗号分隔的字段,同时从末尾,我们删除一个尾随的\n
.燃烧会一直持续到不再有尾随换行符为止。
现在对角线元素位于图案空间的前面。
该工件y/\n_/_\n/
是为了解决以下事实:POSIX sed
在字符类 . 中没有换行符被否定[^\n]
。
作为当前行的最后一步,保持区域会增加。该paste
命令是在一行中获取输出。
假设您的 csv 中的所有字段都是数字,您也可以使用以下代码片段dc
。这tr
是删除逗号,因为 dc 字段以空格分隔,负数以 _ 开头,而不是 -
tr ',-' ' _' < file | dc -e '[q]sq [s0zlk<a]sa [?z0=qzlk1+dsk<an32ancz0=?]s? 0skl?x[]p'
我们定义了 3 个宏,q
用于完成后退出,a
用于循环删除末尾的元素(弹出),并?
设置一个循环来执行面向行的读取并调用宏a
,然后打印现在公开的诊断元素。
tr ... |
dc -e '
# quit macro
[q]sq
# macro to pop elements from stack till they are more than counter k
[s0 zlk<a]sa
# do-while loop for reading lines
[
?z0=q # quit if line read is empty
zlk1+dsk<a # increment the counter k and compare it against NF (z) call a if >
n 32an # print the diagonal element and a space (no newlines)
c z0=? # invoke myself again
]s?
# main
0sk # initialize the counter k
l?x # call the macro ? to start the file read-in loop
[]p # print a trailing newline
'
输出:
1 7 3 9 5
答案4
抽象的:
- 正方形.....:
awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("\n")}' file
矩形的:
awk -F, ' NR==1{printf($1);next} {printf(" "$(NR>NF?NF:NR))}END{printf("\n")} ' file`
其他对角线:
awk -F, -vdiag=9 -vdir=-1 ' {d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)} {printf("%s%s",NR==1?"":" ",$d)} END {printf("\n")} ' file
Posix 选择对角线数和方向
/
vs\
. (代码较长,请阅读文末)。
细节
方阵
使用 awk,最优雅的解决方案是:
$ awk -F, '{print $NR}' file
1
7
3
9
5
要获得一行输出,您可以执行以下操作(尾随空格):
$ awk -F, -v ORS=" " '{print $NR}' file; echo
1 7 3 9 5
如果您的输出必须不带尾随空格:
$ awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("\n")}' file
1 7 3 9 5
矩形的
例如,对于具有以下内容的文件:
$ cat file
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
6,7,8,9,0
1,2,3,4,5
a,b,c,d,e
f,g,h,i,j
k,l,m,n,o
p,q,r,s,t
u,v,w,x,y
上述解决方案将打印空格:
$ awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("|\n")}' file
1 7 3 9 5 |
如果在这种情况下您想要停止处理,那么检查记录数是否大于字段数可能是一个解决方案(如果每行的字段数发生变化,这可能不是正确的解决方案):
$ awk -F, 'NR>NF{exit}{printf(NR==1?$NR:" "$NR)}END{printf("|\n")}' infile
1 7 3 9 5|
如果您想要打印 NR > NF 的任何行中的最后一个字段:
$ awk -F, 'NR==1{printf($1);next}{printf(" "$(NR>NF?NF:NR))}END{printf("|\n")}' file
1 7 3 9 5 e j o t y|
其他对角线
如果需要的是与“主对角线”不同的对角线,我们可以通过将变量 diag 设置为不同于 0 的值来表示这一点(0 是此代码中的主对角线):
$ awk -F, -vdiag=3 ' {d=NR+diag;d=(d<1?1:d);d=(d>NF?NF:d)}
{printf("%s%s",NR==1?"":" ",$d)}
END {printf("\n")}
' file
4 0 5 0 5 e j o t y
请注意,diag 的值可能为负数:
$ awk -F, -vdiag=-3 ' {d=NR+diag;d=(d<1?1:d);d=(d>NF?NF:d)}
{printf("%s%s",NR==1?"":" ",$d)}
END {printf("\n")}
' infile
1 6 1 6 2 c i o t y
对角线可以像/
而不是\
更多的数学:
$ awk -F, -vdiag=4 -vdir=-1 '
{d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)}
{printf("%s%s",NR==1?"":" ",$d)}
END {printf("\n")}
' file
5 9 3 7 1 a f k p u
$ awk -F, -vdiag=9 -vdir=-1 '
{d=(NR-1)*(dir>0?1:-1)+1+diag;d=(d<1?1:d);d=(d>NF?NF:d)}
{printf("%s%s",NR==1?"":" ",$d)}
END {printf("\n")}
' infile
5 0 5 0 5 e i m q u
波斯克斯壳
使用不同的输入文件:
$ printf '%s\n' {1..6}{1..5} 7{1..3} | pr -ta -5 -s',' | tee inputfile
11,12,13,14,15
21,22,23,24,25
31,32,33,34,35
41,42,43,44,45
51,52,53,54,55
61,62,63,64,65
71,72,73
Posix 兼容 shell 中 awk 的等效项可能是:
diagPosix(){ diag=${1%%[!0-9+-]*} dir=$(((${2:-1}>0)?1:-1)) n=0 a=""
while read x ; do
# echo "testing $n $x"
IFS=',' eval 'set -- $x' # Place values in pos parms.
a=$(( diag + n*dir )) # calculate position a
b=$(( (a<0)?0:a )) # low limit is zero (0)
d=$(( (b>$#-1)?$#-1:b )) # upper limit is ($#-1)
# echo "a=$a b=$b d=$d #=$# n=$n"
shift $d # remove leading parms
printf '%s' "$s" "$1" # print parm (and an space)
s=" " # Next loop will have space.
n=$((n+1)) # In which line are we?
done <"${3:-inputfile}"
echo
}
diagPosix "$@"
有了上面的输入,它将按如下方式工作:
$ ./script 0 1 inputfile
11 22 33 44 55 65 73
$ ./script -2 1 inputfile
11 21 31 42 53 64 73
$ ./script 4 -1 inputfile
15 24 33 42 51 61 71
该代码在一些 shell 中进行了测试并且运行良好。
ash : 11 22 33 44 55 65 73
/usr/bin/yash : 11 22 33 44 55 65 73
y2sh : 11 22 33 44 55 65 73
dash : 11 22 33 44 55 65 73
zsh/sh : 11 22 33 44 55 65 73
b203sh : 11 22 33 44 55 65 73
b204sh : 11 22 33 44 55 65 73
b205sh : 11 22 33 44 55 65 73
b30sh : 11 22 33 44 55 65 73
b32sh : 11 22 33 44 55 65 73
b41sh : 11 22 33 44 55 65 73
b42sh : 11 22 33 44 55 65 73
b43sh : 11 22 33 44 55 65 73
b44sh : 11 22 33 44 55 65 73
lksh : 11 22 33 44 55 65 73
mksh : 11 22 33 44 55 65 73
ksh93 : 11 22 33 44 55 65 73
attsh : 11 22 33 44 55 65 73
zsh/ksh : 11 22 33 44 55 65 73
zsh 确实会失败(不在模拟中),因为 zsh 默认情况下不会拆分,而且数组编号从 1(而不是 0)开始。在csh和tcsh中测试过,但不起作用。
它是不是预计在那里工作(不要使用 csh 作为脚本!)。
自下而上工作的解决方案应该很容易在输入上使用 tac 来构建。