Bash 如何对特定列进行唯一排序,打印第一个唯一的3位数字并添加第二列值

Bash 如何对特定列进行唯一排序,打印第一个唯一的3位数字并添加第二列值,bash,awk,Bash,Awk,File.txt chocolate,Paris,ER milkchocolat,France,FR berriesnoire,Paris,FR chocolatewhite,Paris,FR darkchocolat,Italy,IL orange,usa,USA plume,Paris,FR milkshake,France,FR orangebark,usa,USA 期望输出: ber,Paris cho,Paris dar,Italy mil,France ora,usa plu,pa

File.txt

chocolate,Paris,ER
milkchocolat,France,FR
berriesnoire,Paris,FR
chocolatewhite,Paris,FR
darkchocolat,Italy,IL
orange,usa,USA
plume,Paris,FR
milkshake,France,FR
orangebark,usa,USA
期望输出:

ber,Paris
cho,Paris
dar,Italy
mil,France
ora,usa
plu,paris

cat file.txt | awk -F”,” ‘{print $1”,”$2}’ | cut -c-3 | sort -u
这个命令只给了我输出中的第1列

电流输出:

ber
cho
dar
mil
ora
plu
使用GNU sed和排序:

sed -E 's/(...)[^,]*/\1/; s/,[^,]*$//' File.txt | sort -u
sed 's/\(...\)[^,]*\(,[^,]*\).*/\1\2/' file | sort -u
输出:

ber,Paris cho,Paris dar,Italy mil,France ora,usa plu,Paris
请参阅:

对于每一行,将前三个字母与它们在数组中记录中出现的次数相关联,并使用它来消除重复项。因此,打印每个唯一记录的前三个字母和第二个字段

awk 'BEGIN{FS=OFS=","} !seen[key=substr($1,1,3)]++{print key,$2}' file | sort
给定示例输入,输出如下所示:

ber,Paris
cho,Paris
dar,Italy
mil,France
ora,usa
plu,Paris
或者,对于任何sed和sort:

sed -E 's/(...)[^,]*/\1/; s/,[^,]*$//' File.txt | sort -u
sed 's/\(...\)[^,]*\(,[^,]*\).*/\1\2/' file | sort -u
请注意,只有当记录的前三个字母是唯一的时,第一个字母才会列出该记录。但是,如果第一列的前三个字母和整个第二列都是唯一的,则第二列将列出一条记录

对于这样的输入:

chocolate,Paris
chocolate,paris
第一个输出将是:

cho,Paris
cho,Paris
cho,paris
但第二个输出将是:

cho,Paris
cho,Paris
cho,paris
由于您没有对此进行澄清,我为这两种情况都添加了解决方案,请使用符合您要求的解决方案。

我的解决方案如下:

awk -F, '{printf "%.3s,%s\n", $1, $2}' data.txt | sort -u
简单的awk排序解决方案:

$ time awk -F, -v OFS=, '{ print substr($1, 1, 3), $2 }' file | sort -u
ber,Paris
cho,Paris
dar,Italy
mil,France
ora,usa
plu,Paris

real    0m0.007s
user    0m0.003s
sys     0m0.006s
我认为在awk中使用-u部分可以更快地保存IO,但它要么等效,要么稍微慢一点:

$ time awk -F, -v OFS=, -v SUBSEP=, '
    BEGIN { split("", a) }
          { a[substr($1, 1, 3), $2] = "" }
    END   { for (i in a) print i }
' file | sort
ber,Paris
cho,Paris
dar,Italy
mil,France
ora,usa
plu,Paris

real    0m0.007s
user    0m0.006s
sys     0m0.004s
但是,在一个具有asorti实现的awk版本中抛弃管道并完成整个过程要快一些:

$ time gawk -F, -v OFS=, -v SUBSEP=, '
    BEGIN { split("", a) }
          { a[substr($1, 1, 3), $2] = "" }
    END   { N = asorti(a); for (i=1; i<=N; ++i) print a[i] }
' file
ber,Paris
cho,Paris
dar,Italy
mil,France
ora,usa
plu,Paris

real    0m0.006s
user    0m0.000s
sys     0m0.006s
所以,这取决于你。。。第一种解决方案是最简单的,也可以说是最灵活的,因为如果我们想要一个稍微不同的实现,比如按第二列排序或更改substr中的字符数,这是非常简单的,因为在管道中的两个工具之间有一个很好的关注点分离。然而,如果我们关心内存和速度,我们可能会考虑最后一种解决方案

注:

值得一提的是,我比较了mawk 1.3.3和gawk 4.2.1的前两个解决方案,gawk始终在这些情况下及时击败了mawk。 我还将管道输出计时到/dev/null。每个实现的结果彼此相似。
它不是打印第二列;只有作为我的awk命令排序的第一个sed-version 2>&1 | head-n1的输出,因为他使用sort-u,awk'BEGIN{FS=OFS=,}{print substr$1,1,3,$2}'File.txt | sort-u也是可能的。你是说小写的吗?你能从OP中引用一下吗?第二列是一个位置。所有这些都在OP的样本中大写。如果OP想要这种行为,他们可以通过tr。我认为你在细节上吹毛求疵。不管怎样。OP应该澄清,这不是你或我的错Cyrus的回答也不能解决这个问题。我看不出对这个答案有任何异议。您的代码示例使用了,请确保在实际代码中使用ANSI引号字符和。谢谢大家的帮助。