我有两个文件:
文件1:
id|name|address|country
1|abc|efg|xyz
2|asd|dfg|uio
文件2(仅限标题):
id|name|country
现在,我想要一个输出:
输出:
id|name|country
1|abc|xyz
2|asd|uio
基本上,我有一个用户记录文件(file1)
和一个头文件(file2)
。现在,我想只从(file1)
中提取其列与头文件中的列匹配的记录
我想用awk或bash来做这件事。
我尝试使用:
awk 'BEGIN { OFS="..."} FNR==NR { a[(FNR"")] = $0; next } { print a[(FNR"")], $0 > "test.txt"}' header.txt file.txt
并且不知道下一步该做什么。
谢谢
答案 0 :(得分:2)
关注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在这里使用正常的缩进/间距和一些更有意义的变量名称编写相同的脚本:
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)
}
}
答案 1 :(得分:1)
与 Doug McIlroy 打算使用(很多)unix管道......
$ function p() { sed 1q "$1" | 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
答案 2 :(得分:0)
使用bash解决方案&gt; 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 https://stackoverflow.com/questions/1527049/join-elements-of-an-array
join_by() { local IFS="$1"; 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
答案 3 :(得分:0)
这类似于RavinderSingh13's solution,因为它首先从较短的文件中读取标题,然后根据第一行的标题决定保留较长文件的列。
然而,它输出的输出方式不同。如果不希望包含特定字段,则不会构造字符串,而是将列移到左侧。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
答案 4 :(得分:0)
这个尊重file1
中列的顺序,改变了顺序:
$ cat file1
id|country|name
awk:
$ awk '
BEGIN { FS=OFS="|" }
NR==1 { # file1
n=split($0,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
输出HackerRank Problem link)的另一个版本