我正在寻找一种对列表进行排序并打印所有行的方法,其第一列仅出现一次 - 即仅在第一列上匹配。例如,我有一个文件,其中第一列是路径,第二列包含“类型”
/path/foo/1 footsy
/path/foo/1 barsy
/path/foo/X barsy
/path/bar/2 footsy
/path/bar/2 barsy
/path/foo/Y footsy
(文件实际排序为-k1,1)
现在,我只想提取类似的情况
/path/foo/X barsy
/path/foo/Y footsy
我正在考虑使用 awk 的某种方法,我必须存储前一行并将前一行的第一个字段与当前行中的相应字段进行比较。但我还不知道如何完成它:( 我试图适应另一个问题中找到的解决方案,但它并没有真正按希望工作
awk '{
prev=$0; path=$1; type=$2
getline
if ($1 != $path) {
print prev
}
}'
答案1
这些答案不需要对输入进行排序:
将计数和最后一行存储在数组中。大文件需要大量内存,并且需要 GNU awk
gawk '
{count[$1]++; line[$1]=$0}
END {
PROCINFO["sorted_in"]="@val_str_asc"
for (key in line) if (count[key] == 1) print line[key]
}
' file
扫描文件两次,首先获取计数,然后打印计数为 1 的行
awk 'NR == FNR {count[$1]++; next} count[$1]==1' file file
这将是最快的并且需要最少的内存,利用排序的输入:
awk '
prev_key && prev_key != $1 {if (count==1) print prev_line; count=0}
{prev_key=$1; prev_line=$0; count++}
END {if (count==1) print prev_line}
' file
答案2
awk
通常读取输入的每一行并调用其上的脚本。您将使用的情况getline
很少且相距甚远。当您的脚本使用六行输入运行时,以下是所发生情况的概述:正常读取第1行
设置变量
Callgetline
,读取第 2 行
比较变量正常读取第3行
设置变量
Callgetline
,读取第 4 行
比较变量正常读取第5行
设置变量
Callgetline
,读取第 6 行
比较变量显然这是行不通的。
其次,您在代码中犯了一个常见错误
awk
。在 中awk
,输入中的字段被引用为 ,变量被引用为$number
variable_name
。这与 shell 脚本不同,在 shell 脚本中,命令行参数被引用为 ,变量被引用为。您的测试$number
$variable_name
if ($1 != $path)
应该
if ($1 != path)
你的整体方法是有缺陷的。您无法通过一次查看两行来识别文件中仅出现一次的字符串。我相信您可以通过一次查看三行来做到这一点(即,通过保留二变量中的前几行),但是类似的事情变得复杂而混乱。计算出现次数可能更简单。为此,您需要对脚本进行最小的修改。
awk '{ if ($1 != path) { if (count == 1) { print prev } count=1 } else count++ prev=$0; path=$1 } END { if (count == 1) { print prev } }'
我删除了
type
,因为你从未使用过它。披露:这基本上与格伦答案的最后部分相同。
答案3
如果你的 shell 支持流程替代、X
andY
不包含空格、制表符:
$ grep -Ff <(awk '{print $1" "}' <file | LC_ALL=C uniq -u) <file
/path/foo/X barsy
/path/foo/Y footsy
答案4
你可以尝试用这个:
cat text.tx | sort | uniq -c -w11 | fgrep '1 /' | awk '{print $2" "$3}'
像这样的你的text.txt
]#cat text.txt
/path/foo/1 footsy
/path/foo/1 barsy
/path/foo/X barsy
/path/bar/2 footsy
/path/bar/2 barsy
/path/foo/Y footsy