如何跳过文件修改中的第一个、最后一个非空行和空行?

如何跳过文件修改中的第一个、最后一个非空行和空行?

我有一个类似这样的文件:

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的变量;有一个变量作为\nm="\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 

解释:

  1. tac- 反向连接并打印文件。tac是一个cat相反的。
  2. 1,/./!- 从第一行到第一行非空(包括)跳过行。
  3. $!- 除最后一行之外的所有行。请记住,我们翻转了文件,最后一行实际上是第一行。
  4. s/\(|\w*\)/\1\1/3- 复制第四列。我决定使用\w而不是[^|]为了美观。但如果字段中需要非单词字符,您可以更改它。
  5. s/|\w*//5- 删除以前的第五列(现在是第六列)。
  6. | tac- 将文件翻转回来。

相关内容