【问题标题】:search for a key value pair and append the value to other keys in unix搜索键值对并将值附加到 unix 中的其他键
【发布时间】:2021-08-26 03:11:32
【问题描述】:

我需要搜索一个键并将值附加到 Unix 文件中的每个键:值对

输入文件数据:

1A:trans_ref_id|10:account_no|20:cust_name|30:trans_amt|40:addr
1A:trans_ref_id|10A:ccard_no|20:cust_name|30:trans_amt|40:addr

我想要的输出:

account_no|1A:trans_ref_id
account_no|10:account_no
account_no|20:cust_name
account_no|30:trans_amt
account_no|40:addr
ccard_no|1A:trans_ref_id
ccard_no|10A:ccard_no
ccard_no|20:cust_name
ccard_no|30:trans_amt
ccard_no|40:addr

基本上,我需要将1010A 的值附加到每个键:值对并拆分为新行。需要明确的是,这并不总是第二个字段。

我是 sed、awk 和 perl 的新手。我开始使用 awk 提取值:

awk -v FS="|" -v key="59" '$2 == key {print $2}' target.txt

【问题讨论】:

  • awk 字段分隔符为 '|',然后在 ':' 上使用 split() 将有助于您入门,GNU Awk User's Guide 您会在 @ 下找到 split() 987654322@
  • 好吧,我对字符串函数很陌生。但正在尝试各种 perl 和 AWK 选项。
  • @nag,当然,请在您的问题中添加您尝试的代码,这在 SO 上受到强烈鼓励。你的努力没有对错,我们都在这里互相学习。
  • @RavinderSingh13,谢谢,我开始使用 awk 提取值:awk -v FS="|" -v key="59" '$2 == key {print $2}' target.txt 但我的密钥可以出现在任何位置

标签: perl awk sed


【解决方案1】:

我需要将 1010A 的值附加到每个 key:value

按照这些要求,你可以试试这个awk

awk '
BEGIN{FS=OFS="|"}
match($0, /\|10A?:[^|]+/) {
   s = substr($0, RSTART, RLENGTH)
   sub(/.*:/, "", s)
}
{
   for (i=1; i<=NF; ++i)
      print s, $i
}' file

account_no|1A:trans_ref_id
account_no|10:account_no
account_no|20:cust_name
account_no|30:trans_amt
account_no|40:addr
ccard_no|1A:trans_ref_id
ccard_no|10A:ccard_no
ccard_no|20:cust_name
ccard_no|30:trans_amt
ccard_no|40:addr

【讨论】:

  • 这是巧妙地使用match()sub() 来实现相同的目标!
【解决方案2】:
# Looks for 10 or 10A
perl -F'\|' -lane'my ($id) = map /^10A?:(.*)/s, @F; print "$id|$_" for @F'
# Looks for 10 or 10<non-digit><maybe more>
perl -F'\|' -lane'my ($id) = map /^10(?:\D[^:]*)?:(.*)/s, @F; print "$id|$_" for @F'
  • -n 为每一行输入执行程序。
  • -l 在读取时删除 LF 并在打印时添加。
  • -a|(由-F 指定)上的行拆分为@F
  • 第一条语句在 ID 为 10 或 10-plus-something 的字段中提取 : 后面的内容。
  • 第二条语句为每个字段打印一行。

Specifying file to process to Perl one-liner

【讨论】:

  • @Sundeep,谢谢,正要测试。显然,-F 采用正则表达式模式,因此对于 shell 和正则表达式引擎都需要对其进行转义。新版本的问题也已修复。
  • @nag, Re“10 或 10A 的键:值对的位置在记录中可能会有所不同。并不总是在 2 美元。”,我已经调整了我的答案相应地。
  • @Sundeep,根据 OP 提供的附加信息,它不起作用。
  • @Sundeep,OP 没有指定是否应该接受10B 等。我认为答案要么是肯定的,要么10B 永远不会发生。
  • @Sundeep,是的,你可以使用它。或/(?:^|\|)10A?:([^|]*)/。但在这一点上,我相信map 变得更清晰了:)
【解决方案3】:

如果您仍然不知道从哪里开始,您将使用 field-separatoroutput-field-separatorFSOFS)设置为等于'|',这会将每条记录拆分为每个'|' 的字段。您的字段以$1, $2, ... $NF 的形式提供。你关心得到,例如account_no 来自字段二 ($2),因此您 split() 字段二和分隔符 ':' 将拆分字段保存在数组中(a 在下面使用)。您希望将位于第二个数组元素 a[2] 中的字段 2 的第二部分用作输出中的新字段 1。

剩下的只是循环每个字段并输出a[2]一个分隔符,然后是当前字段。你可以这样做:

awk  'BEGIN{FS=OFS="|"} {split ($2,a,":"); for(i=1;i<=NF;i++) print a[2],$i}' file

使用/输出示例

使用file 中的示例输入,结果将是:

account_no|1A:trans_ref_id
account_no|10:account_no
account_no|20:cust_name
account_no|30:trans_amt
account_no|40:addr
ccard_no|1A:trans_ref_id
ccard_no|10A:ccard_no
ccard_no|20:cust_name
ccard_no|30:trans_amt
ccard_no|40:addr

这似乎是你所追求的。如果您还有其他问题,请告诉我。

未知领域的“10”或“10A”

您可以按任意顺序处理包含"10""10A" 的字段。您只需添加一个循环来遍历字段并确定哪个包含 "10""10A" 并保存来自该字段的 split() 生成的数组中的第二个元素。其余的都是一样的,例如

