sed查找和替换两个字符串

时间:2016-03-22 20:05:13

标签: regex unix sed

我有一个管道分隔文件,其中一个列中的某些值/记录包含值本身的管道,使其看起来好像有更多的列而不是实际的列 - 请注意“列8”(粗体)是否有管道在中间。这应该显示为“| col u lm n8 |”用空格代替管道。

column1|column2|column3|column4|column5|column6|column7|**col|u|lm|n8**|2016|column10|column11|column12|column13|column14|

我需要用空格替换column8中的这些管道。

好的是第7列和第9列(| 2016)中的数据在整个文件中是相同的,所以我能够做一个这样的sed

sed 's/|/ /7g;s/.\(|2016\)/|\1/' 

但是,这将在第7个管道之后将所有管道更改为该行的末尾。我的问题是如何在第7个管道之后将所有管道更改为空格,但直到“| 2016”列?

谢谢

7 个答案:

答案 0 :(得分:1)

使用您的示例输入,这适用于GNU sed 4.2.2:

sed -r ':start s/(column7.)([^\|]*?)\|(.*?.2016)/\1\2 \3/; t start' file

它取代了column7..2016之间的管道,一次取代一个管道。成功替换后,t gotos 返回:start标签,以进行其他替换尝试。

答案 1 :(得分:1)

基于Lars提供的内容,以下内容适用于所有版本的sed:

sed -e ':b' -e 's/\(|column7|\)\(.*\)|\(.*|2016|\)/\1\2 \3/' -e 'tb' inputfile

这可以通过重复替换嵌入式分隔符直到无法找到替代模式。只有先前的替换成功,Sed的t命令才会分支到:b标签。

我们使用更经典的BRE来兼容并避免sed将竖条解释为ERE中的“或”分隔符。

sed脚本被分成单独的-e选项,因为某些种类的sed要求标签引用“在行的末尾”,并且-e的参数的终止被认为是相当于行尾。 (GNU sed不需要这个,但是其他一些seds也是如此。)

但正如anubhava在评论中指出的那样,这是一种较差的方法,因为如果输入数据在第9列的右侧某处包含第二个2016|,它将会失败。

另一种解决方案,如果你正在运行bash,可能是将字段放入一个数组,然后合并元素:

#!/usr/bin/env bash

input="column1|column2|column3|column4|column5|column6|column7|**col|u|lm|n8**|2016|column10|column11|column12|column13|column14|"

IFS=\| read -a a <<< "$input"

while [ "${a[8]}" != "2016" ]; do
  a[7]="${a[7]} ${a[8]}"   # merge elements
  unset a[8]               # delete merged element
  a=( "${a[@]}" )          # renumber array
done

printf "%s|" "${a[@]}"

请注意,bash数组默认从索引0开始。 readarray内置版允许您为索引(-O)指定备用起始点,但内置版本以bash版本4开头,并且仍然有很多版本3。所以为了便携性,它是read -a

另请注意,如果由于某种原因您的输入数据中没有“2016”字段,则无需进一步错误检查,上述脚本将进入无限循环。 : - )

答案 2 :(得分:1)

以下perl解决方案适用于案例,即使|2016再次出现在该行中:

cat file
column1|column2|column3|column4|column5|column6|en|col|u|lm|n8|2016|column10|column11|2016|

perl -pe 's/(en\|[^|]*|(?<!^)\G[^|]*)\|(?!2016)/$1 /g' file

column1|column2|column3|column4|column5|column6|en|col u lm n8|2016|column10|column11|2016|

此正则表达式使用PCRE构造\G,它在上一个匹配结束时或第一个匹配的字符串开头处断言位置。

RegEx Demo

答案 3 :(得分:0)

这个问题真的让我感兴趣,我对它进行了投票,并且在sedawk

中解决了这个问题

我在python中尝试过并制作它。 我没有提供official answer但有些想法:)

$cat sample.csv
column1|column2|column3|column4|column5|column6|column7|col|u|lm|n8|2016|column10|column11|column12|column13|column14|

我的代码:

$cat test.py                                                                                                                                                                           
import re
REGEX = ur"column7\|(.+?)\|2016+?"

with open("sample.csv", "r") as inputs:
    for line in inputs:
        matches = re.findall(REGEX, line)
        column8 = matches[0]
        new_column8 = column8.replace("|", "")
        print line.replace(column8, new_column8)

结果:

$python test.py                                                                                                                                                                       
column1|column2|column3|column4|column5|column6|column7|colulmn8|2016|column10|column11|column12|column13|column14|

答案 4 :(得分:0)

使用GNU awk为第3个arg匹配():

$ awk 'match($0,/(([^|]*[|]){7})(.*)(\|2016\|.*)/,a){gsub(/\|/," ",a[3]); $0=a[1] a[3] a[4]} 1' file
column1|column2|column3|column4|column5|column6|column7|**col u lm n8**|2016|column10|column11|column12|column13|column14|

答案 5 :(得分:0)

当文件只有一行时,你可以这样做 col8 = $(sed's /([^ |] |){7}(。)| 2016。 / \ 2 /'文件) echo“调试行:col8 = $ {col8},修复$ {col8 // | /}” sed's / ^(([^ |] |){7})。* | 2016 / \ 1'“$ {col8 // | /}”'| 2016 /'file

当您知道一个唯一的字符或字符串时,对于包含更多行的文件,您可以执行相同的操作。我将mk97用作唯一字符串:

答案 6 :(得分:0)

这可能适合你(GNU sed):

sed 's/|/&\n/7;:a;ta;s/\n\(|2016|\)/\1/;s/\n|/ \n/;ta;s/\n\(.\)/\1\n/;ta' file

在字段8的开头添加换行符。如果换行在第9场之前出现,请将其删除。如果换行符后跟|,请用空格替换|并在字符上随机播放换行符。如果换行符后面没有|随机播放字符的换行符。

N.B。在任何成功的替换循环到占位符:a