awk
遇到以下情况该如何使用呢?
我想连接以同一列开头的行。连接后仅保留第一列(在本例中为aaa
, www
)hhh
。
该文件可以用空格或制表符分隔。
输入示例:
aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL
期望的输出:
aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL
其背景是我想建立一个非常简单的基于文件的数据库,其中第一列始终是实体的标识符。基于相同标识符列的所有行都被连接起来。
答案1
要使用 awk 获取每行的第一列,您可以执行以下操作:
< testfile awk '{print $1}'
aaa
aaa
aaa
www
hhh
hhh
这些是其余行的键。因此,您可以创建一个哈希表,使用该行的第一列作为键,使用该行的第二列作为值:
< testfile awk '{table[$1]=table[$1] $2;} END {for (key in table) print key " => " table[key];}'
www => yyy
aaa => bbbNULLbbb
hhh => 111111
要获取该行的其余部分(从第 2 列开始),您需要收集所有列:
< testfile awk '{line="";for (i = 2; i <= NF; i++) line = line $i " "; table[$1]=table[$1] line;} END {for (key in table) print key " => " table[key];}'
www => yyy hhh NULL NULL NULL NULL
aaa => bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
hhh => 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL
答案2
其他人可以用 awk 或 sed 回答,但 Python 版本很简单,可能对您有帮助。
#!/usr/bin/env python
input_file = 'input.dat'
in_fh = open(input_file, 'r')
input_order = []
seen = {}
for line in in_fh:
# Remove the newline character...
line = line[:-1]
# Separate the first column from the rest of the line...
key_col, sep, rest_of_line = line.partition(" ")
rest_of_line = sep + rest_of_line
# If we've seen this key already, concatenate the line...
if key_col in seen:
seen[key_col] += rest_of_line
# ...otherwise, record the ordering, and store the new info
else:
input_order.append(key_col)
seen[key_col] = rest_of_line
in_fh.close()
# Dump the ordered output to stdout
for unique_col in input_order:
print unique_col + seen[unique_col]
答案3
这是 coreutils 的一个有趣的应用程序,我怀疑它对于大输入不是很有效,因为它为输入中的每一行调用 join。
touch outfile
while read; do
join -a1 -a2 outfile <(echo $REPLY) > tmp
mv tmp outfile
done < infile
为了提高效率,保存outfile
到tmp
虚拟磁盘可能会有所帮助。
编辑
或者没有临时文件:
out=""
while read; do
out=$(join -a1 -a2 <(echo -n "$out") <(echo -n "$REPLY"))
done < infile
echo "$out"
答案4
假设OP通缉保留输入顺序所需输出,那么此处不能使用关联(字符串索引)数组。使用 迭代其键时,输出顺序是不可预测的for (var in array)
,就像 Perl 的哈希值或 Python 字典一样3.6之前。
这是一个注释良好的普通 AWK 解决方案,它保留了键的原始顺序,就像它们首次出现在输入文件中一样。删除重复项其余的列将是一个不同的挑战,但这似乎不是必需的。
这是在 macOS 上使用 BSD 版本的 AWK 进行测试的,并且应该用你拥有的一切工作awk
。如果需要,您可以将其作为独立的 shell 脚本运行*(没有人知道它是用 AWK 编写的),前提是您首先chmod +x concat-by-first-col
要将其标记为可执行程序。
#!/usr/bin/awk -f
## concat-by-first-col
## concatenate values from lines beginning with the same first column
##
## usage:
## $ chmod +x concat-by-first-col
## $ ./concat-by-first-col inputfile > outputfile
BEGIN {
# default output separator *is* a space, but if you wanted to change it…
OFS = " "
}
{
# assuming EVERY input record has AT LEAST the key…
# append to `keys` if we haven’t seen this key before
if (!($1 in values))
keys[length(keys)+1] = $1
# append second and subsequent columns to what we already have
for (i = 2; i <= NF; i++)
# insert an OFS *only* if there’s an existing value for this key
values[$1] = (values[$1] ? values[$1] OFS : "") $i
}
END {
# for all the keys, in the order they appeared in the input…
for (i = 1; i <= length(keys); i++) {
key = keys[i]
# a comma stands in for OFS in AWK’s `print` statement
print key, values[key]
}
}
作为独立脚本的替代方案,您可以使用开关将程序文本提供给 AWK -f
:
awk -f concat-by-first-col inputfile > outputfile
如果您还省略了该BEGIN
块,则可以OFS
在命令行上指定:
awk -v OFS='\t' -f concat-by-first-col inputfile > outputfile
它与所需的输出匹配吗?查看:
echo "aaa bbb ccc ddd NULL NULL NULL
aaa NULL NULL NULL NULL NULL NULL
aaa bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy
hhh 111 333 yyy ooo hyy NULL" > input
echo "aaa bbb ccc ddd NULL NULL NULL NULL NULL NULL NULL NULL NULL bbb ccc NULL NULL NULL NULL
www yyy hhh NULL NULL NULL NULL
hhh 111 333 yyy ooo hyy uuuioooy 111 333 yyy ooo hyy NULL" > expected
# uses Bash’s process substitution**; expect no output
diff expected <(./concat-by-first-col input)
*为了原因,如果您使用的是 Linux 和/或您的 AWK 版本不是/usr/bin/awk
,您可能需要调整系统上的“shebang”行。
**https://www.gnu.org/software/bash/manual/html_node/Process-Substitution.html