使用sed保留行前缀,每行拆分多个字段以分隔行

时间:2013-03-16 21:28:29

标签: sed

上周五我遇到了一个问题,将文本转换为另一种格式。在那台机器上,只有gnu sed可用,没有awk(奇怪,我知道)。我对perl一无所知。所以我正在寻找一种唯一的解决方案。

文件内容为:

a  yao.com sina.com
b  kongu.com
c  polm.com unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com
d  kinge.net

所需的输出(应该是一个新文件)是:

a  yao.com 
a  sina.com
b  kongu.com
c  polm.com 
c  unee.net 
c  21cn.com 
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

我尝试了很多,也搜索了着名的sed oneliner,但我做不到...有人可以帮助我吗?

8 个答案:

答案 0 :(得分:6)

有趣的问题:

$ sed -r 's/(\w+\.\w+)/>  &/2g;:a s/^([a-z]+)(.*)>/\1\2\n\1/g;ta' file
a  yao.com 
a  sina.com
b  kongu.com
c  polm.com 
c  unee.net 
c  21cn.com 
c  iop.com 
c  foo.com 
c  bar.com 
c  baz.net 
c  happy2all.com
d  kinge.net

修改

它通过使用两个替换来工作。

第一个将>放在需要展平的网址之前作为保留字符:

$ sed -r 's/(\w+\.\w+)/>  &/2g' file
a  yao.com >  sina.com
b  kongu.com
c  polm.com >  unee.net >  21cn.com >  iop.com >  foo.com >  bar.com ...
d  kinge.net

第二个基本上用换行符替换持有> (使用条件分支)

$ sed -r ':a s/^([a-z]+)(.*)>/\1\2\n\1/g;ta'

答案 1 :(得分:4)

对于sed来说,这不是一件容易的事,特别是一个班轮。但是你提到了“gnu sed”。我看到了光!

gnu sed支持s/.../.../ge,这对这种情况很有用:

kent$  sed -r 's@(^[a-z]+) (.*)@echo "\2"\|sed "s# #\\n\1  #g"\|sed "/^$/d"@ge' file  
a  yao.com
a  sina.com
b  kongu.com
c  polm.com
c  unee.net
c  21cn.com
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

简短说明:

  1. 外部sed为sed -r 's@..x..@..y..@ge' file ge允许我们将匹配的部分传递给外部命令
  2. ..y..部分由ge的魔力完成。我将\2传递给另一个sed(通过echo):sed "s# #\\n\1 #g"此sed将所有空格替换为\n + \1 + space
  3. 在原始文件中,每行(结尾)都有\n,因此步骤2的结果中有空行(上面的步骤),我们需要删除那些空行"/^$/d"
  4. 最后,步骤1中的替换(外部sed)可以完成,我们得到结果。
  5. 检查info sed

    s/../../ge

    编辑,添加双空格作为OP注释。

答案 2 :(得分:1)

正如其他人所说,sed解决方案很棘手,所以我想我发布了一个bash-dito:

#!/bin/bash

while read -a array
do
    for i in ${array[@]:1}
    do
        echo ${array[0]} $i
    done
done < input

输出:

a yao.com
a sina.com
b kongu.com
c polm.com
c unee.net
c 21cn.com
c iop.com
c foo.com
c bar.com
c baz.net
c happy2all.com
d kinge.net

答案 3 :(得分:1)

这可能适合你(GNU sed):

sed -r 's/^((\S+\s+)\S+)\s+/\1\n\2/;P;D' file

答案 4 :(得分:1)

