我有一个类似这样的文件:
H|ACCT|XEC|1|TEMP|20130215035845 849002|48|1208004|100|||1 849007|28|1208004|100|||1 T|2|3
请注意,文件末尾有多余的空行。
我想在除第一个和最后一个非空行之外的所有行中将第 5 列的值替换为第 4 列的值。
我不能依赖字段的数量,因为最后一行可能具有与其他行一样多的字段,也不能依赖要修改的行始终以数字开头。
我尝试了下面的代码:
awk 'BEGIN{FS="|"; OFS="|"} {$5=$4; print}' in.txt
输出是:
H|ACCT|XEC|1|1|20130215035845
||||
849002|48|1208004|100|100||1
||||
849007|28|1208004|100|100||1
||||
T|2|3||
||||
||||
||||
预期输出:
H|ACCT|XEC|1|TEMP|20130215035845| 849002|48|1208004|100|100||1 849007|28|1208004|100|100||1 T|2|3
如何跳过第一行和最后一行非空行进行更改?我还想跳过空白行。
答案1
在这里,您只需awk
处理一次文件。
awk -F'|' 'NR==1{print;next} m && NF{print m}
NF{l="\n"$0; $5=$4; m="\n"$0; c=0}; !NF{c++}
END{ print l; for (; i++<c;)print }' OFS='|' infile
解释:
这里我们将第一行通过 skype 传输到将第 5个字段的值替换为第4 个字段的值,然后打印它并执行next
。
...如果它(当前下一行)不是空行(至少包含一个字段NF
),则备份整行并\n
添加 ewlinel="\n"$0
第一个下一个设置第 5 个字段的值和第 4 个字段的值$5=$4
,最后将其设置为添加了 ewlinem
的变量;有一个变量作为\n
m="\n"$0;
c
柜台!NF{c++}
标志,如果没有看到至少一个字段的行,则用于确定空行的数量;否则c=0
将重置该计数器。
现在我们已经修改了m
变量中的行,m && NF{print m}
并将在下一步awk
运行和m
设置的位置打印它,并且它不在空行上& NF
(这用于防止空行时重复打印)。
最后,我们打印未触及的最后一行,我们每次在执行替换之前都会备份该行END{ print l; ...
,然后打印从未见过带有循环字段的行的空行数for (; i++<c;)print }'
。
如果您不需要多余的空行,那么这会短得多。
awk -F'|' 'NR==1{print;next} m && NF{print m}
NF{l=$0; $5=$4; m=$0} END{ print l}' OFS='|' infile
答案2
对于sed
,依赖于第二行为空白:
sed '1{n;d;};/./!{H;$g;$p;d;};x;s/|/\n/4;s/\([^|]*\)\n[^|]*/\1|\1/'
如果您sed
不理解\n
替换的含义,请改用文字换行符(或使用已知不属于文件的字符)。
解释:
行(第一行除外)被收集在保留空间中,当到达文件末尾时,保留空间按原样打印,否则使用所需的替换。
详细地:
1{n;d;}
:对于第一行,n
不改变地打印它,读取下一行,只是将d
其删除。为什么?因为保留空间要包含要打印的内容,所以无论如何它都包含一个空行。/./!{H;$g;$p;d;}
仅对空行执行,将其自身附加到H
旧空间。仅对于最后一行,$
将保留空间移回并打印。无论如何,d
删除以停止进一步执行该行。x
与保持缓冲区交换非空行,因此它被保留在那里,而我们现在可以处理保存的行,因为我们知道它不是最后一个非空行。s/|/\n/4;s/\([^|]*\)\n[^|]*/\1|\1/
通过用换行符替换第四列|
来标记它来执行从第 4 列到第 5 列的复制,然后将匹配项前后的字段替换为之前字段的两倍。
答案3
正如我所说,最简单的方法是处理文件两次。
第一遍 - 获取行号。对于最后一个非空行。
第二遍 - 处理最后一个非空行之前至少有五个字段的所有行(标题除外):
awk -F'|' -vc=0 'NR==FNR{if (NF){c=NR};next};
FNR>1 && NF>4 && FNR<c {$5=$4};1' OFS='|' infile infile
答案4
我做出了假设,如果该行只有四列怎么办 - 应添加第五列和第四列的值。正确的?
第一个版本 - 使用 awk
awk '
BEGIN {
FS = "|";
OFS = "|";
}
FNR == NR && $0 {
last = NR;
}
FNR != NR {
if(NF > 3 && FNR != last && FNR != 1) {
$5 = $4;
}
print;
}' input.txt input.txt
相同的代码和注释:
awk '
BEGIN {
FS = "|";
OFS = "|";
}
# The first traversing through file
# It is needed for getting the number of the last, non-empty line
FNR == NR && $0 {
last = NR;
}
# The second traversing through file
FNR != NR {
# if the number of fields more than 3 (therefore, the fourth column exists)
# and the line number of the current file is not the last and not the first.
if(NF > 3 && FNR != last && FNR != 1) {
$5 = $4;
}
print;
}' input.txt input.txt
第二个版本 - 使用 sed 和 tac
tac input.txt |
sed '
1,/./!{
$!{
s/\(|\w*\)/\1\1/3
s/|\w*//5
}
}' | tac
解释:
tac
- 反向连接并打印文件。tac
是一个cat
相反的。1,/./!
- 从第一行到第一行非空(包括)跳过行。$!
- 除最后一行之外的所有行。请记住,我们翻转了文件,最后一行实际上是第一行。s/\(|\w*\)/\1\1/3
- 复制第四列。我决定使用\w
而不是[^|]
为了美观。但如果字段中需要非单词字符,您可以更改它。s/|\w*//5
- 删除以前的第五列(现在是第六列)。| tac
- 将文件翻转回来。