2013-03-16 93 views
9

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

文件内容为:

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,但我不能让它...可以有人帮助我吗?

+0

你可以在Perl或Python脚本中写几行。我知道你只说过sed,但它可能并不理想。我也可能是错的。 – squiguy 2013-03-16 21:51:32

+0

感谢您的建议。事情就是......长话短说。只有我的同事和我负责该机器上的一些工作。我们两个都知道sed,(不是专家级别),还有一点awk。我们不知道python或perl。我正在学习python。如果这个脚本是由python或perl编写的,万一有新的变更请求出现,我们必须再次来这里再询问。如果是sed。我们可以理解它是如何工作的,并在未来由我们自己改变它。 – Imagination 2013-03-16 21:57:13

回答

4

对于sed来说,这并不容易,尤其是一个班轮。不过你提到“gnu sed”。我看到了光明!

GNU sed的支持s/.../.../ge这是这种情况下有用:

kent$ sed -r '[email protected](^[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 '[email protected]@[email protected]' filege允许我们通过匹配部分到外部命令
  2. ..y..部分是由ge的魔术完成的。我通过\2到另一个sed(经由echo):sed "s# #\\n\1 #g"此SED替换原始文件与\n + \1 + space
  3. 所有空间,有\n每行(截止),所以有空行,在步骤2的结果(上述步骤),我们需要删除那些空行"/^$/d"
  4. 最后,可以完成步骤1中的替换(外部sed),并且我们得到结果。

检查info seds/../../ge

编辑,添加了双空格作为OP评论。

+0

不知道gnu sed的“ge”。有效!!!一个小问题是输出我需要“a,b,c ..”和域之间的双空格。但我可以通过在“\\ n \ 1”之后添加另一个空格来修复它。我的问题解决了。谢谢.. – Imagination 2013-03-16 22:40:50

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个文本

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

另一个尝试用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 
+0

只有当[az] +。[abc] +组的数量小于或等于9时,此方法才有效。 – ffledgling 2013-03-16 21:43:55

+0

谢谢Rostislav。在这里,我用本地笔记本电脑上的示例测试了您的命令。但是没有用真正的文件工作,在真实的文件中,每行中基本上可以有任意数量的域,我只是复制了几行代码作为例子,对不起,如果这些信息误导了你,再次感谢,我向你致谢。你做了一些更改,以便它可以处理任何数量的域? – Imagination 2013-03-16 21:47:32

+0

我更新了问题,并添加了一些更多的域名希望现在清楚 – Imagination 2013-03-16 21:51:46

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 

编辑:

它的工作原理通过使用两个替换。

第一把该需要平坦化作为保持字符的URL之前一个>

$ 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' 
+0

+1纯粹的努力;是7更新? :-) 好一个! – 2013-03-16 22:37:11

+1

@FredrikPihl谢谢,是的,当仅限于'sed'时,这是一个有趣的问题。用'bash'或'awk'轻松完成,但没有乐趣':P' – 2013-03-16 22:43:07

+0

您的解决方案也可以。我喜欢的是,单线,整齐!我不喜欢的是,用“#”部分替换。它使解决方案不是通用的。但它为我目前的问题做了工作。谢谢。 Upvote你的答案.. – Imagination 2013-03-16 22:57:35

-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应该最终会为您提供所需的格式。

我知道这可能不是最佳答案,如果可能的话我会尽量提炼。

+0

感谢您的努力。但是我们怎么能知道我应该运行这个sed线多少次?我没有降低你的答案,但它没有解决问题。 – Imagination 2013-03-16 22:18:21

+0

您必须对您认为会在线上出现的单词组的最大数量设置粗略的上限。我知道这在你的情况下可能是不可能的,但是我也不确定这个问题是否可以通过一次sed解决。我需要看看ω自动机(http://en.wikipedia.org/wiki/%CE%A9-automaton)和DFA(http://en.wikipedia.org/wiki/Deterministic_finite_automaton)以了解是否可能。我会相应地更新答案。 – ffledgling 2013-03-16 22:23:25

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 
+0

是的,我也是这样想,如果我找不到一个简短的sed行。谢谢。 up – Imagination 2013-03-16 22:41:51

0

这里是工作的真正的沉渣 - 仅脚本。我在下面将它写为一个在命令行中由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 

战略经济对话的模式缓冲区(您通常使用s/a/b/种工作命令)和一个保持缓冲区。在这个脚本中,信息来回交换到保持缓冲区,以保留另一部分工作时未经编辑的部分。

:start =标签以使跳跃

h =交换图案缓冲器(当前行)插入到保持缓冲器

s/\(.\ \ [^ ]*\).*/\1/ =尽管实线是在保持缓冲器安全,第一后剥离一切域,留下第一个期望的行(例如“a yao.com”)。

t continue =如果先前的命令导致了替换,跳转到“继续”标签

d =如果我们不跳了,这意味着我们就大功告成了。删除模式缓冲区并转到文件的下一行。

:continue =前一个跳跃

p =打印出来的图形缓冲存储器(例如,“一yao.com”)

x =交换与保持缓冲模式缓冲区标签(也可以使用g简单地将保留缓冲区复制到模式缓冲区中)

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

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

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

+0

顺便说一下,这是我在面对sed挑战时总是碰到的网站(这当然是一个挑战!):http://www.grymoire.com/Unix/Sed.html – 2013-03-16 23:59:35

1

这可能为你工作(GNU SED):

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

下面是做它一衬垫(用于“一”的一些定义)。它应该适用于任何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命令获取整个模式空间缓冲区,包括之前替换中添加的任何换行符。