是否有一个 Bash 函数允许我在文件具有相同的第一个单词时从文件中 separate/delete/isolate 行

Is there a Bash function that allow me to separate/delete/isolate line from a file when they have the same first word

我有一个这样的文本文件:

id ; lorem ipsum  fgdg df gdg
id ; lorem ipsum  fgdg df gdg
id ; lorem ipsum  fgdg df gdg
id ; lorem ipsum  fgdg df gdg
id ; lorem ipsum  fgdg df gdg

如果 2 个 ID 相似,我想将 2 个 ID 相似的行和唯一的行分开。

uniquefile 包含具有唯一 ID 的行。 notuniquefile 包含没有的行。

我已经找到了一种几乎可以做到的方法,但只能用第一个词。基本上它只是隔离 id 并删除其余行。

命令 1:隔离唯一 ID(但缺少该行):

awk -F ";" '{!seen[]++};END{for(i in seen) if(seen[i]==1)print i }' originfile >> uniquefile

命令 2:隔离不唯一的 id(但缺少该行并丢失可能因行而异的 "lorem ipsum" 内容):

awk -F ":" '{!seen[]++;!ligne[=14=]};END{for(i in seen) if(seen[i]>1)print i  }' originfile >> notuniquefile

所以在一个完美的世界里,我希望你能帮助我获得这样的结果:

originfile:

1 ; toto
2 ; toto
3 ; toto
3 ; titi
4 ; titi

uniquefile:

1 ; toto
2 ; toto
4 ; titi

notuniquefile:

3 ; toto
3 ; titi

祝你有美好的一天。

这是一个小的 Python 脚本,可以执行此操作:

#!/usr/bin/env python3

import sys

unique_markers = []
unique_lines = []
nonunique_markers = set()
for line in sys.stdin:
  marker = line.split(' ')[0]
  if marker in nonunique_markers:
    # found a line which is not unique
    print(line, end='', file=sys.stderr)
  elif marker in unique_markers:
    # found a double
    index = unique_markers.index(marker)
    print(unique_lines[index], end='', file=sys.stderr)
    print(line, end='', file=sys.stderr)
    del unique_markers[index]
    del unique_lines[index]
    nonunique_markers.add(marker)
  else:
    # marker not known yet
    unique_markers.append(marker)
    unique_lines.append(line)
for line in unique_lines:
  print(line, end='', file=sys.stdout)

这不是一个纯粹的 shell 解决方案(恕我直言,这会很麻烦且难以维护),但也许它对您有所帮助。

这样称呼它:

separate_uniq.py < original.txt > uniq.txt 2> nonuniq.txt

未测试:处理文件两次:第一次计算 ID,第二次决定打印记录的位置:

awk -F';' '
    NR == FNR      {count[]++; next}
    count[] == 1 {print > "uniquefile"}
    count[]  > 1 {print > "nonuniquefile"}
' file file

使用纯 bash 脚本,您可以这样做:

duplicate_file="duplicates.txt"
unique_file="unique.txt"
file="${unique_file}"
rm $duplicate_file $unique_file
last_id=""
cat testfile.txt | sort | ( 
    while IFS=";" read id line ; do
      echo $id
      if [[ "${last_id}" != "" ]] ; then
          if [[ "${last_id}" != "${id}" ]] ; then
             echo "${last_id};${last_line}" >> "${file}"
             file="${unique_file}"
          else
             file="${duplicate_file}"
             echo "${last_id};${last_line}" >> "${file}"
          fi
      fi
      last_line="${line}"
      last_id="${id}"
    done
    echo "${last_id};${last_line}" >> "${file}"
)

输入文件为:

1;line A
2;line B
2;line C
3;line D
3;line E
3;line F
4;line G

它输出:

$ cat duplicates.txt 
2;line B
2;line C
3;line D
3;line E
3;line F
work$ cat unique.txt 
1;line A
4;line G

另一种只有两个 unix 命令的方法,如果您的 id 字段始终具有相同的长度,则该方法有效(假设它们的长度与我的测试数据中的一样,但它当然也适用于更长的字段):

# feed the testfile.txt sorted to uniq
# -w means: only compare the first 1 character of each line
# -D means: output only duplicate lines (fully not just one per group)
sort testfile.txt | uniq -w 1 -D > duplicates.txt

# then filter out all duplicate lines from the text file
# to just let the unique files slip through
# -v means: negate the pattern
# -F means: use fixed strings instead of regex
# -f means: load the patterns from a file
grep -v -F -f duplicates.txt testfile.txt > unique.txt

并且输出是(对于与我的另一个 post 中使用的相同输入线):

$uniq -w 2 -D  testfile.txt 
2;line B
2;line C
3;line D
3;line E
3;line F

和:

$ grep -v -F -f duplicates.txt testfile.txt 
1;line A
4;line G

顺便说一句。如果你想避免 grep,你也可以存储排序的输出(假设在 sorted_file.txt 中)并将第二行替换为

uniq -w 1 -u sorted_file.txt > unique.txt

-w 后面的数字又是您的 ID 字段的字符长度。