[英]How to remove double quotes in a specific column by using sub() in AWK
[英]How to remove some words in specific field using 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}'
cat test.text | awk '{some_commands}'
到 output 3 列:
Index AllocTres CPUTotal
1 18
2 16
3 4
4 3 12
5 8
6 9
7 4 20
8 3 21
第一个解决方案:使用您显示的示例,请尝试遵循 GNU awk
代码。 这会处理字段之间的空间。
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
Output 对于上面的示例代码如下:
Index AllocTres CPUTotal
1 18
2 16
3 4
4 3 12
5 8
6 9
7 4 20
8 3 21
第二种解决方案:如果您不关心空格,请尝试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]\)\|[^ ]*\)[^ ]* \+~\1\3 \t\t\t\t ~' input_file
Index AllocTres CPUTotal
1 18
2 16
3 4
4 3 12
5 8
6 9
7 4 20
8 3 21
这可能对你有用(GNU sed):
sed -E '/=/!b
s/\S+/\n&\n/2;h
s/.*\n(.*)\n.*/\1/
/gpu=/!{s/./ /g;G;s/(^.*)\n(.*)\n.*\n/\2\1/p;d}
s/gpu=([^,]*)/\n\1 \n/;s/(.*)\n(.*\n)/\2\1/;H
s/.*\n//;s/./ /g;H;g
s/\n.*\n(.*)\n(.*)\n.*\n(.*)/\2\3\1/' file
本质上,上述解决方案涉及使用保留空间(参见此处和最终此处)作为暂存器来保存中间结果。 这些结果是通过隔离第二个字段然后再次隔离 gpu 信息来收集的。 一步一步的故事如下:
如果该行不包含第二个字段,则保留。
用换行符包围第二个字段并制作副本。
隔离第二个字段
如果第二个字段不包含 gpu 信息,请用空格替换整个字段并使用副本,相应地格式化该行。
否则,隔离 gpu 信息,将其移至行首,将 append 移至保留空间中行的副本。
同时,从模式空间中删除 gpu 信息,并将模式空间中的每个字符替换为一个空格。
将这些空间附加到副本,然后用副本覆盖模式空间。
最后,知道行的每个部分都被换行符分割,将这些部分重新组合成所需的格式。
注意解决方案取决于列的间距是真实空间。 如果文件中有制表符,则在前面添加 sed 命令s/\t/ /g
(示例中制表符被 8 个空格替换)。
选择:
sed -E '/=/!b
s/\S+/\n&\n/2;h
s/.*(\n.*)\n.*/\1/;s/(.)(.*gpu=)([^,]+)/\3\1\2/;H
s/.*\n//;s/./ /g;G
s/(.*)\n(.*)\n.*\n(.*)\n(.*)\n.*$/\2\4\1\3/' file
在这个解决方案中,我没有将带有第二个字段但没有 gpu 信息的行作为一个单独的案例,而是为这个缺失的信息引入了一个占位符,并遵循相同的解决方案,就好像 gpu 信息存在一样。
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%s\n",$1,$2,$3}' test.txt
Output:
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()
进行 1 次全面调用:
awk ' sub("[^ ]*$", sprintf("%*s&", length($_) - length($(\
gsub(" [^ /]*gres[/]gpu=|[,: ][^= ]+=[^,: ]+", _)*_)),_))'
甚至更简洁但更糟糕的语法样式:
awk 'sub("[^ ]*$",sprintf("%*s&",length^gsub(" [^ /]*gres\/gpu=|"\
"[,: ][^= ]+=[^,: ]+",_)^_ - length,_) )'
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.