如果在辅助列表中找到匹配项,则替换主列表中的字段

如果在辅助列表中找到匹配项,则替换主列表中的字段

我有一个包含大约几千行的列表,每行包含 4 或 5 个字段。我还有第二个列表,包含多行,只有 1 个字段。两个列表都将存储在变量中。

第一个列表:

item_1 something something value something
item_2 something something value 
item_3 something something value something
item_4 something something value something
... 
item_2155 something something value 
item_2156 something something value something

第二个清单:

item_3
item_2155

期望的最终结果:

item_1 something something value something
item_2 something something value 
item_3 something something new_value something
item_4 something something value something
...
item_2155 something something new_value 
item_2156 something something value something

我尝试在 while 循环中使用 sed 。它有点有效,但此方法每次循环时都会将列表附加到自身。我也觉得 awk 可能是更好的解决方案。

#!/bin/bash

MYHUGELIST=$(command)
MYSHORTLIST=$(command)

while read -r line ; do
  sed "/^$line /s/1of3-possible-matches/newvalue/;/^$line /s/2of3-possible-matches/newvalue/;/^$line /s/3of3-possible-matches/newvalue/" <<< "$MYHUGELIST"
done <<< "$MYSHORTLIST"

答案1

awk您会考虑使用而不使用的解决方案sed吗?如果是,

#!/bin/bash

read -r -d '' shortlistOneString < shortlist.txt

awk -v oldv=value -v newv=new_value -v s="$shortlistOneString" \
'BEGIN {n=split(s,a,"\n")} { \
found=0; \
for (i=1; ! found && i<=n; ++i) { \
  if (a[i] == $1) { \
    for (j=2; j<= NF; ++j) { \
      if ($j == oldv) { \
        $j = newv; found=1; break }}}}; \
print}' longlist.txt

笔记

  • 我们将整个shortlist.txt、换行符和所有内容读入 shell 变量shortlistOneString
  • BEGIN块中,我们将 的值拆分"$shortlistOneString"为一个称为 的数组a;这个数组有n元素,我们可以访问th 元素与a[i].该块仅awk在输入正确之前执行一次。
  • 就其本身而言,awk将每一行解析为一个特殊的保留数组;对于任何输入行,该数组都有NF元素,我们可以访问j第一个元素带有$j;甚至可以覆盖这些元素。
  • 对于每一行,awk将执行第二个块中的语句(其第一个语句是found=0,最后一个语句是print)。
  • 如果j第一个字段等于oldv,我们用 覆盖该字段newv,然后停止查找。因为awk没有breakBash(例如)那样的多级,所以我们使用一个名为 的辅助变量来凑合found,我们0为每行重置该变量。
  • 无论我们是否覆盖字段,我们都会print一行一行。
  • 该解决方案比您所要求的更通用。您可以通过仅检查每行的来收紧它最后一个字段 $(NF)倒数第二个字段 $(NF-1);您甚至可以分别将字段位置硬编码为$5$4

答案2

您可以使用 sed 发出 awk 脚本

cmd1 | awk "$(cmd2 | sed -e 's:.*:/^& /{\$4=\"new_value\"}:')1"

在哪里

  • cmd1产生$MYHUGELIST
  • cmd2产生$MYSHORTLIST
  • 中的每一行都$MYSHORTLIST变成/^item_N /{$4="new_value"}
  • awk 脚本中的结尾1会导致打印当前行
  • $并且"根据需要对字符进行转义

或者,仅限 sed,

cmd1 | sed -e "$(cmd2 | sed -e 's:.*:/^& /s/[^ ][^ ]*/new_value/4:')"

其中命令4中的标志s选择第四个字段。

答案3

您所需要的只是对 awk 的一次简单调用,无需循环、管道或其他命令。

如果您的数据位于文件中:

$ awk 'NR==FNR{a[$1]; next} $1 in a{$4="new_value"} 1' secondFile firstFile
item_1 something something value something
item_2 something something value
item_3 something something new_value something
item_4 something something value something
...
item_2155 something something new_value
item_2156 something something value something

或者如果您的数据位于变量中:

$ awk -v sec="$second" '
    BEGIN{split(sec,tmp); for (i in tmp) a[tmp[i]]}
    $1 in a{$4="new_value"} 1
' <<<"$first"
item_1 something something value something
item_2 something something value
item_3 something something new_value something
item_4 something something value something
...
item_2155 something something new_value
item_2156 something something value something

相关内容