代码之家  ›  专栏  ›  技术社区  ›  stack0114106

使用awk从多个文件中获取最后两行

awk
  •  0
  • stack0114106  · 技术社区  · 6 年前

    我可以使用 tail 命令式

    $ tail -n 2 file1.txt file2.txt
    ==> file1.txt <==
    3,2,1,1
    8,8,4,4
    
    ==> file2.txt <==
    B1 987 6545
    C1 876 5434
    $
    

    但我需要将上面的输出格式化如下

    3,2,1,1 file1.txt
    8,8,4,4 file1.txt
    B1 987 6545 file2.txt
    C1 876 5434 file2.txt
    

    只有用awk才能得到上面的输出吗?

    $ awk ' { if(FNR==1 && NR!=1)  print p,f; p=$0;f=FILENAME }  END { print p,f } ' file1.txt file2.txt
    8,8,4,4 file1.txt
    C1 876 5434 file2.txt
    $
    

    如何扩展到最后2行或n行?。

    0 回复  |  直到 6 年前
        1
  •  1
  •   kvantour    6 年前

    下面的答案代表 解决方案与任何awk一起工作。从中可以看出 Ed Morton's solution ,GNU awk可以让生活更轻松。


    最后两行: 两行中最简单的是:

    awk '(FNR==1) && f!="" { print t1,f; print t2,f }
         (FNR==1) { f=FILENAME }
         { t1=t2; t2=$0 }
         END { print t1, f; print t2, f}' file1 file2 file3 ...
    

    程序引入了变量 f t1 t2 表示文件的最后两行。每次我们输入一个新的非空文件 (FNR==1) 或者在程序结束时,我们使用这些变量进行打印。

    但是,这种方法有一个主要缺陷:

    最后 n 线: 如果你想把这个扩展到最后 n个 行,必须使用数组 t n个 行,你必须跟踪文件有多少行( fnr ). 后一个变量 fnr公司 FNR 在程序周期开始时的前一行。

    而且,如果你想使用交换原则,它会变得有点混乱

    awk -v n=2 '(FNR==1) && f!="" { for(i=1; i <= (fnr < n ? fnr : n); ++i) print t[i],f }
                (FNR==1) { f=FILENAME }
                { fnr = FNR }
                (fnr <= n) { t[fnr] = $0 }                
                (fnr >  n) { for(i=1; i < n; ++i) t[i] = t[i+1]; t[n]=$0 }
                END { for(i=1; i <= (fnr < n ? fnr : n); ++i) print t[i],f }
               ' file1 file2 file3 ...
    

    Ed Morton ):

    awk -v n=2 'function tail { 
                   for(i=1+(fnr < n ? n-fnr : 0); i<=n; ++i) print t[(fnr+i)%n],f  
                }
                (FNR==1) && f!="" { tail() }
                (FNR==1) { f=FILENAME }
                { fnr = FNR; t[FNR%n] = $0 }
                END { tail() }
               ' file1 file2 file3 ...
    

    在GNU中,awk只是:

    awk -v n=2 '{ t[FNR%n] = $0 }
                ENDFILE { 
                  for(i=1+(FNR < n ? n-FNR : 0); i<=n; ++i) print t[(FNR+i)%n],FILENAME                
                }' file1 file2 file3 ...
    
        2
  •  3
  •   Ed Morton    6 年前

    使用GNU awk作为ENDFILE:

    $ awk '{p2=p1; p1=$0} ENDFILE{print p2, FILENAME ORS p1, FILENAME }' file1 file2
    3,2,1,1 file1
    8,8,4,4 file1
    B1 987 6545 file2
    C1 876 5434 file2
    

    任何数量的 n 行数:

    $ awk -v n=2 '{p[NR%n]=$0} ENDFILE{for (i=1; i<=n; i++) print p[(NR+i)%n], FILENAME}' file1 file2
    3,2,1,1 file1
    8,8,4,4 file1
    B1 987 6545 file2
    C1 876 5434 file2
    

    delete p 在打印和/或执行任何您喜欢处理的文件后 n个

        3
  •  1
  •   B. Shefter    6 年前

    这不是最漂亮的解决方案,但它是 awk 按要求提供一条班轮:

    awk '{if (FNR==1 && NR!=1) {print secondLast" "prevFname ORS last" "prevFname} prevFname=FILENAME;last=$0} {secondLast=prevLine;prevLine=$0} END {print secondLast" "FILENAME ORS last" "FILENAME}' file1.txt file2.txt
    

    请注意,随着所需行数的增加,这将变得很难处理。