如何使用 sed 或 awk 将管道替换为空格,但只能在字母单词之间



我一直在为脚本而苦苦挣扎。 我有一份体操队和分数的名单。 我已经设法将文件管道分隔开来,就像下面的小数据示例一样。 但是,现在我需要用空格替换管道,但前提是它在字母单词之间。 数字之间的管道必须保留。 这样,我可以将团队名称作为一个字段,无论组成多少个单词。 然后,数据将准备好加载到我的数据库中。 我知道sedawk使用正则表达式应该能够做到这一点,但我还没有弄清楚。 我一整天都在搞砸这个,我相信有人可能会在 2 分钟内告诉我如何做到这一点。:-)

起始格式:

Twistars|28.250|28.700|28.100|27.950|113.600
Excel|Gymnastics|28.250|28.700|28.100|27.950|113.600
Head|Over|Heels|Gymnastics|28.250|28.700|28.100|27.950|113.600

我的最终目标:

Twistars|28.250|28.700|28.100|27.950|113.600
Excel Gymnastics|28.250|28.700|28.100|27.950|113.600
Head Over Heels Gymnastics|28.250|28.700|28.100|27.950|113.600

任何建议/小例子将不胜感激。

这是一个awk的解决方案:

awk -F| '{printf $1;for (i=2;i<=NF;i++) printf (($(i-1)!~/[0-9.]+/ && $(i)!~/[0-9.]+/)?" ":"|")"%s",$i;print ""}' file
Twistars|28.250|28.700|28.100|27.950|113.600
Excel Gymnastics|28.250|28.700|28.100|27.950|113.600
Head Over Heels Gymnastics|28.250|28.700|28.100|27.950|113.600

这可能对你有用(GNU sed):

sed -r ':a;s/([[:alpha:]])|([[:alpha:]])/1 2/g;ta' file

这会用空格替换单词之间的|。第二次扫荡会抓住第一次传球时错过的任何比赛。

with sed

sed 's/([[:alpha:]])|([[:alpha:]])/1 2/g'  file
Twistars|28.250|28.700|28.100|27.950|113.600
Excel Gymnastics|28.250|28.700|28.100|27.950|113.600
Head Over Heels Gymnastics|28.250|28.700|28.100|27.950|113.600

假设最后 5 个字段不应连接:

awk '{p=$0; for(i=1;i<=NF-6;i++) sub(FS,OFS,p); print p}' FS='\|' file
echo 'Excel|Gymnastics|28.250|28.700|28.100|27.950|113.600' | sed 's/([A-Za-z])|([A-Za-z])/1 2/g'

以上输出

Excel Gymnastics|28.250|28.700|28.100|27.950|113.600

上面显示了您需要的基本sed表达式。您可以通过几种不同的方式进行修饰。

cat file | sed 's/([A-Za-z])|([A-Za-z])/1 2/g' > newFile
sed 's/([A-Za-z])|([A-Za-z])/1 2/g' file > newfile

您甚至可以对某些版本的sed使用"就地编辑"标志 - 这意味着您将在同一位置将文件写回原处:

sed -i 's/([A-Za-z])|([A-Za-z])/1 2/g' file

解释:

([A-Za-z])  capturing group: any character A-Z or a-z
|            escaped pipe symbol (otherwise it means "or")
([A-Za-z])  second capturing group: any character A-Z or a-z

我们将上述内容替换为

1 2         first captured group, space, second captured group
/g            global flag: do it for all occurrences
sed -e 's/([[:alpha:]])|([[:alpha:]])/1 2/g'

效果很好,除了单个字母会导致问题的事实:

$ echo 'Tete|A|Tete|4.489' | sed -e 's/([[:alpha:]])|([[:alpha:]])/1 2/g'
Tete A|Tete|4.489

这使得awk可能是一个更好的解决方案,尽管它更冗长。

由于您已经有一个文件,因此可以使用 ex:

printf '%%s/\([[:alpha:]]\)|\([[:alpha:]]\)/\1 \2/gn%%&gn%%pn' | ex -s file >file2

假设您使用的是支持 here-Documents 的 shell,您可以使其更具可读性,如下所示:

ex -s file >file2 <<EOF
%s/([[:alpha:]])|([[:alpha:]])/1 2/g
%&g
%p
EOF
它只是

执行与sed相同的替换,只是它允许您使用&命令重复它。如果希望它只是就地修改文件,请删除>file2并将%p更改为w

ex -s file <<EOF
%s/([[:alpha:]])|([[:alpha:]])/1 2/g
%&g
w
EOF

应该有"一个 sed for ex",它使 ex 的功能可用于管道输入,就像 sed 对 ed 所做的那样,但我知道目前没有这样的实现。Emacs 附带了一个手册页,表明存在这样的实用程序,但该命令不可用,并且根本不会根据手册页执行 ex 所做的事情。

最新更新