在模式之前插入 /textline - 不是紧接之前

在模式之前插入 /textline - 不是紧接之前

我正在尝试预处理发票文件并将每张发票分成单独的文件。发票可能是多页的。每页的标题如下所示:







                                                                 121084
                                                                   A134
                                                               09.17.19
                                                                      1

每页顶部有 6 个空行,后面是发票编号、客户编号、日期和页码(然后是发票的其余部分)。

在每页第 10 行(每页第 10 行是发票页号)的每个“1”(70 个空格后跟“1”)处,我需要在第 1 行插入一些文本(将用作分隔符)分割文件)。发票可以有多页,但“1”(70 个空格后跟“1”)表示这是一张新发票。

当遇到“ 1”(70个空格后跟“1”)时,我想在其上方9行的空白行(这是此发票的第一行)插入一些文本。对文件中的每个出现位置都执行此操作。然后我可以将每个发票的文件分成一个单独的文件。

我知道我可以使用 sed 在模式之前立即插入数据,但是如何在其上方 9 行插入数据?

我通常可以用 sed 和 awk 完成它,但这一个难倒了我。

答案1

假设你想说的是:

我有一个很长的文件,里面有几张发票。每张发票均以文本开头70 spaces and a 1。我需要在每张发票的内容之前插入新内容 9 行

然后你需要做的就是积累10行。当当前行与发票开始在第一行插入一些新文本。

在实践中,这可以实现:

sed -e '1{N;N;N;N;N;N;N;N}' -e 'N;l;/\n \{70\}1$/{iNew content here' -e 'P};P;D' file

或者,以长形式(注释不适用于某些 sed 实现):

sed '1{                              # (only) on the first line
         N;N;N;N;N;N;N;N             # accumulate 9 lines (first one plus 8 more).
      }
     N                               # On every line,also accumulate that line 
     /\n \{70\}1$/{                  # If buffer ends ($) in 70 spaces and a "1"
i\
New content added here               # insert the content of this line at
                                     # the start of the buffer (10 lines above).
                       P             # and then print it.
                  }
     P;D                             # close the N cycle above by
                                     # printing and deleting one line
    ' file                           # On the selected file.

解决awk方案可以利用将 RS 设置为标记发票开始的行。在这种情况下,将 FS 设置为换行符会将每一行分成一个字段,并且 $(NF-9) 将引用上面的第 9 行:

awk -v ln=9 '
              ( NF < ln ){ print; next };           # not enought fields?
                                                    # include
                         { $(NF-ln) = "New Text to include" newln $(NF-ln);
                           print                    # and print
                         };
              BEGIN{ breakln = sprintf("%71s",1);   # 70 spaces and a 1
                     newln   = sprintf("\n");       # a newline
                     RS      = breakln newln;       # set the Record Separator
                     FS      = "\n";                # set the Field separator
                     OFS     = FS;                  # print what got removed
                     ORS     = RS                   # print what got removed
                   }
            ' file

或者,(shell/awk 解决方案):

breakln="$(printf '%71s' 1)";
newl=$'\n';
awk '  (NF<ln){print;next};
              { $(NF-ln)="New Text to include\n"$(NF-ln); print}' \
    ln=10 \
    RS="$breakln$newl" \
    FS="$newl" \
    OFS="$newl" \
    ORS="$breakln$mewl" \
    file

答案2

awk+ tac

tac file | awk -v delim="--split page here--" '{
  if (nextnr=="" && $1~/^[0-9]+$/ && $0=="                                                                      "$1) {
     nextnr=NR+9  # pagenr found, remember next position
  }
  else if (NR==nextnr) {
     $0=delim     # overwrite line with delimiter
     nextnr=""    # reset
  }  
  print
}' | tac

首先使用 反转文件tac,这样我们就可以自上而下搜索页码并插入分隔符。

当满足这些条件时,搜索从 if 子句开始:

  • nextnr(最初)未设置 ( nextnr=="")。这是保存下一个分隔符的行号的变量。
  • 第一个字段是数字 ( $1~/^[0-9]+$/)
  • 该行包含 70 个空格和数字

如果三个条件全部成立,则nextnr设置为当前行号(记录数)NR + 9

如果当前行是分隔符 ( ) 的行NR==nextnr,则用分隔符覆盖该行并重置nextnr

脚本的最后一行打印当前行(原始行或被分隔符覆盖)。

在最后一步中,输出再次用 反转tac

答案3

这是一个可编写脚本的编辑器解决方案。其想法是仅根据文件中“(70 个空格)1”行的数量来找出文件中有多少张发票。然后该脚本会循环多次并输出 的命令ed。该循环输出足够的命令来更改“带有‘(70 个空格) 1’的行之前的第 9 行”,以改为虚线剪切线。将-----8<-----文本替换为除了裸露的前导句点(ed用于区分替换字符串的结尾)之外的任何内容。如果我们正在循环查看发票 ( i < count),请在进行更改后向前跳 10 行,这样我们就不会不会重新发现我们刚刚截取的页面。如果我们完成了循环 ( i == count),则打印出 ed 的“write and quit”命令,所有 printf/echo 输出都会发送到管道,该选项ed将作为输入读取。-s“静默”模式——ed不会报告读取或写入的字节数。

#!/usr/bin/bash
count=$(grep -c '^                                                                      1' input)
for((i=1; i<=count; i++))
do
  printf '%s\n' '/^                                                                      1/-9c'
  printf '%s\n' '-----8<-----' '.'
  [[ $i < $count ]] && printf '%s\n' '+10'
  [[ $i == $count ]] && echo wq
done | ed -s input

相关内容