我有几行文本。我想使用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信息存在。