代码之家  ›  专栏  ›  技术社区  ›  Syed Ahmed Jamil

打印文件中没有重复的行并保留排序顺序linux

  •  2
  • Syed Ahmed Jamil  · 技术社区  · 2 年前

    我有以下文件:

    2
    1
    4
    3
    2
    1
    

    我想要这样的输出(唯一的行,没有任何重复和保留顺序):

    4
    3
    

    我试过了 sort file.txt | uniq -u 它有效,但输出是排序的:

    3
    4
    

    我试过了 awk '!x[$0]++' file.txt 它保持秩序,但只打印一次所有值:

    2
    1
    4
    3
    
    3 回复  |  直到 2 年前
        1
  •  3
  •   markp-fuso    2 年前

    有几个想法可供选择:

    读取输入文件两次:

    awk '
    FNR==NR         { counts[$1]++; next }  # 1st pass: keep count
    counts[$1] == 1                         # 2nd pass: print rows with count == 1
    ' file.txt file.txt
    

    b 读取输入文件一次(需要通过数组将所有行存储在内存中):

    awk '
        { lines[NR] = $1                    # maintain ordering of rows
          counts[$1] ++
        }
    END { for ( i=1;i<=NR;i++ )             # run thru the indices of the lines[] array and ...
              if ( counts[i] == 1 )         # if the associated count == 1 then ...
                 print lines[i]             # print the array entry to stdout
        }
    ' file.txt
    

    这两者都会产生:

    4
    3
    
        2
  •  2
  •   dawg    2 年前

    这里有一个Ruby可以做到这一点:

    ruby -lne 'BEGIN{cnt=Hash.new {|h,k| h[k] = 0} } 
    cnt[$_]+=1
    END{puts cnt.select{|k,v| v==1}.keys.join("\n") }
    ' file 
    

    打印:

    4
    3
    

    与awk不同,Ruby关联数组保持插入顺序。

    如果你想要一个通行证 awk 你可以做到:

    awk '
    BEGIN{OFS="\t"}
    {seen[$0]++; order[$0]=FNR}
    END{
        for (e in seen) {
            if (seen[e]>1) continue
            print order[e], e
        }
    }' file | sort -nk 1,1 | cut -f2-
    # same output
    
        3
  •  1
  •   amphetamachine    2 年前

    完全使用Bash内置程序,只需几行即可完成此操作:

    declare -A SEEN=()
    while IFS= read -r LINE; do
        (( ++SEEN[_$LINE] ))
    done < file.txt
    while IFS= read -r LINE; do
        if [[ ${SEEN[_$LINE]} -eq 1 ]]; then
            printf -- '%s\n' "$LINE"
        fi
    done < file.txt
    
        4
  •  0
  •   pmf    2 年前

    以下是一种仅使用 awk ,它只读取输入一次,但不会将整个文件存储在内存中:

    • fo 将行的第一次出现存储到数组中。如果线路尚未注册( !fo[$0] ),保存当前行号( fo[$0]=NR ).
    • fq 计数一行的频率,每读取一行,频率就会递增( fq[$0]++ ).
    • 此外,尚未增加的值 fq[$0] 用作条件(在上不满足 0 ,即唯一将增加到所需频率的值 1 ,并且在其他情况下由于超过频率而满足),以放弃第一次出现的相应寄存器( delete fo[$0] ).
    • 最后 法罗群岛 仅包含相关项目(行出现的次数不超过一次),行的内容作为索引,首次出现的行号作为值。因此,要完成此操作,只需要按数值的升序打印数组的索引。实现这一目标的一种方法是使用 asorti (可在GNU Awk 4+中获得),带有proc指令 @val_num_asc 按值升序进行数字排序。
    awk '
      !fo[$0]  {fo[$0]=NR}
      fq[$0]++ {delete fo[$0]}
      END      {asorti(fo,fo,"@val_num_asc"); for (i in fo) print fo[i]}
    '
    
    4
    3