如何使用awk从两个文件中找出公共列及其记录
how to find out common columns and its records from two files using awk
我有两个文件:
文件 1:
id|name|address|country
1|abc|efg|xyz
2|asd|dfg|uio
文件 2(仅 headers):
id|name|country
现在,我想要这样的输出:
输出:
id|name|country
1|abc|xyz
2|asd|uio
基本上,我有一个用户记录文件(file1)
和一个header文件(file2)
。现在,我只想从(file1)
中提取那些列匹配的记录在 header 文件中。
我想使用 awk 或 bash。
我尝试使用:
awk 'BEGIN { OFS="..."} FNR==NR { a[(FNR"")] = [=16=]; next } { print a[(FNR"")], [=16=] > "test.txt"}' header.txt file.txt
不知道下一步该做什么。
谢谢
以下 awk
可能会对您有所帮助。
awk -F"|" 'FNR==NR{for(i=1;i<=NF;i++){a[$i]};next} FNR==1 && FNR!=NR{for(j=1;j<=NF;j++){if($j in a){b[++p]=j}}} {for(o=1;o<=p;o++){printf("%s%s",$b[o],o==p?ORS:OFS)}}' OFS="|" File2 File1
现在也添加了一种非线性形式的解决方案。
awk -F"|" '
FNR==NR{
for(i=1;i<=NF;i++){
a[$i]};
next}
FNR==1 && FNR!=NR{
for(j=1;j<=NF;j++){
if($j in a){ b[++p]=j }}
}
{
for(o=1;o<=p;o++){
printf("%s%s",$b[o],o==p?ORS:OFS)}
}
' OFS="|" File2 File1
Ed Morton 编辑:FWIW 这是用普通 indenting/spacing 和几个更有意义的变量名编写的相同脚本:
BEGIN { FS=OFS="|" }
NR==FNR {
for (i=1; i<=NF; i++) {
names[$i]
}
next
}
FNR==1 {
for (i=1; i<=NF; i++) {
if ($i in names) {
f[++numFlds] = i
}
}
}
{
for (i=1; i<=numFlds; i++) {
printf "%s%s", $(f[i]), (i<numFlds ? OFS : ORS)
}
}
使用 bash>4 的解决方案:
IFS='|' headers1=($(head -n1 $file1))
IFS='|' headers2=($(head -n1 $file2))
IFS=$'\n'
# find idxes we want to output, ie. mapping of headers1 to headers2
idx=()
for i in $(seq 0 $((${#headers2[@]}-1))); do
for j in $(seq 0 $((${#headers1[@]}-1))); do
if [ "${headers2[$i]}" == "${headers1[$j]}" ]; then
idx+=($j)
break
fi
done
done
# idx=(0 1 3) for example
# simple join output function from
join_by() { local IFS=""; shift; echo "$*"; }
# first line - output headers
join_by '|' "${headers2[@]}"
isfirst=true
while IFS='|' read -a vals; do
# ignore first (header line)
if $isfirst; then
isfirst=false
continue;
fi;
# filter from line only columns with idx indices
tmp=()
for i in ${idx[@]}; do
tmp+=("${vals[$i]}")
done
# join ouptut with '|'
join_by '|' "${tmp[@]}"
done < $file1
这类似于RavinderSingh13's solution,它首先从较短的文件中读取headers,然后根据headers决定从较长的文件中保留哪些列在它的第一行。
然而,它的输出不同。如果不想包含特定字段,它不会构造字符串,而是将列向左移动。
BEGIN { FS = OFS = "|" }
# read headers from first file
NR == FNR { for (i = 1; i <= NF; ++i) header[$i]; next }
# mark fields in second file as "selected" if the header corresponds
# to a header in the first file
FNR == 1 {
for (i = 1; i <= NF; ++i)
select[i] = ($i in header)
}
{
skip = 0
pos = 1
for (i = 1; i <= NF; ++i)
if (!select[i]) { # we don't want this field
++skip
$pos = $(pos + skip) # shift fields left
} else
++pos
NF -= skip # adjust number of fields
print
}
运行这个:
$ mawk -f script.awk file2 file1
id|name|country
1|abc|xyz
2|asd|uio
这个遵循 file1
中的列顺序,更改了顺序:
$ cat file1
id|country|name
awk:
$ awk '
BEGIN { FS=OFS="|" }
NR==1 { # file1
n=split([=11=],a)
next
}
NR==2 { # file2 header
for(i=1;i<=NF;i++)
b[$i]=i
}
{ # output part
for(i=1;i<=n;i++)
printf "%s%s", $b[a[i]], (i==n?ORS:OFS)
}' file1 file2
id|country|name
1|xyz|abc
2|uio|asd
(另一个版本使用cut
输出in revisions)
使用(很多)unix 管道作为 Doug McIlroy 的意图...
$ function p() { sed 1q "" | tr '|' '\n' | cat -n | sort -k2; }
$ cut -d'|' -f"$(join -j2 <(p header) <(p file) | sort -k2n | cut -d' ' -f3 | paste -sd,)" file
id|name|country
1|abc|xyz
2|asd|uio
我有两个文件:
文件 1:
id|name|address|country
1|abc|efg|xyz
2|asd|dfg|uio
文件 2(仅 headers):
id|name|country
现在,我想要这样的输出:
输出:
id|name|country
1|abc|xyz
2|asd|uio
基本上,我有一个用户记录文件(file1)
和一个header文件(file2)
。现在,我只想从(file1)
中提取那些列匹配的记录在 header 文件中。
我想使用 awk 或 bash。
我尝试使用:
awk 'BEGIN { OFS="..."} FNR==NR { a[(FNR"")] = [=16=]; next } { print a[(FNR"")], [=16=] > "test.txt"}' header.txt file.txt
不知道下一步该做什么。
谢谢
以下 awk
可能会对您有所帮助。
awk -F"|" 'FNR==NR{for(i=1;i<=NF;i++){a[$i]};next} FNR==1 && FNR!=NR{for(j=1;j<=NF;j++){if($j in a){b[++p]=j}}} {for(o=1;o<=p;o++){printf("%s%s",$b[o],o==p?ORS:OFS)}}' OFS="|" File2 File1
现在也添加了一种非线性形式的解决方案。
awk -F"|" '
FNR==NR{
for(i=1;i<=NF;i++){
a[$i]};
next}
FNR==1 && FNR!=NR{
for(j=1;j<=NF;j++){
if($j in a){ b[++p]=j }}
}
{
for(o=1;o<=p;o++){
printf("%s%s",$b[o],o==p?ORS:OFS)}
}
' OFS="|" File2 File1
Ed Morton 编辑:FWIW 这是用普通 indenting/spacing 和几个更有意义的变量名编写的相同脚本:
BEGIN { FS=OFS="|" }
NR==FNR {
for (i=1; i<=NF; i++) {
names[$i]
}
next
}
FNR==1 {
for (i=1; i<=NF; i++) {
if ($i in names) {
f[++numFlds] = i
}
}
}
{
for (i=1; i<=numFlds; i++) {
printf "%s%s", $(f[i]), (i<numFlds ? OFS : ORS)
}
}
使用 bash>4 的解决方案:
IFS='|' headers1=($(head -n1 $file1))
IFS='|' headers2=($(head -n1 $file2))
IFS=$'\n'
# find idxes we want to output, ie. mapping of headers1 to headers2
idx=()
for i in $(seq 0 $((${#headers2[@]}-1))); do
for j in $(seq 0 $((${#headers1[@]}-1))); do
if [ "${headers2[$i]}" == "${headers1[$j]}" ]; then
idx+=($j)
break
fi
done
done
# idx=(0 1 3) for example
# simple join output function from
join_by() { local IFS=""; shift; echo "$*"; }
# first line - output headers
join_by '|' "${headers2[@]}"
isfirst=true
while IFS='|' read -a vals; do
# ignore first (header line)
if $isfirst; then
isfirst=false
continue;
fi;
# filter from line only columns with idx indices
tmp=()
for i in ${idx[@]}; do
tmp+=("${vals[$i]}")
done
# join ouptut with '|'
join_by '|' "${tmp[@]}"
done < $file1
这类似于RavinderSingh13's solution,它首先从较短的文件中读取headers,然后根据headers决定从较长的文件中保留哪些列在它的第一行。
然而,它的输出不同。如果不想包含特定字段,它不会构造字符串,而是将列向左移动。
BEGIN { FS = OFS = "|" }
# read headers from first file
NR == FNR { for (i = 1; i <= NF; ++i) header[$i]; next }
# mark fields in second file as "selected" if the header corresponds
# to a header in the first file
FNR == 1 {
for (i = 1; i <= NF; ++i)
select[i] = ($i in header)
}
{
skip = 0
pos = 1
for (i = 1; i <= NF; ++i)
if (!select[i]) { # we don't want this field
++skip
$pos = $(pos + skip) # shift fields left
} else
++pos
NF -= skip # adjust number of fields
print
}
运行这个:
$ mawk -f script.awk file2 file1
id|name|country
1|abc|xyz
2|asd|uio
这个遵循 file1
中的列顺序,更改了顺序:
$ cat file1
id|country|name
awk:
$ awk '
BEGIN { FS=OFS="|" }
NR==1 { # file1
n=split([=11=],a)
next
}
NR==2 { # file2 header
for(i=1;i<=NF;i++)
b[$i]=i
}
{ # output part
for(i=1;i<=n;i++)
printf "%s%s", $b[a[i]], (i==n?ORS:OFS)
}' file1 file2
id|country|name
1|xyz|abc
2|uio|asd
(另一个版本使用cut
输出in revisions)
使用(很多)unix 管道作为 Doug McIlroy 的意图...
$ function p() { sed 1q "" | tr '|' '\n' | cat -n | sort -k2; }
$ cut -d'|' -f"$(join -j2 <(p header) <(p file) | sort -k2n | cut -d' ' -f3 | paste -sd,)" file
id|name|country
1|abc|xyz
2|asd|uio