搜索键值对并将值附加到 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
基本上,我需要将10
或10A
的值附加到每个键:值对并拆分为新行。需要明确的是,这并不总是第二个字段。
我是 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】:
我需要将
10
或10A
的值附加到每个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-separator 和 output-field-separator(FS
和 OFS
)设置为等于'|'
,这会将每条记录拆分为每个'|'
的字段。您的字段以$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 在答案中的位置为10
或10A
可以。它们只会按照出现在输入中的字段顺序出现在输出中——您还需要其他东西吗? (换句话说,我没有做任何特别的事情来把它们放在那个位置上)。您可以提供另一个变量(作为您的 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】:
编辑:要在行中的任意位置查找 10
或 10A
值,然后按照尝试进行打印。
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_no
和 ccard_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) = /(?<![^|])10A?:([^|]+)/
这将捕获10:
或10A:
之后的字符串并保存在$id
变量中。首先将捕获该行中的此类匹配。
s/([^|]+)[|\n]/$id|$1\n/g
每个字段都以 $id
和 |
字符中的值作为前缀
【讨论】:
以上是关于搜索键值对并将值附加到 unix 中的其他键的主要内容,如果未能解决你的问题,请参考以下文章
java:我想使用键值对,并且键或者值可以重复,在java中找得到对应的类吗?