搜索键值对并将值附加到 unix 中的其他键

Posted

技术标签:

【中文标题】搜索键值对并将值附加到 unix 中的其他键【英文标题】:search for a key value pair and append the value to other keys in 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 但我的密钥可以出现在任何位置 【参考方案1】:

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

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

awk '
BEGINFS=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  'BEGINFS=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| 字符中的值作为前缀

【讨论】:

以上是关于搜索键值对并将值附加到 unix 中的其他键的主要内容,如果未能解决你的问题,请参考以下文章

使用列表中的每个值作为键值对中的键

插入键值对后在搜索树中增加值

java:我想使用键值对,并且键或者值可以重复,在java中找得到对应的类吗?

将控件绑定到键值对的动态集合

java问题,我想在java中存储键值对,以便使用,但是键值对的键和值都有重复元素,使用hashmap会产生覆盖。

如何在JSON格式的android动态键值对中动态获取输入数据