将一个文件的列值替换为另一个文件中存在的值

问题描述 投票:0回答:2

考虑两个文件

文件1:

name|gender|phone|email|city|country 
abc|F|11111|ldiskmsjdh|pune|india

文件2:

sno|name|email|country 
1|abc|zzzzzzzz|USA

我需要使用unix所需的以下输出:

name|gender|phone|email|city|country
abc|F|11111|zzzzzzzz|pune|USA

注意:根据主键(即“名称”)进行匹配

我能够更换整条生产线。但我想在匹配时仅替换行的列,并仅更新第二个文件中存在的那些列。原始格式不应改变。

我正在寻找适用于 N 列的通用代码。关键位置保持不变。

linux bash shell unix ksh
2个回答
0
投票

类似这样的事情:

节目:

merge.awk

BEGIN {
    FS  = "|"
    OFS = "|"
    FILE1_KEY_COL    = 1
    FILE1_COL_NAME   = 1
    FILE1_COL_GENDER = 2
    FILE1_COL_PHONE  = 3
    FILE1_COL_CITY   = 5
    FILE2_KEY_COL     = 2
    FILE2_COL_EMAIL   = 3
    FILE2_COL_COUNTRY = 4
}
NR == FNR && FNR == 1 {
    # Ignore first line
    next
}
NR == FNR {
    file2_h_email[$FILE2_KEY_COL]   = $FILE2_COL_EMAIL
    file2_h_country[$FILE2_KEY_COL] = $FILE2_COL_COUNTRY
}
NR != FNR && FNR == 1 {
    print $0
    next
}
NR != FNR {
    print $FILE1_COL_NAME,$FILE1_COL_GENDER,$FILE1_COL_PHONE,file2_h_email[$FILE1_KEY_COL],$FILE1_COL_CITY,file2_h_country[$FILE1_KEY_COL]
}

这样执行:

awk -f merge.awk file2 file1

警告:

file2
(具有新值的引用)必须位于
file1

之前

0
投票

这是一个尴尬但通用的解决方案。

# patch_csv.awk

BEGIN {
  FS="|"
}

FNR==NR {
  if (FNR == 1) {
    nf1 = NF
    print
    for (i = 1; i <= NF; i++) {
      header1[i] = $i
      if ($i == "name") {
        key = i
      }
    }
  } else {
    data[$key] = $0
  }
}

FNR!=NR {
  if (FNR == 1) {
    for (j = 1; j <= NF; j++) {
      header2[j] = $j
      if ($j == "name") {
        key = j
      }
      for (i = 1; i <= nf1; i++) {
        if (header1[i] == header2[j]) {
          colmap[i] = j
        }
      }
    }
  } else if (data[$key]) {
    split(data[$key], record, FS)
    for (i = 1; i <= nf1; i++) {
      printf "%s%s", (colmap[i] ? $colmap[i] : record[i]), (i == nf1 ? "\n" : FS)
    }
  }
}

像这样运行:

awk -f patch_csv.awk file1 file2

输出:

name|gender|phone|email|city|country
abc|F|11111|zzzzzzzz|pune|USA

请注意,这是对 CSV 格式的非常原始的处理,因为它不支持引用或多行值。

当然,如果使用 Python 或 Ruby,这会简单得多。更不用说他们还正确处理完整的 CSV 规范。

但是如果您愿意对列号进行硬编码,那么这可能会更简单:

head -1 file1.csv && \
join -a 1 -t '|' -1 1 -2 2 \
  <(tail -n +2 file1.csv | sort -t '|' -k 1,1) \
  <(tail -n +2 file2.csv | sort -t '|' -k 2,2) | \
awk -v FS='|' \
  '{ print($1 FS $2 FS $3 FS ($8 ? $8 : $4) FS $5 FS ($9 ? $9 : $6)) }'

使用

head -1
打印标题。然后使用
join
显示第一个文件 (
-a 1
) 外连接到第二个文件的所有行,使用第一个文件的第一列 (
-1 1
) 和第二个文件的第二列 (
-2 2) 
)。对于
join
,文件必须排序;因此,首先撕下标题 (
tail -n +2
),然后对相关列上的每个文件进行排序 (
sort -k
)。
join
sort
都应使用
|
作为分隔符 (
-t '|'
)。
join
输出包含两个文件中的列,因此我们需要自己合并它们,并且我们可以使用
awk
来执行此操作,例如如果第 8 个字段为空,则打印第 4 个字段。请注意,
file1.csv
顺序不会保留。

编辑:如果你有 300 个字段......只需使用适当的编程语言哈哈

© www.soinside.com 2019 - 2024. All rights reserved.