我可以删除awk中的字段吗?

Can I delete a field in awk?

这是test.txt:

0x01,0xDF,0x93,0x65,0xF8
0x01,0xB0,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0xB2,0x00,0x76

如果我运行 awk -F, 'BEGIN{OFS=","}{="";print [=14=]}' test.txt 结果是:

0x01,,0x93,0x65,0xF8
0x01,,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,,0x00,0x76

$2 并没有被删除,它只是变空了。 我希望在打印 $0 时,结果是:

0x01,0x93,0x65,0xF8
0x01,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0x00,0x76

我认为最简单的方法是使用 sub 函数将第一次出现的连续 ,,(在您将第二个字段设为 NULL 后创建)替换为单个 ,。但这假定您在字段值之间没有任何逗号。

awk 'BEGIN{FS=OFS=","}{="";sub(/,,/,",");print [=10=]}' Input_file

第二个解决方案: 或者你可以使用 match 函数来捕获从第一个逗号到下一个逗号出现的正则表达式并得到匹配字符串行前后。

awk '
match([=11=],/,[^,]*,/){
  print substr([=11=],1,RSTART-1)","substr([=11=],RSTART+RLENGTH)
}' Input_file

有点笨手笨脚,但是这会将字段 2 之后的每个字段向下移动一个位置,然后更改 NF 因此不需要的字段不存在:

$ awk -F, -v OFS=, '{ for (i = 2; i < NF; i++) $i = $(i+1); NF--; print }' test.txt
0x01,0x93,0x65,0xF8
0x01,0x01,0x03,0x02,0x00,0x64,0x06,0x01
0x01,0x00,0x76
$

在 macOS Mojave 10.14.6 上使用 GNU Awk 4.1.3 和 BSD Awk(“awk version 20070501”进行测试——别问;这也让我很沮丧,但有时雇主不太擅长转发思维)。设置 NF 在旧版本的 Awk 上可能有效,也可能无效——我有点惊讶它 确实 有效,但惊喜是一个令人愉快的变化。

如果 Awk 不是绝对要求,并且输入确实像您的示例中一样微不足道,sed 可能是一个更简单的解决方案。

sed 's/,[^,]*//' test.txt

如果你想删除第二个字段,这是特别优雅的。一种更通用的删除方法,nth 字段将要求您输入一个匹配第一个 n - 1 后跟 nth,然后将其替换为第一个 n - 1.

所以对于 n = 4 你会有

sed 's/\([^,]*,[^,]*,[^,]*,\)[^,]*,//' test.txt

或者更一般地说,如果您的 sed 方言理解用于指定重复的大括号

sed 's/\(\([^,]*,\)\{3\}\)[^,]*,//' test.txt

一些 sed 方言允许您使用 -r-E 之类的选项去掉所有那些讨厌的反斜杠,但同样,这并没有得到普遍支持或便携。

如果不明显,[^,] 匹配一个不是(换行符或)逗号的字符;并且 </code> 调用第一个括号匹配的文本(反向引用;<code> 调用第二个,等等)。

此外,这完全不适合转义或引用字段(尽管我并不是说不能这样做)。无论如何,每个逗号都充当字段分隔符。

所有现有的解决方案都很好,尽管这实际上是为 cut 量身定制的工作:

cut -d, -f 1,3- file

0x01,0x93,0x65,0xF8
0x01,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0x00,0x76

如果要删除第 3 个字段,请使用:

cut -d, -f 1,2,4- file

要删除第 4 个字段,请使用:

cut -d, -f 1-3,5- file

使用 sub() 函数评论

awk 'BEGIN{FS=OFS=","}{="";sub(/,,/,",");print [=10=]}' Input_file

gnu-awk 手册:https://www.gnu.org/software/gawk/manual/html_node/Changing-Fields.html

It is important to note that making an assignment to an existing field changes the value of [=15=] but does not change the value of NF, even when you assign the empty string to a field." (4.4 Changing the Contents of a Field)

所以,按照 RavinderSingh13 的第一个解决方案,但没有使用,在这种情况下,sub()“该字段仍然存在;它只有一个空值,由两个冒号分隔”:

awk 'BEGIN {FS=OFS=","} {="";print [=11=]}' file 
0x01,,0x93,0x65,0xF8
0x01,,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,,0x00,0x76

我的解决方案:

awk -F, '
{
    regex = "^"","
    sub(regex, , [=10=]);
    print [=10=];
}'

或者一行代码: awk -F, '{regex="^"",";sub(regex, , [=11=]);print [=11=];}' test.txt

我发现 OFS="," 不是必需的

我会按照以下方式进行,让 file.txt 内容为:

0x01,0xDF,0x93,0x65,0xF8
0x01,0xB0,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0xB2,0x00,0x76

然后

awk 'BEGIN{FS=",";OFS=""}{for(i=2;i<=NF;i+=1){$i="," $i};="";print}' file.txt

产出

0x01,0x93,0x65,0xF8
0x01,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0x00,0x76

说明:我将 OFS 设置为空(空字符串),然后对于第二列和后续列,我在开始处添加 ,。最后,我将现在的逗号和值设置为空。请记住,如果您希望删除第一列,则此解决方案需要返工。

另一个解决方案:

您可以将输出通过管道传输到另一个 sed 并压缩分隔符。

$ awk -F, 'BEGIN{OFS=","}{=""}1 ' edward.txt  | sed 's/,,/,/g'
0x01,0x93,0x65,0xF8
0x01,0x01,0x03,0x02,0x00,0x64,0x06,0x01,0xB0
0x01,0x00,0x76
$

使用 GNU sed,您可以添加一个数字修饰符来替换第 n 个匹配的非逗号字符后跟逗号:

sed -E 's/[^,]*,//2' file

以无正则表达式的方式使用 awk,并可选择要删除的行:

awk '{ col = 2; n = split([=10=],arr,","); line = ""; for (i = 1; i <= n; i++) line = line ( i == col ? "" : ( line == "" ? "" : ","  ) arr[i] ); print line }' test.txt

一步一步:

{
col = 2    # defines which column will be deleted
n = split([=11=],arr,",")    # each line is split into an array
                         # n is the number of elements in the array

line = ""     # this will be the new line

for (i = 1; i <= n; i++)   # roaming through all elements in the array
    line = line ( i == col ? "" : ( line == "" ? "" : "," ) arr[i] )
    # appends a comma (except if line is still empty)
    # and the current array element to the line (except when on the selected column)

print line    # prints line
}