查找特定列并用 gawk 用特定值替换以下列
Posted
技术标签:
【中文标题】查找特定列并用 gawk 用特定值替换以下列【英文标题】:Find specific columns and replace the following column with specific value with gawk 【发布时间】:2012-03-31 06:26:22 【问题描述】:我正在尝试查找我的数据具有重复行的所有位置并删除重复行。另外,我正在寻找第 2 列的值为 90 的位置,并将以下第 2 列替换为我指定的特定数字。
我的数据如下所示:
# Type Response Acc RT Offset
1 70 0 0 0.0000 57850
2 31 0 0 0.0000 59371
3 41 0 0 0.0000 60909
4 70 0 0 0.0000 61478
5 31 0 0 0.0000 62999
6 41 0 0 0.0000 64537
7 41 0 0 0.0000 64537
8 70 0 0 0.0000 65106
9 11 0 0 0.0000 66627
10 21 0 0 0.0000 68165
11 90 0 0 0.0000 68700
12 31 0 0 0.0000 70221
我希望我的数据看起来像:
# Type Response Acc RT Offset
1 70 0 0 0.0000 57850
2 31 0 0 0.0000 59371
3 41 0 0 0.0000 60909
4 70 0 0 0.0000 61478
5 31 0 0 0.0000 62999
6 41 0 0 0.0000 64537
8 70 0 0 0.0000 65106
9 11 0 0 0.0000 66627
10 21 0 0 0.0000 68165
11 90 0 0 0.0000 68700
12 5 0 0 0.0000 70221
我的代码:
BEGIN
priorline = "";
ERROROFFSET = 50;
ERRORVALUE[10] = 1;
ERRORVALUE[11] = 2;
ERRORVALUE[12] = 3;
ERRORVALUE[30] = 4;
ERRORVALUE[31] = 5;
ERRORVALUE[32] = 6;
ORS = "\n";
NR == 1
print;
getline;
priorline = $0;
NF == 6
brandnewline = $0
mytype = $2
$0 = priorline
priorField2 = $2;
if (mytype !~ priorField2)
print;
priorline = brandnewline;
if (priorField2 == "90")
mytype = ERRORVALUE[mytype];
END print brandnewline
##Here the parameters of the brandnewline is set to the current line and then the
##proirline is set to the line on which we just worked on and the brandnewline is
##set to be the next new line we are working on. (i.e line 1 = brandnewline, now
##we set priorline = brandnewline, thus priorline is line 1 and brandnewline takes
##on line 2) Next, the same parameters were set with column 2, mytype being the
##current column 2 value and priorField2 being the same value as mytype moves to
##the next column 2 value. Finally, we wrote an if statement where, if the value
##in column 2 of the current line !~ (does not equal) value of column two of the
##previous line, then the current line will be print otherwise it will just be
##skipped over. The second if statement recognizes the lines in which the value
##90 appeared and replaces the value in column 2 with a previously defined
##ERRORVALUE set for each specific type (type 10=1, 11=2,12=3, 30=4, 31=5, 32=6).
我已经能够成功删除重复的行,但是,我无法执行我的代码的下一部分,即替换我在 BEGIN 中指定的值作为 ERRORVALUES (10=1, 11=2, 12=3, 30=4, 31=5, 32=6) 以及包含该值的实际列。本质上,我只想用我的 ERRORVALUE 替换该行中的值。
如果有人能帮我解决这个问题,我将不胜感激。
【问题讨论】:
【参考方案1】:一个挑战是您不能只将一行与前一行进行比较,因为 ID 号会不同。
awk '
BEGIN
ERRORVALUE[10] = 1
# ... etc
# print the header
NR == 1 print; next
NR == 2 || $0 !~ prev_regex
prev_regex = sprintf("^\\s+\\w+\\s+%s\\s+%s\\s+%s\\s+%s\\s+%s",$2,$3,$4,$5,$6)
if (was90) $2 = ERRORVALUE[$2]
print
was90 = ($2 == 90)
'
对于第二列被更改的行,这会破坏行格式:
# Type Response Acc RT Offset
1 70 0 0 0.0000 57850
2 31 0 0 0.0000 59371
3 41 0 0 0.0000 60909
4 70 0 0 0.0000 61478
5 31 0 0 0.0000 62999
6 41 0 0 0.0000 64537
8 70 0 0 0.0000 65106
9 11 0 0 0.0000 66627
10 21 0 0 0.0000 68165
11 90 0 0 0.0000 68700
12 5 0 0 0.0000 70221
如果这是个问题,您可以将 gawk 的输出通过管道传输到 column -t
,或者如果您知道行格式是固定的,请在 awk 程序中使用 printf()。
【讨论】:
首先:非常感谢您的回答,这对您很有帮助。另外,感谢您如此迅速的回复。第二:我担心的一个问题是,是否有可能在我看到 2 美元中的 90 之后,我用两行之前的 2 美元代替什么?在此示例中,您在第 11 行中看到 $2 中的 90 是否可以将第 9 行中的 $2 更改为 BEGIN 中描述的格式,如果可以,我将如何执行此操作? 我可能会对您的文件进行两次传递:awk 'remove duplicate lines' | tac | awk 'replace $2 if value 2 lines before is 90' | tac
-- tac 是一个方便的实用程序,可以从最后一行打印文件到第一行。否则,awk 脚本会变得有点混乱,现在必须记住前 2 行,注意前 2 行没有被删除,等等。【参考方案2】:
这可能对你有用:
v=99999
sed ':a;$!N;s/^\(\s*\S*\s*\)\(.*\)\s*\n.*\2/\1\2/;ta;s/^\(\s*\S*\s*\) 90 /\1'"$(printf "%5d" $v)"' /;P;D' file
# Type Response Acc RT Offset
1 70 0 0 0.0000 57850
2 31 0 0 0.0000 59371
3 41 0 0 0.0000 60909
4 70 0 0 0.0000 61478
5 31 0 0 0.0000 62999
6 41 0 0 0.0000 64537
8 70 0 0 0.0000 65106
9 11 0 0 0.0000 66627
10 21 0 0 0.0000 68165
11 99999 0 0 0.0000 68700
12 31 0 0 0.0000 70221
【讨论】:
【参考方案3】:这可能对你有用:
awk 'BEGIN
ERROROFFSET = 50;
ERRORVALUE[10] = 1;
ERRORVALUE[11] = 2;
ERRORVALUE[12] = 3;
ERRORVALUE[30] = 4;
ERRORVALUE[31] = 5;
ERRORVALUE[32] = 6;
NR == 1 print ; next
if (a[$2 $6]) next else a[$2 $6]++
if ( $2 == 90) print ; n++ ; next
if (n>0) $2 = ERRORVALUE[$2] ; n=0
printf("% 4i% 8i% 3i% 5i% 9.4f% 6i\n", $1, $2, $3, $4, $5, $6)
' INPUTFILE
See it in action here at ideone.com.
IMO BEGIN
块很明显。然后发生以下情况:
NR == 1
行打印第一行(并切换到下一行,此规则也仅适用于第一行)
然后检查我们是否已经看到了具有相同第 2 列和第 6 列的任何行,如果是,则切换到下一行,否则将其标记为在数组中看到(使用连接的列值作为不定值,但请注意,如果您在第 2 个和第 6 个有较小的值(例如 2 0020
连接为 20020
和 20 020
相同),这可能会让您失败,因此您可能需要添加索引中的列分隔符,例如a[$2 "-" $6]
...,您可以使用更多列来更正确地检查)
如果该行在第二列有90
,则打印它,在下一行交换标志然后切换到下一行(在输入文件中)
在下一行检查ERRORVALUE
中的第二列,如果找到,则替换其内容。
然后打印格式化的行。
【讨论】:
【参考方案4】:我同意 Glenn 的观点,即两次遍历文件会更好。您可以使用这样的哈希删除重复的(可能是不连续的)行:
awk '!a[$2,$3,$4,$5,$6]++' file.txt
然后您应该根据需要编辑您的值。如果您希望将第二列中的值 90
更改为 5000
,请尝试以下操作:
awk 'NR == 1 print; next sub(/^90$/, "5000", $2); printf("%4i% 8i% 3i% 5i% 9.4f% 6i\n", $1, $2, $3, $4, $5, $6) ' file.txt
您可以看到我窃取了 Zsolt 的 printf 语句(感谢 Zsolt!)用于格式化,但您可以在必要时对其进行编辑。您还可以将第一个语句的输出通过管道传输到第二个语句中,以获得一个不错的单行:
cat file.txt | awk '!a[$2,$3,$4,$5,$6]++' | awk 'NR == 1 print; next sub(/^90$/, "5000", $2); printf("%4i% 8i% 3i% 5i% 9.4f% 6i\n", $1, $2, $3, $4, $5, $6) '
【讨论】:
【参考方案5】:以前的选项在大多数情况下都有效,但这是我的做法,简单而甜蜜。在查看其他帖子后,我相信这将是最有效的。此外,这还允许 OP 在 cmets 中添加的额外请求将 90 之后的行替换为之前 2 行的变量。这一步就完成了。
BEGIN
PC2=PC6=1337
replacement=5
if( $6 == PC6 ) next
if( PC2 == 90 ) $2 = replacement
replacement = PC2
PC2 = $2
PC6 = $6
printf "%4s%8s%3s%5s%9s%6s\n",$1, $2, $3, $4, $5, $6
示例输入
1 70 0 0 0.0000 57850
2 31 0 0 0.0000 59371
3 41 0 0 0.0000 60909
4 70 0 0 0.0000 61478
5 31 0 0 0.0000 62999
6 41 0 0 0.0000 64537
7 41 0 0 0.0000 64537
8 70 0 0 0.0000 65106
9 11 0 0 0.0000 66627
10 21 0 0 0.0000 68165
11 90 0 0 0.0000 68700
12 31 0 0 0.0000 70221
示例输出
1 70 0 0 0.000000 57850
2 31 0 0 0.000000 59371
3 41 0 0 0.000000 60909
4 70 0 0 0.000000 61478
5 31 0 0 0.000000 62999
6 41 0 0 0.000000 64537
8 70 0 0 0.000000 65106
9 11 0 0 0.000000 66627
10 21 0 0 0.000000 68165
11 90 0 0 0.000000 68700
12 21 0 0 0.000000 70221
【讨论】:
以上是关于查找特定列并用 gawk 用特定值替换以下列的主要内容,如果未能解决你的问题,请参考以下文章