上周五我遇到了一个问题,将文本转换为另一种格式。在那台机器上,只有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,但我做不到...有人可以帮助我吗?
答案 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
简短说明:
sed -r 's@..x..@..y..@ge' file
ge
允许我们将匹配的部分传递给外部命令..y..
部分由ge
的魔力完成。我将\2
传递给另一个sed
(通过echo
):sed "s# #\\n\1 #g"
此sed将所有空格替换为\n + \1 + space
\n
,因此步骤2的结果中有空行(上面的步骤),我们需要删除那些空行"/^$/d"
检查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\
之后的字面换行符。
说明:
:l
制作了一个名为l
的标签。t l
会循环到标签l
。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。
说明:
最后,删除包含空“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应最终为您提供所需的格式。
我知道这可能不是最佳答案,如果可能,我会尝试对其进行改进。