抽象的:

抽象的:

如何输出文件的对角线?

例如,我得到了一个包含以下内容的文件。

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

抽象的:

  1. 正方形.....:awk -F, '{printf(NR==1?$NR:" "$NR)}END{printf("\n")}' file
  2. 矩形的:

    awk -F, ' NR==1{printf($1);next}
              {printf(" "$(NR>NF?NF:NR))}END{printf("\n")}
            ' file`
    
  3. 其他对角线:

    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
    
  4. 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 来构建。

相关内容