我不知道如何命名这个标题,但这就是我所得到的:
包含如下文本的文件:
[10:03:43] 10:03:35 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:03:44] CW2B1 T CW2B4 T CW2B5 T CW2B7 T
[10:03:44] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:03:44] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:03:44] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:03:44] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:03:44] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
[10:04:16] 10:04:28 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:04:36] CW2B1 F CW2B3 F CW2B5 F CW2B7 F
[10:04:36] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:04:36] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:04:37] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:04:37] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:04:37] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
然后,实际文件中充满了 True 和 False 值发生变化的内容。
每当 a 有一个 True 条目,并且自上次条目以来它没有更改时,我想删除该条目,仅在该值更改为 False (或从 False 更改为 True)时保留该条目。
我的想法是我需要找到第一次出现的字符串(例如 CW1B1),然后将其后面的 T 或 F 值存储为变量。然后检查下一个出现的条目,并比较T/F值,如果相同,则使用cut或send删除该条目,然后再寻找下一个。如果它的值发生了变化,则忽略它并继续。
预期输出:
[10:03:43] 10:03:35 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:03:44] CW2B1 T CW2B4 T CW2B5 T CW2B7 T
[10:03:44] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:03:44] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:03:44] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:03:44] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:03:44] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
[10:04:16] 10:04:28 22JUN22 ConUP
[10:04:36] CW2B1 F CW2B3 F CW2B5 F CW2B7 F
[10:04:36]
[10:04:36]
[10:04:37]
[10:04:37]
[10:04:37]
也许有更好的方法来做到这一点,但我仍然需要框架方面的帮助,然后我可以去创建脚本中所需的所有可能的变量。
答案1
这可能就是您想要的(未经测试,因为没有提供可测试的示例输入/输出):
awk '
{
for ( i=2; i<=NF; i++ ) {
if ( $i ~ /^[TF]$/ ) {
tag = $(i-1)
val = $i
if ( map[tag] == val ) {
$(i-1) = $i = ""
}
map[tag] = val
i++
}
}
$0 = $0
$1 = $1
print
}
' file
答案2
使用乐(以前称为 Perl_6)
raku -e 'my @a = words; @a.splice(1, *-128); @a.splice(64,*-62); \
@a.=rotor(9); @a = @a>>.[1..*-1].flat.rotor(2); my @b; for 0..27 -> $i { \
@a[$i] eq @a[$i+28] ?? @b.push(@a[$i]~"+") !! @b.push(@a[$i+28]~"x") }; \
.say for @b.rotor(4)>>.join("\t|\t");'
输出:
CW1B1 T+ | CW1B3 T+ | CW1B5 T+ | CW1B7 T+
CW2B1 Fx | CW2B3 Fx | CW2B5 Fx | CW2B7 Fx
CW3B1 T+ | CW3B4 T+ | CW3B5 T+ | CW4B4 T+
CW4B8 T+ | CW5B4 T+ | CW5B8 T+ | CW6B4 T+
CW6B8 T+ | CW7B8 T+ | CW8B4 T+ | CW8B8 T+
CW9B4 T+ | CW9B8 T+ | CW10B4 T+ | CW10B8 T+
CW11B4 T+ | CW11B8 T+ | CW12B4 T+ | CW12B8 T+
这是使用 Raku 的一个想法:基本上,代码构建了一个可以在tail
日志文件上运行的仪表板。获取输入,将其分解为words
,并将其分配给@a
数组。用于从第 1 行和第 8 行中splice
删除额外的三个words
。现在,通过几次重新排列,您可以 1) 使用 重新创建 9 元素行rotor(9)
,然后 2) 删除第一列并将剩余 8 列配对为 2 元素行,例如("CW1B1", "T"), ("CW1B3", "T"), ("CW1B5", "T")...
。
从此时起,您知道有 28 个元素,编号为0..27
。使用迭代遍历这 28 个元素来搜索和eq
之间的字符串相等性。 Raku 的三元运算符,即“测试True False” ,如果为 True,则将第一个字符串推送到数组(添加 a表示两个值保持不变), 如果为 False,则将第二个字符串推送到数组(添加 a表示值已更改) 。然后只需将行(带有)重新排列回每行 4 对,然后打印。@a[$i]
@a[$i+28]
??
!!
@b
+
@b
x
rotor(4)
输入示例:
[10:03:43] 10:03:35 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:03:44] CW2B1 T CW2B4 T CW2B5 T CW2B7 T
[10:03:44] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:03:44] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:03:44] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:03:44] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:03:44] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
[10:04:16] 10:04:28 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:04:36] CW2B1 F CW2B3 F CW2B5 F CW2B7 F
[10:04:36] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:04:36] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:04:37] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:04:37] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:04:37] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
https://stackoverflow.com/questions/3416467/how-to-tail-f-the-latest-log-file-with-a-given-pattern
https://docs.raku.org/routine/splice
https://docs.raku.org/language/operators#index-entry-operator_ternary
https://raku.org
答案3
以下 perl 脚本将从每个输入行中删除重复的变量值。只有我们之前没有见过的变量或者自上次看到以来发生更改的变量才会包含在输出中。不打印空行(即没有新的或更改的变量的行)。
$ cat remove-dupes.pl
#!/usr/bin/perl
use strict;
# %vars is a hash (associative array) to store last-seen T/F values
# for the "variables". This is global in scope, i.e. defined here
# outside of the main loop because its keys & values need to be
# remembered across lines.
my %vars;
# read in and process each input line
while(<>) {
chomp; # remove newline character at end of line
# @out is an array to hold "VAR [TF]" values that we either
# haven't seen before or have changed since last time we saw
# them. i.e. the values we want to output. It needs to be
# reset to empty every time we read a line.
my @out = ();
# extract the first word (i.e. "ConUp") from input line
# by deleting all "VAR [TF]" strings from the line
my $first_word = $_;
$first_word =~ s/ ?\w+\s+[TF]//g;
# add it to @out if it isn't empty
push @out, $first_word if $first_word;
# Iterate over each "VAR [TF]" string in the line. Split
# it into the key (variable name) and value (T or F).
# If it's new or changed then add it to @out and
# remember its current value in %vars
while (/ ?\b(\w+ [TF])\b/g) {
my ($key,$val) = split / /, $1;
next if ($vars{$key} eq $val);
push @out, $1;
$vars{$key} = $val;
};
# join (with spaces) and print @out if it isn't empty.
if (@out) {print join(" ",@out), "\n"};
}
注意:如果任何行上有任何其他不是“VAR [TF]”的字符串,它们都将被移动到输出中该行的开头。这可能被认为是错误,也可能不被认为是错误。由于示例数据中唯一的此类实例是第一行开头的“ConUp”,因此它按要求工作。该变量可能不应该被调用$first_word
,但这比$anything_that_doesnt_match_a_boolean_variable_pattern
您的输入文件不包含任何应删除的重复变量值,因此我创建了一个包含该值的文件(并添加了一些会更改的变量实例):
$ cat file2.txt
ConUp CW1B1 T CW1B2 T CW1B3 F CW1B4 F
CW2B1 T CW2B2 F CW2B3 F CW2B4 T
CW2B1 F CW2B2 F CW2B3 F CW2B4 T
CW2B1 F CW2B2 F CW2B3 F CW2B4 T
CW2B1 T CW2B2 F CW2B3 F CW2B4 T
CW2B1 F CW2B2 F CW2B3 T CW2B4 T
示例输出:
$ ./remove-dupes.pl file2.txt
ConUp CW1B1 T CW1B2 T CW1B3 F CW1B4 F
CW2B1 T CW2B2 F CW2B3 F CW2B4 T
CW2B1 F
CW2B1 T
CW2B1 F CW2B3 T
更新后的版本:
此版本提取每行的前 34 个字符作为“第一个单词”,用于将sprintf()
的每个元素格式化@out
为 10 个字符宽的字符串,并从输出中去除尾随空格。它还使用\s*
和\s+
而不是单个空格来匹配(或分割)任何空白字符。为了简洁起见,注释已被删除,因为原始脚本中的注释仍然适用(更改很小)。
$ cat remove-dupes2.pl
#!/usr/bin/perl
use strict;
my %vars;
while(<>) {
chomp;
my @out = ();
my $first_word;
($first_word = $_) =~ s/\b\w+\s+[TF]//g;
$first_word =~ s/^(.{34})\s+$/$1 /;
push @out, $first_word if $first_word;
while (/\s*\b(\w+\s+[TF])\b/g) {
my ($key,$val) = split /\s+/, $1;
next if ($vars{$key} eq $val);
push @out, sprintf("%-10s",$1);
$vars{$key} = $val;
};
my $out = join(" ",@out);
$out =~ s/\s+$//; # remove trailing spaces
print "$out\n";
}
使用新输入数据的示例输出(input.txt
)。仍然不是确切地与示例输出相同,仍然存在一些空格差异,但输出比上面原始脚本的输出更接近。
$ ./remove-dupes2.pl input.txt
[10:03:43] 10:03:35 22JUN22 ConUP CW1B1 T CW1B3 T CW1B5 T CW1B7 T
[10:03:44] CW2B1 T CW2B4 T CW2B5 T CW2B7 T
[10:03:44] CW3B1 T CW3B4 T CW3B5 T CW4B4 T
[10:03:44] CW4B8 T CW5B4 T CW5B8 T CW6B4 T
[10:03:44] CW6B8 T CW7B8 T CW8B4 T CW8B8 T
[10:03:44] CW9B4 T CW9B8 T CW10B4 T CW10B8 T
[10:03:44] CW11B4 T CW11B8 T CW12B4 T CW12B8 T
[10:04:16] 10:04:28 22JUN22 ConUP
[10:04:36] CW2B1 F CW2B3 F CW2B5 F CW2B7 F
[10:04:36]
[10:04:36]
[10:04:37]
[10:04:37]
[10:04:37]
顺便说一句,如果您不想[HH:MM::SS]
打印几乎空的行(仅包含 ),请将print "$out\n";
行更改为print "$out\n" unless ($out =~ /^\[[^]]\]\s*$/);
。
用于diff
确认唯一的区别是空格(output.txt
是您的示例输出):
$ ./remove-dupes2.pl input.txt > out.txt
$ diff --ignore-space-change -u output.txt out.txt
$