通过shell脚本在第n次出现模式后在第n个字符处插入一个新行

时间:2016-04-13 15:25:28

标签: unix awk sed sh

我有一个单行大字符串,其中包含'〜|〜'作为分隔符。 10个字段组成一行,第10个字段长9个字符。我想在每一行之后插入一个新行,这意味着在(9,18,27 ..)出现'〜|〜'

之后插入一个updated_at 10个字符

是否有快速单行sed / awk选项可用而不通过字符串循环?

我用过

$user->touch()

但它将用新行替换每第10次出现。我想保留分隔符,但在字段10中的9个字符后添加一个新行

\n

以下是我想要的内容

sed -e's/\(\([^~|~]*~|~\)\{9\}[^~|~]*\)~|~/\1\n/g'

2 个答案:

答案 0 :(得分:1)

让我们试试awk:

awk 'BEGIN{FS="[~|~]+"; OFS="~|~"}
     {for(i=10; i<NF; i+=9){
          str=$i
          $i=substr(str, 1, 9)"\n"substr(str, 10, length(str))
     }
     print $0}' t.txt 

输入:

one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten1234562one~|~2‌​two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten1234563one~|~‌​3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten123456

输出:

one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten123456
2one~|~2‌​two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten12345
63one~|~‌​3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten123456

我认为您的评论中有一些错误:如果您的输入包含ten1234562one2ten1234563one,则必须在第一种情况下2之后和{{{}之后插入换行符1}}在第二种情况下(因为这是第十个字符)。但是你的预期输出与此不同。

答案 1 :(得分:0)

你的sed脚本并不太远。这似乎可以完成你想要的工作:

sed -e '/^$/d' \
    -e 's/\([^~|]*~|~\)\{9\}.\{9\}/&\' \
    -e '/' \
    -e 'P;D' \
    data

对于您的输入文件(我称之为data),我得到:

one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten123456
2one~|~2two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten12345
63one~|~3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten12345
6

我担心脚本需要一点解释。它使用了一些模糊的shell和一些模糊的sed行为。晦涩的shell行为是在单引号字符串中,反斜杠没有特殊含义,因此第二个-e中第二个单引号之前的反斜杠在sed处显示为结尾处的反斜杠论点。晦涩的sed行为是它将每个-e选项的参数视为一条线。因此,在第三个/之后的尾随反斜杠加-e被视为有反斜杠,换行符,斜杠序列,这就是BSD sed(和POSIX {{1} })要求您添加换行符。 GNU sed将替换中的sed视为换行符,但POSIX(和BSD)说:

  

转义序列“\n”应匹配模式空间中嵌入的\n

<newline>替换的替换部分中,\n没有说明<newline>被视为s///。因此,前两个-e选项组合在匹配后添加换行符。匹配的是什么?好吧,这是一系列'零或多个非波形,非管道字符后跟~|~',重复9次,然后是9'任何字符'。这是你想要的近似值。如果你有~|~tilde~pipe|bother~|~这样的字段,那么正则表达式会失败,因为'tilde'和'pipe'之间的~以及'pipe'之间的|和'打扰' ”。修复它来处理所有可能的序列是非常重要的,并且样本数据无法保证。

脚本的其余部分是直截了当的:-e '/^$/d'删除一个空行,如果数据的长度恰好合适则重要,在-e 'P;D' P打印模式空间的初始段直到第一个换行符(我们刚刚添加的换行符); D删除模式空间的初始段直到第一个换行符并重新开始。

我不相信这是值得的复杂性。如果脚本位于文件script.sed

中,则可能更容易理解
/^$/d
s/\([^~|]*~|~\)\{9\}.\{9\}/&\
/
P
D

,命令行是:

$ sed -f script.sed data
one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten123456
2one~|~2two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten12345
63one~|~3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten12345
6
$

毋庸置疑,它会产生相同的输出。如果没有/^$/d,脚本只能在输入结尾处使用奇数6。在第三条记录之后恰好有9个字符,然后它会在无限循环中翻转。

使用扩展正则表达式

如果您使用扩展正则表达式,则可以处理中间包含~|(或实际上~|)的奇数球。

script2.sed

/^$/d
s/(([^~|]{1,}|~[^|]|~\|[^~])*~\|~){9}.{9}/&\
/
P
D

data2

one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten1234562one~|~2two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten1234563one~|~3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten12345666=beast~tilde|pipe~|twiddle~|~4-two~|~4-three~|~4-four~|~4-five~|~4-six~|~4-seven~|~4-eighty-eight~|~4-999~|~987654321

sed -E -f script.sed data2的输出:

one~|~two~|~three~|~four~|~five~|~six~|~seven~|~eight~|~nine~|~ten123456
2one~|~2two~|~2three~|~2four~|~2five~|~2six~|~2seven~|~2eight~|~2nine~|~2ten12345
63one~|~3two~|~3three~|~3four~|~3five~|~3six~|~3seven~|~3eight~|~3nine~|~3ten12345
666=beast~tilde|pipe~|twiddle~|~4-two~|~4-three~|~4-four~|~4-five~|~4-six~|~4-seven~|~4-eighty-eight~|~4-999~|~987654321

仍然无法处理像tilde~~|~这样的字段。使用-E对于BSD(Mac OS X)sed是正确的。它可以扩展正则表达式。 GNU sed的等效选项是-r