这是一个单行(对于某些定义&#34;一个&#34;)来做到这一点。它应该适用于任何sed,但我只用gnu sed测试它。

sed ':l;s/\(^\|\n\)\([^ \n]\)  \([^ \n][^ \n]*\) /\1\2  \3\ 
\2  /;t l'

这是\3\之后的字面换行符。

说明:

  1. 通过使用反斜杠转义,可以在替换中包含文字换行符。
  2. :l制作了一个名为l的标签。
  3. 如果进行了替换,t l会循环到标签l
  4. s命令对最初包含输入行的模式空间缓冲区进行操作。在s命令之后,模式空间缓冲区包含替换的结果,包括换行符。通过循环的第二次和后续时间,s命令获取整个模式空间缓冲区,包括在先前替换中添加的任何换行符。

答案 5 :(得分:0)

cat inputFile.txt | sed -e 's/\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)/\1 \3\n\1 \5\n\1 \7\n\1 \9/' | grep -vE "^..$"

适用于我的Ubuntu 12.10。

说明:

  • 将其分为2组:带文字的组和带空字符的组
  • 重复组1(带有第一个字符)和偶数组(带有文本)
  • 目前适用于以空字符分隔的4个文本

最后,删除包含空“second”组的行。

使用BASH的另一次尝试(执行为“script.sh inputFile.txt”):

#!/bin/bash

firstParams=`cat $1 | sed -e 's/\([^\ ]*\)\(.*\)/\1/'`
count=1
for MY1 in $firstParams
do
    # print line number ${count} and filter params from the second one forth
    restParams=`cat $1 | sed -n "${count}p" | sed -e 's/\([^\ ]*\)\(.*\)/\2/'`
    for MY2 in $restParams
    do
        echo "$MY1 $MY2"
    done
    count=$(($count+1))
done

答案 6 :(得分:0)

这是一个真正的sed-only脚本。我在下面将它写为sed在命令行上调用的文件,但它可以在命令行上输入,也可以全部输入到单独的脚本中:

将以下内容保存为sedscript(或任何您想要调用的内容)。输出后面有解释。

:start
    h
    s/\(.\ \ [^ ]*\).*/\1/
    t continue
    d
:continue
    p
    x
    s/\(.\ \)\ [^ ]*\(\ .*\)/\1\2/
    t start
    d

现在运行sed -f sedscript myfile.txt

上面的示例保存为myfile.txt,输出如下:

a  yao.com
a  sina.com
b  kongu.com
c  polm.com
c  unee.net
c  21cn.com
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

Sed有一个模式缓冲区(通常使用s/a/b/种命令)和保持缓冲区。在这个脚本中,信息被来回交换到保持缓冲区,以便在处理另一部分时保留一行的未编辑部分。

:start =启用跳转的标签

h =将模式缓冲区(当前行)交换到保持缓冲区

s/\(.\ \ [^ ]*\).*/\1/ =虽然整行在保留缓冲区中是安全的,但在第一个域之后删除所有内容,留下第一个所需的行(例如“a yao.com”)。

t continue =如果上一个命令导致替换,请跳转到“继续”标签

d =如果我们没有跳,那意味着我们已经完成了。删除模式缓冲区并继续执行文件的下一行。

:continue =上一次跳转的标签

p =打印出模式缓冲区(例如“a yao.com”)

x =将模式缓冲区与保持缓冲区交换(也可以使用g简单地在模式缓冲区上复制保持缓冲区)

s/\(.\ \)\ [^ ]*\(\ .*\)/\1\2/ =完整的原始字符串现在已经被交换到模式缓冲区 - 剥离我们刚刚处理的域名(例如“yao.com”)

t start =如果那不是最后一个域,请使用新的缩短字符串启动脚本。

d =如果那是最后一个域,请删除模式缓冲区并继续执行文件中的下一行。

答案 7 :(得分:-1)

您可以使用

sed -r -n 's/^([a-z])\ \ ([0-9a-z.]*)\ ([0-9a-z .]*)/\1  \2\n\1  \3/p'

它将转换表格的每一行

c  polm.com unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com

进入

c  polm.com
c  unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com

每次运行。

因此,下次在前一个sed的输出上运行时,这将变为

c  polm.com
c  unee.net
c  21cn.com iop.com foo.com bar.com baz.net happy2all.com

等等。

因此,将前一个sed的输出推送到新的sed应最终为您提供所需的格式。

我知道这可能不是最佳答案,如果可能,我会尝试对其进行改进。