如何使用awk删除特定字段中的某些单词?



我有几行文本。我想使用awk提取特定单词后的数字。

我试了下面的代码,但它不起作用。

首先,通过:vi test.text创建测试文件。有3列(这3个字段是由使用awk的其他管道命令生成的)。

Index  AllocTres                              CPUTotal
1      cpu=1,mem=256G                         18
2      cpu=2,mem=1024M                        16
3                                             4
4      cpu=12,gres/gpu=3                      12
5                                             8
6                                             9
7      cpu=13,gres/gpu=4,gres/gpu:ret6000=2   20
8      mem=12G,gres/gpu=3,gres/gpu:1080ti=1   21

请注意这个文件中有几个空字段。我想要实现的只是保持后面的数字第一个gres/gpu部分和删除所有cpu=mem=部分使用管道:cat test.text | awk '{some_commands}'输出3列:

Index  AllocTres                              CPUTotal
1                                             18
2                                             16
3                                             4
4      3                                      12
5                                             8
6                                             9
7      4                                      20
8      3                                      21

解决方案1: 对于所显示的示例,请尝试遵循GNUawk代码。这会处理字段之间的空格。

awk '
FNR==1{ print; next }
match($0,/[[:space:]]+/){
space=substr($0,RSTART,RLENGTH-1)
}
{
match($2,/gres/gpu=([0-9]+)/,arr)
match($0,/^[^[:space:]]+[[:space:]]+[^[:space:]]+([[:space:]]+)/,arr1)
space1=sprintf("%"length($2)-length(arr[1])"s",OFS)
if(NF>2){ sub(OFS,"",arr1[1]);$2=space arr[1] space1 arr1[1] }
}
1
'   Input_file

对于上面的示例代码,输出如下:

Index  AllocTres                              CPUTotal
1                                             18
2                                             16
3                                             4
4      3                                      12
5                                             8
6                                             9
7      4                                      20
8      3                                      21


解决方案2:如果您不关心空格,请尝试遵循awk代码。

awk 'FNR==1{print;next} match($2,/gres/gpu=([0-9]+)/,arr){$2=arr[1]} 1' Input_file

解释:添加以上代码的详细说明。

awk '             ##Starting awk program from here.
FNR==1{           ##Checking condition if this is first line then do following.
print           ##Printing current line.
next            ##next will skip all further statements from here.
}
match($2,/gres/gpu=([0-9]+)/,arr){  ##using match function to match regex gres/gpu= digits and keeping digits in capturing group.
$2=arr[1]       ##Assigning 1st value of array arr to 2nd field itself.
}
1                 ##printing current edited/non-edited line here.
' Input_file      ##Mentioning Input_file name here.

使用sed

$ sed 's~( +)[^,]*,(gres/gpu=([0-9])|[^ ]*)[^ ]* +~13 tttt      ~' input_file
Index  AllocTres                              CPUTotal
1                                             18
2                                             16
3                                             4
4      3                                      12
5                                             8
6                                             9
7      4                                      20
8      3                                      21
awk '
FNR>1 && NF==3 {
n = split($2, a, ",")
for (i=1; a[i] !~ /gres/gpu=[0-9]+,?/ && i<=n; ++i);
sub(/.*=/, "", a[i])
$2 = a[i]
}
NF==2 {$3=$2; $2=""}
{printf "%-7s%-11s%sn",$1,$2,$3}' test.txt

输出:

Index  AllocTres  CPUTotal
1                 18
2                 16
3                 4
4      3          12
5                 8
6                 9
7      4          20
8      3          21

你可以根据需要调整列的宽度。

这假设第一列和最后一列总是有一个值,因此NF(字段数)可以用来标识字段2。然后,如果字段2不为空,则用逗号分隔该字段,扫描结果数组以查找gres/gpu的第一个匹配,删除该后缀,并打印三个字段。如果字段2为空,则最后第二行插入空awk字段,因此printf始终有效。

如果上面的假设是错误的,也可以通过它的字符索引来识别字段2。

基于awk的解决方案,无需

- array        splitting, 
- regex back-referencing,
- prior   state tracking, or 
- input    multi-passing 
—- since m.p. for /dev/stdin would require state tracking

|

{mng}awk '!_~NF || sub("[^ ]+$", sprintf("%*s&", length-length($!(NF=NF)),_))' 
FS='[ ][^ \/]*gres[/]gpu[=]|[,: ][^= ]+[=][^,: ]+' OFS=
Index  AllocTres                              CPUTotal
1                                             18
2                                             16
3                                             4
4     3                                       12
5                                             8
6                                             9
7     4                                       20
8     3                                       21

如果您不关心nawk,那么它甚至是更简单的单遍方法,每行只有一次对sub()的全面调用:

awk ' sub("[^ ]*$", sprintf("%*s&", length($_) - length($(
gsub(" [^ /]*gres[/]gpu=|[,: ][^= ]+=[^,: ]+", _)*_)),_))'

或者更精简但更糟糕的语法样式:

awk 'sub("[^ ]*$",sprintf("%*s&",length^gsub(" [^ /]*gres/gpu=|"
"[,: ][^= ]+=[^,: ]+",_)^_ - length,_) )'

这可能适合您(GNU sed):

sed -E '/=/!b
s/S+/n&n/2;h
s/.*n(.*)n.*/1/
/gpu=/!{s/./ /g;G;s/(^.*)n(.*)n.*n/21/p;d}
s/gpu=([^,]*)/n1    n/;s/(.*)n(.*n)/21/;H
s/.*n//;s/./ /g;H;g
s/n.*n(.*)n(.*)n.*n(.*)/231/' file

本质上,上面的解决方案涉及使用保持空间(见这里和最终在这里)作为一个刮板来保存中间结果。这些结果是通过隔离第二个字段和gpu信息来收集的。一步步的故事如下:

如果该行不包含第二个字段,则不考虑

将第二个字段用换行符括起来并复制。

隔离第二个字段

如果第二个字段不包含gpu信息,则将整个字段替换为空格并使用副本,并相应地格式化行

否则,隔离gpu信息,将其移动到行前面,并将其附加到保持空间中的行副本。

同时,从模式空间中删除gpu信息,并将模式空间中的每个字符替换为一个空格。

将这些空格追加到副本,然后用副本覆盖模式空间。

最后,知道该行的每个部分都被换行分隔,将这些部分重新组装成所需的格式。

注意:解取决于列的间距是实空间。如果文件中有制表符,则在sed命令s/t/ /g前面加上(在示例中,制表符被8个空格替换)。

替代:

sed -E '/=/!b
s/S+/n&n/2;h
s/.*(n.*)n.*/1/;s/(.)(.*gpu=)([^,]+)/312/;H
s/.*n//;s/./ /g;G
s/(.*)n(.*)n.*n(.*)n(.*)n.*$/2413/' file

在这个解决方案中,而不是处理带有第二个字段但没有gpu信息的行,作为一个单独的情况,我为这个缺失的信息引入一个占位符,并遵循相同的解决方案,如果gpu信息存在。

最新更新