awk  '
    BEGIN { FS=OFS="|" } 
    {   for (i=1;i<=NF;i++){ 
            split ($i,a,":")
            if (a[1]=="10"||a[1]=="10A"){ 
                key=a[2]
                break
            }
        }
        for (i=1;i<=NF;i++)
            print key, $i
    }
' file1

示例输入

1A:trans_ref_id|10:account_no|20:cust_name|30:trans_amt|40:addr
1A:trans_ref_id|20:cust_name|30:trans_amt|10A:ccard_no|40:addr

使用/输出示例

awk  '
>     BEGIN { FS=OFS="|" }
>     {   for (i=1;i<=NF;i++){
>             split ($i,a,":")
>             if (a[1]=="10"||a[1]=="10A"){
>                 key=a[2]
>                 break
>             }
>         }
>         for (i=1;i<=NF;i++)
>             print key, $i
>     }
> ' file1
account_no|1A:trans_ref_id
account_no|10:account_no
account_no|20:cust_name
account_no|30:trans_amt
account_no|40:addr
ccard_no|1A:trans_ref_id
ccard_no|20:cust_name
ccard_no|30:trans_amt
ccard_no|10A:ccard_no
ccard_no|40:addr

从上面第二行包含"10A"的第4个字段中选择正确的新字段1作为输出。

让他们知道这是否是您需要的。

【讨论】:

  • 感谢您的解决方案。但是,记录中 10 或 10A 的键:值对的位置可能会有所不同。并不总是 2 美元。
  • 很好解释的答案
  • @nag 在答案中的位置为1010A 可以。它们只会按照出现在输入中的字段顺序出现在输出中——您还需要其他东西吗? (换句话说,我没有做任何特别的事情来把它们放在那个位置上)。您可以提供另一个变量(作为您的 key="match" 并找到包含该键的字段并将其用作您的新字段 - 完全取决于您。这需要一两个微小的添加。
  • 我的输入文件数据有时可能是:1A:trans_ref_id|10:account_no|20:cust_name|30:trans_amt|40:addr 1A:trans_ref_id|20:cust_name|30:trans_amt|10A: ccard_no|40:addr
  • @nag -- 谢谢你说清楚。我在原始答案的底部添加了基于该要求的另一个答案 - 这应该是您所需要的。
【解决方案4】:

编辑:要在行中的任意位置查找 1010A 值,然后按照尝试进行打印。

awk '
BEGIN{
  FS=OFS="|"
}
match($0,/(10|10A):[^|]*/){
  split(substr($0,RSTART,RLENGTH),arr,":")
}
{
  for(i=1;i<=NF;i++){
    print arr[2],$i
  }
}'  Input_file

说明:为上述添加详细说明。

awk '                        ##Starting awk program from here.
BEGIN{                       ##Starting BEGIN section of this program.
  FS=OFS="|"                 ##Setting FS and OFS to | here.
}
match($0,/(10|10A):[^|]*/){  ##using match function to match either 10: till | OR 10A: till | here.
  split(substr($0,RSTART,RLENGTH),arr,":") ##Splitting matched sub string into array arr with delmiter of : here.
}
{
  for(i=1;i<=NF;i++){        ##Running for loop for each field for each line.
    print arr[2],$i          ##Printing 2nd element of ar, along with current field.
  }
}'  Input_file               ##Mentioning Input_file name here.


使用您展示的示例,请尝试以下操作。

awk '
BEGIN{
  FS=OFS="|"
}
{
  split($2,arr,":")
  print arr[2],$1
  for(i=2;i<=NF;i++){
    print arr[2],$i
  }
}
' Input_file

【讨论】:

  • 嗯...发生了什么,例如account_no|10:account_noccard_no|10A:ccard_no 在所需的输出中 :)
  • @DavidC.Rankin,谢谢大卫先生,我错过了,如果我现在改变它,让它和你的一样,所以先打印第一列然后使用循环:)
  • 你的缩进格式很好——我不介意——改掉。毕竟伟大的思想是一样的......
  • 感谢您的解决方案。但是,记录中 10 或 10A 的键:值对的位置可能会有所不同。并不总是 2 美元。
  • 非常感谢先生,一切正常。
【解决方案5】:

Perl 脚本实现

use strict;
use warnings;
use feature 'say';

my $fname = shift || die "run as 'script.pl input_file key0 key1 ... key#'";

open my $fh, '<', $fname || die $!;

while( <$fh> ) {
    chomp;
    my %data = split(/[:\|]/, $_);
    for my $key (@ARGV) {
        if( $data{$key} ) {
            say "$data{$key}|$_" for split(/\|/,$_);
        }
    }
}

close $fh;

script.pl input_file 10 10A运行

输出

account_no|1A:trans_ref_id
account_no|10:account_no
account_no|20:cust_name
account_no|30:trans_amt
account_no|40:addr
ccard_no|1A:trans_ref_id
ccard_no|10A:ccard_no
ccard_no|20:cust_name
ccard_no|30:trans_amt
ccard_no|40:addr

【讨论】:

    【解决方案6】:

    这是另一个 perl 解决方案:

    perl -pe '($id) = /(?<![^|])10A?:([^|]+)/; s/([^|]+)[|\n]/$id|$1\n/g'
    
    • ($id) = /(?&lt;![^|])10A?:([^|]+)/ 这将捕获10:10A: 之后的字符串并保存在$id 变量中。首先将捕获该行中的此类匹配。
    • s/([^|]+)[|\n]/$id|$1\n/g 每个字段都以 $id| 字符中的值作为前缀

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-08
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多